HTTP/1.1
一、简介
HTTP协议:超文本传输协议(HyperText Transfer Protocol, HTTP),使用TCP作为运输层协议。HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。HTTP 是一个无状态协议,但是web站点往往希望能够识别用户,因此有了cookie和session。
二、HTTP报文格式
Client request:
GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
Server response:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
Hello World! My payload includes a trailing CRLF.
1、HTTP请求报文
请求行:
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。
HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、TRACE等。
注意:HTTP协议本身并没有限制URL的长度,实际中URL长度限制是实现http协议的客户端(浏览器等)和服务器限制的。
首部(Header Fields)
请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的首部头(包括请求头或者响应头)有:
User-Agent:产生请求的浏览器类型。
Accept:客户端可识别的内容类型列表。
Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
Cookie:该请求域名下的cookie值。
Set-Cookie:_security_token=51568727949026296;Path=/;Domain=.dasouche-fin.net;Max-Age=600000。
Content-Type:请求的与实体对应的MIME信息,application/x-www-form-urlencoded、text/html;charset=UTF-8等。
Connection:keep-alive,持久连接,HTTP/1.0需要显示声明,HTTP/1.1默认开启。关闭:Connection:close。
Referer:该首部包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。服务端一般使用 Referer
首部识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等。
Accept-Language:浏览器可接受的语言,比如Accept-Language:en,zh。
Accept-Encoding:客户端能够理解的内容编码方式——通常是某种压缩算法——进行通知,通过内容协商的方式,服务端会选择一个客户端提议的方式,使用并在响应报文首部Content-Encoding中通知客户端该选择。需要注意的是,服务器端并不强制要求一定使用何种压缩模式。
注意:有字符集限制,中文汉字可能需要URLEncoder.encode、URLDecoder.decode
实体主体:(Message Body)
实体主体是可选的,如GET时,实体主体为空;Post请求时使用。
2、HTTP响应报文
与请求报文类似,只是请求行与状态行的不同。在RFC中,请求行/状态行称为起始行(Start Line),状态值翻译为“短语”(reason-phrase)更好一些.
状态码:There are five values for the first digit:
o 1xx (Informational): The request was received, continuing process
o 2xx (Successful): The request was successfully received, understood, and accepted
o 3xx (Redirection): Further action needs to be taken in order to complete the request
o 4xx (Client Error): The request contains bad syntax or cannot be fulfilled
o 5xx (Server Error): The server failed to fulfill an apparently valid request
在网络世界中,浏览器绝对不是唯一的 HTTP 客户端工具,上面图片中提到的 Postman等工具也是HTTP客户端实现的一种,其实还有更多,只不过浏览器的使用最为广泛而已;明确不能将浏览器对HTTP协议的实现,当做了HTTP协议本身。
三、跨域问题
1、跨域问题的产生
跨域问题的产生原因:浏览器的同源策略。为什么要这个策略?安全问题,缺少易受到跨站脚本攻击(XSS)、跨站请求伪造(CSRF)等攻击。
所谓同源是指:协议、域名、端口相同。
参考:
注意:如果一个主机IP上有多个域名应用,在跨域问题上,域仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。
2、解决方案
1、原生js的JSONP
由于同源策略,一般来说位于server1.example.com的网页无法与 server2.example.com的服务器沟通,而HTML的 <script>元素是一个例外。利用 <script>元素的这个开放策略,网页可以得到从其他来源动态产生的JSON数据,而这种使用模式就是所谓的 JSONP。
演示代码:
服务端:
const url = require('url');
require('http').createServer((req, res) => {
const data = {
x: '隔壁超市薯片半价啦'
};
const callback = url.parse(req.url, true).query.callback
res.writeHead(200);
//回调原页面上函数处理返回结果
res.end(`${callback}(${JSON.stringify(data)})`);
}).listen(3000, 'localhost');
console.log('服务器已启动...');
浏览器端:http://local.dasouche-fin.net:8086/index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<script>
function jsonpCallback(data){
alert('跨域成功!'+data.x)
}
</script>
<script src="http://localhost:3000/?callback=jsonpCallback"></script>
</body>
</html>
缺点:
只能是get请求,因为<script >是GET请求,不安全可能会遭受XSS攻击。
2、jQuery的jsonp形式
jsonp技术跟ajax没有任何关系,只不过jQuery将jsonp和ajax技术做了结合。
有很多种写法,只列出一种:
$.ajax({
url:'http://外域.com/xxx.php',
dataType:"jsonp",
jsonp: "callback",
jsonpCallback:"ooo",
success:function(data){
console.log(data);
}
});
3、跨域资源共享 CORS
CORS(Cross-Origin Resource Sharing)是一种机制(W3C 标准),它使用额外的HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源,需要浏览器和服务端都支持才行。
3.1 简单请求
使用下列方法之一:
GET
HEAD
POST
HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
Content-Type 的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
请求中没有使用 ReadableStream 对象。
简单请求流程:
分别查看请求报文和响应报文:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]
3.2 非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
1、非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
2、服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
3、如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出跨域报错信息。
4、一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
非简单请求流程:
预检请求的请求报文和响应报文:
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
预检请求完成之后,发送实际请求报文和响应报文:
POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache
<?xml version="1.0"?><person><name>Arun</name></person>
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[Some GZIP'd payload]
3.3 CORS相关首部
Request headers:
- Origin:
指示了请求来自于哪个站点。该字段仅指示服务器名称,并不包含任何路径信息。
- Access-Control-Request-Method:
请求首部 Access-Control-Request-Method 出现于 preflight request (预检请求)中,用于通知服务器在真正的请求中会采用哪种 HTTP 方法。因为预检请求所使用的方法总是 OPTIONS ,与实际请求所使用的方法不一样,所以这个首部是必要的。
- Access-Control-Request-Headers
请求首部 Access-Control-Request-Headers 出现于 preflight request (预检请求)中,用于通知服务器在真正的请求中会采用哪些请求首部。如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
Response headers:
- Access-Control-Allow-Origin
响应头指定了该响应的资源是否被允许与给定的origin共享。该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
- Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
- Access-Control-Allow-Credentials
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
- Access-Control-Expose-Headers
该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
- Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
- Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET
请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
4、Nginx反向代理
正向代理 和反向代理 ? ?
......
四、相关文档
HTTP/1.1 RFC文档:https://tools.ietf.org/html/rfc7230#section-2.7.1
Cookie RFC文档:https://tools.ietf.org/html/rfc6265#section-4.2
XSS攻击介绍文档:https://tech.meituan.com/2018/09/27/fe-security.html
CSRF介绍文档:https://tech.meituan.com/2018/10/11/fe-security-csrf.html
CORS文档:https://www.w3.org/TR/cors/
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS