HTTP协议扫盲
转载自 https://www.cnblogs.com/lexiaofei/p/6943690.html
作者:长安快马
HTTP协议的基本概念和通讯原理
一、HTTP协议的概念
1、引子 - 从url开始
URL(Uniform Resource Locator) 地址用于描述一个网络上的资源, 基本格式如下
- schema://host[:port#]/path/.../[?query-string][#anchor]
- scheme 指定低层使用的协议(例如:http, https, ftp)
- host HTTP服务器的IP地址或者域名
- port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/
- path 访问资源的路径
- query-string 发送给http服务器的数据
- anchor- 锚
URL 的一个例子
http://www.mywebsite.com/sj/test/test.aspx?name=sviergn&x=true#stuff
- Schema: http
- host: www.mywebsite.com
- path: /sj/test/test.aspx
- Query String: name=sviergn&x=true
- Anchor: stuff
当我们打开浏览器,在地址栏中输入URL,然后我们就看到了网页。 这个过程中发生了什么呢?
- 我们在浏览器中输入URL回车后,浏览器给Web服务器发送了一个Request请求,
- Web服务器接到Request后进行处理,生成相应的Response,然后发送给浏览器,
- 浏览器解析Response中的HTML,这样我们就看到了网页
过程可能如下图所示
当然,我们的Request 有可能是经过了代理服务器,最后才到达Web服务器的。过程如下图所示
代理服务器也没什么神秘的,代理服务器其实就是一个信息中转站,它可以:
1. 提高访问速度, 大多数的代理服务器都有缓存功能。
2. 突破限制(传说中的FQ?)
3. 隐藏身份。
上面的两个过程中,我们就用到了http协议和tcp/ip协议。
2、名词术语
2.1、TCP/IP协议
术语TCP/IP代表传输控制协议/网际协议,指的是一系列协议。“IP”代表网际协议,TCP和UDP使用该协议从一个网络传送数据包到另一个网络。
如果把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到其它电脑的出口。TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP,文件传输协议FTP这样的协议等。
TCP和UDP是FTP,HTTP和SMTP之类使用的传输层协议。虽然TCP和UDP都是用来传输其他协议的,它们却有一个显著的不同:TCP提供有保证的数据传输,而UDP不提供。
这意味着TCP有一个特殊的机制来确保数据安全的不出错的从一个端点传到另一个端点,而UDP不提供任何这样的保证。
2.2、什么是http协议
HTTP协议,又叫做超文本传输协议,是关于如何在网络上传输 HTML文档的一种通讯协议。
- HTTP协议采用客户/服务器通信模式: 客户端一般指各种浏览器,服务端一般指各种web服务器。
- HTTP协议建立在TCP/IP协议的基础上,规定了浏览器和web服务器之间的通信细节。
HTTP协议目前最新的版本是1.1,对应RFC2068,http://www.ietf.org/rfc/rfc2068.txt,对http1.1协议做了全面的阐述。目前我们使用的是HTTP/1.1版本。
2.3、TCP/IP、Http的区别
TPC/IP协议是传输层协议,解决的是数据如何在网络中传输的问题;HTTP是应用层协议,解决的是如何包装数据的问题。
我们在传输数据时可以只使用(传输层)TCP/IP协议,但是那样的话没有应用层便无法识别数据内容,要使传输的数据有意义必须使用到应用层协议,
应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。
WEB通讯使用HTTP协议作应用层协议,封装HTTP 文本信息,然后使用TCP/IP做传输层协议将它发到网络上。
HTTP(超文本传输协议)是利用TCP在两台电脑(通常是Web服务器和客户端)之间传输信息的协议。客户端使用Web浏览器发起HTTP请求给Web服务器,Web服务器发送被请求的信息给客户端。
2.4、更深入地,还有 七层传输协议 呢!
下面图表描述TCP/IP和其他的协议在最初OSI模型中的位置:
- 7 应用层 例如HTTP、SMTP、SNMP、FTP、Telnet、SIP、SSH、NFS、RTSP、XMPP、Whois、ENRP
- 6 表示层 例如XDR、ASN.1、SMB、AFP、NCP
- 5 会话层 例如ASAP、TLS、SSH、ISO 8327 / CCITT X.225、RPC、NetBIOS、ASP、Winsock、BSD sockets
- 4 传输层 例如TCP、UDP、RTP、SCTP、SPX、ATP、IL
- 3 网络层 例如IP、ICMP、IGMP、IPX、BGP、OSPF、RIP、IGRP、EIGRP、ARP、RARP、 X.25
- 2 数据链路层 例如以太网、令牌环、HDLC、帧中继、ISDN、ATM、IEEE 802.11、FDDI、PPP
- 1 物理层 例如线路、无线电、光纤、信鸽
二、HTTP协议通讯
1、通讯过程
打开一个网页需要浏览器发送很多次Request
1. 当你在浏览器输入URL http://www.cnblogs.com 的时候,浏览器发送一个Request去获取 http://www.cnblogs.com 的html. 服务器把Response发送回给浏览器.
2. 浏览器分析Response中的 HTML,发现其中引用了很多其他文件,比如图片,CSS文件,JS文件。
3. 浏览器会自动再次发送Request去获取图片,CSS文件,或者JS文件。
4. 等所有的文件都下载成功后。 网页就被显示出来了。
2、会话保持
http协议是无状态的,同一个客户端的这次请求和上次请求是没有对应关系,对http服务器来说,它并不知道这两个请求来自同一个客户端。 为了解决这个问题, Web程序引入了Cookie机制来维护状态.
3、链接保持
HTTP 对 TCP 连接的使用,分为两种方式:俗称“短连接”和“长连接”(“长连接”又称“持久连接”,洋文叫做“Keep-Alive”或“Persistent Connection”)
假设有一个网页,里面包含好多图片,还包含好多【外部的】CSS 文件和 JS 文件。在“短连接”的模式下,浏览器会先发起一个 TCP 连接,拿到该网页的 HTML 源代码(拿到 HTML 之后,这个 TCP 连接就关闭了)。然后,浏览器开始分析这个网页的源码,知道这个页面包含很多外部资源(图片、CSS、JS)。然后针对【每一个】外部资源,再分别发起一个个 TCP 连接,把这些文件获取到本地(同样的,每抓取一个外部资源后,相应的 TCP 就断开)
相反,如果是“长连接”的方式,浏览器也会先发起一个 TCP 连接去抓取页面。但是抓取页面之后,该 TCP 连接并不会立即关闭,而是暂时先保持着(所谓的“Keep-Alive”)。然后浏览器分析 HTML 源码之后,发现有很多外部资源,就用刚才那个 TCP 连接去抓取此页面的外部资源。
在 HTTP 1.0 版本,【默认】使用的是“短连接”(那时候是 Web 诞生初期,网页相对简单,“短连接”的问题不大);
到了1995年底开始制定 HTTP 1.1 草案的时候,网页已经开始变得复杂(网页内的图片、脚本越来越多了)。这时候再用短连接的方式,效率太低下了(因为建立 TCP 连接是有“时间成本”和“CPU 成本”滴)。所以,在 HTTP 1.1 中,【默认】采用的是“Keep-Alive”的方式。
关于“Keep-Alive”的更多介绍,可以参见维基百科词条(在“这里”)
4、HTTP协议是无状态的和Connection: keep-alive的区别
无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。从另一方面讲,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系
HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的是UDP协议(无连接)
三、HTTP协议报文
HTTP协议通讯中,客户端和服务器之间的交互通过HTTP消息完成,HTTP消息包括:客户端到服务器的请求、服务器到客户端的响应。
- 请求消息
请求行<CRLF>
消息报头(可选)<CRLF>
<CRLF>(只有CRLF的行)
消息正文(可选)
- 响应消息
响应行<CRLF>
消息报头(可选)<CRLF>
<CRLF>空行(只有CRLF的行)
消息正文(可选)
1、HTTP请求报文
一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,请求报文的一般格式如下:
<request-line>
<headers>
<blank line>
[<request-body>]
1.1、请求行
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔,请求行格式如下:
Method Request-URI HTTP-Version CRLF
例如,GET /index.html HTTP/1.1。
其中,
Method表示请求方法;
Request-URI是一个统一资源标识符;
HTTP-Version表示请求的HTTP协议版本;
CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。
请求方法(所有方法全为大写)有多种,各个方法的解释如下:
GET 请求获取Request-URI所标识的资源
POST 在Request-URI所标识的资源后附加新的数据
HEAD 请求获取由Request-URI所标识的资源的响应消息报头
PUT 请求服务器存储一个资源,并用Request-URI作为其标识
DELETE 请求服务器删除Request-URI所标识的资源
TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT 保留将来使用
OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求
1.2、请求头部
请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。
请求头部通知服务器有关于客户端请求的信息,典型的请求头有:
User-Agent:产生请求的浏览器类型。
Accept:客户端可识别的内容类型列表。
Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
1.3、空行
最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
1.4、请求数据
请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是Content-Type和Content-Length。
2、HTTP响应报文
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。如下所示,HTTP响应的格式与请求的格式十分类似:
<status-line>
<headers>
<blank line>
[<response-body>]
如你所见,在响应中唯一真正的区别在于第一行中用状态信息代替了请求信息。
状态行(status line)通过提供一个状态码来说明所请求的资源情况。
下面给出一个HTTP响应报文例子
HTTP/1.1 200 OK
Date: Sat, 31 Dec 2005 23:59:59 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 122
<html>
<head>
<title>Wrox Homepage</title>
</head>
<body>
<!-- body goes here -->
</body>
</html>
2.1、状态行
状态行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。
1xx:指示信息--表示请求已接收,继续处理。
2xx:成功--表示请求已被成功接收、理解、接受。
3xx:重定向--要完成请求必须进行更进一步的操作。
4xx:客户端错误--请求有语法错误或请求无法实现。
5xx:服务器端错误--服务器未能实现合法的请求。
常见状态代码、状态描述
常见状态代码、状态描述的说明如下。
200 OK:客户端请求成功。
400 Bad Request:客户端请求有语法错误,不能被服务器所理解。
401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用。
403 Forbidden:服务器收到请求,但是拒绝提供服务。
404 Not Found:请求资源不存在,举个例子:输入了错误的URL。
500 Internal Server Error:服务器发生不可预期的错误。
503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常,举个例子:HTTP/1.1 200 OK(CRLF)。
2.2、消息报头
2.3、响应正文
HTTP协议的请求方法、请求头和响应头
一、HTTP请求方法
Http协议定义了很多与服务器交互的方法,最基本的有4种,分别是GET,POST,PUT,DELETE.
一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELETE就对应着对这个资源的查,改,增,删4个操作。
我们最常见的就是GET和POST了。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息.
1、GET - 最常见的一种请求方式
当客户端要从服务器中读取文档时,当点击网页上的链接或者通过在浏览器的地址栏输入网址来浏览网页的,使用的都是GET方式。
GET方法要求服务器将URL定位的资源放在响应报文的数据部分,回送给客户端。
使用GET方法时,请求参数和对应的值附加在URL后面,利用一个问号(“?”)代表URL的结尾与请求参数的开始,传递参数长度受限制。
例如,/index.jsp?id=100&op=bind,这样通过GET方式传递的参数直接表示在地址中
以用google搜索domety为例,Request报文如下:
GET /search?hl=zh-CN&source=hp&q=domety&aq=f&oq= HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint,
application/msword, application/x-silverlight, application/x-shockwave-flash, */*
Referer: <a href="http://www.google.cn/">http://www.google.cn/</a>
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld)
Host: <a href="http://www.google.cn">www.google.cn</a>
Connection: Keep-Alive
Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y-FxlRugatx63JLv7CWMD6UB_O_r
可以看到,GET方式的请求一般不包含”请求内容”部分,请求数据以地址的形式表现在请求行。地址链接如下:
<a href="http://www.google.cn/search?hl=zh-CN&source=hp&q=domety&aq=f&oq=">http://www.google.cn/search?hl=zh-CN&source=hp&q=domety&aq=f&oq=</a>
地址中”?”之后的部分就是通过GET发送的请求数据,在地址栏中可以看到,各个数据之间用”&”符号隔开。很显然,这种方式不适合传送私密数据。
另外,由于不同的浏览器对地址的字符限制也有所不同,一般最多只能识别1024个字符,所以如果需要传送大量数据的时候,也不适合使用GET方式。
2、POST
对于上面提到的不适合使用GET方式的情况,可以考虑使用POST方式,因为使用POST方法可以允许客户端给服务器提供信息较多。POST方法将请求参数封装在HTTP请求数据中,以名称/值的形式出现,可以传输大量数据,这样POST方式对传送的数据大小没有限制,而且也不会显示在URL中。还以上面的搜索domety为例,如果使用POST方式的话,格式如下:
POST /search HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint,
application/msword, application/x-silverlight, application/x-shockwave-flash, */*
Referer: <a href="http://www.google.cn/">http://www.google.cn/</a>
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld)
Host: <a href="http://www.google.cn">www.google.cn</a>
Connection: Keep-Alive
Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y-FxlRugatx63JLv7CWMD6UB_O_r
hl=zh-CN&source=hp&q=domety
可以看到,POST方式请求行中不包含数据字符串,这些数据保存在”请求内容”部分,各数据之间也是使用”&”符号隔开。
POST方式大多用于页面的表单中。因为POST也能完成GET的功能,因此多数人在设计表单的时候一律都使用POST方式,其实这是一个误区。
GET方式也有自己的特点和优势,我们应该根据不同的情况来选择是使用GET还是使用POST。
3、HEAD
HEAD就像GET,只不过服务端接受到HEAD请求后只返回响应头,而不会发送响应内容。当我们只需要查看某个页面的状态的时候,使用HEAD是非常高效的,因为在传输的过程中省去了页面内容。
4、Get和Post方法的区别
我们看看GET和POST的区别
1. GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditPosts.aspx?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的Body中.
2. GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
3. GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值。
4. GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码.
二、请求的header
使用Fiddler 能很方便的查看Reques header, 点击Inspectors tab ->Request tab-> headers 如下图所示.
header 有很多,比较难以记忆,我们也按照Fiddler那样把header 进行分类,这样比较清晰也容易记忆。
1、Cache 头域
1.1、If-Modified-Since
作用: 把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行对比。如果时间一致,那么返回304,客户端就直接使用本地缓存文件。如果时间不一致,就会返回200和新的文件内容。客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示在浏览器中.
例如:If-Modified-Since: Thu, 09 Feb 2012 09:07:57 GMT
实例如下图
1.2、If-None-Match
作用: If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 当用户再次请求该资源时,将在HTTP Request 中加入If-None-Match信息(ETag的值)。如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件。否则将返回200状态和新的资源和Etag. 使用这样的机制将提高网站的性能
例如: If-None-Match: "03f2b33c0bfcc1:0"
实例如下图
1.3、Pragma
作用: 防止页面被缓存, 在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一样
Pargma只有一个用法, 例如: Pragma: no-cache
注意: 在HTTP/1.0版本中,只实现了Pragema:no-cache, 没有实现Cache-Control
1.4、Cache-Control
作用: 这个是非常重要的规则。 这个用来指定Response-Request遵循的缓存机制。各个指令含义如下
Cache-Control:Public 可以被任何缓存所缓存()
Cache-Control:Private 内容只缓存到私有缓存中
Cache-Control:no-cache 所有内容都不会被缓存
还有其他的一些用法, 我没搞懂其中的意思, 请大家参考其他的资料
2、Client 头域
2.1、Accept
作用: 浏览器端可以接受的媒体类型,
例如: Accept: text/html 代表浏览器可以接受服务器回发的类型为 text/html 也就是我们常说的html文档,
如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(non acceptable)
通配符 * 代表任意类型
例如 Accept: */* 代表浏览器可以处理所有类型,(一般浏览器发给服务器都是发这个)
2.2、Accept-Encoding:
作用: 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate),(注意:这不是只字符编码);
例如: Accept-Encoding: gzip, deflate
2.3、Accept-Language
作用: 浏览器申明自己接收的语言。
语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等;
例如: Accept-Language: en-us
2.4、User-Agent
作用:告诉HTTP服务器, 客户端使用的操作系统和浏览器的名称和版本.
我们上网登陆论坛的时候,往往会看到一些欢迎信息,其中列出了你的操作系统的名称和版本,你所使用的浏览器的名称和版本,这往往让很多人感到很神奇,实际上,服务器应用程序就是从User-Agent这个请求报头域中获取到这些信息User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。
例如: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)
2.5、Accept-Charset
作用:浏览器申明自己接收的字符集,这就是本文前面介绍的各种字符集和字符编码,如gb2312,utf-8(通常我们说Charset包括了相应的字符编码方案);
例如:
3、Cookie/Login 头域
3.1、Cookie:
作用: 最重要的header, 将cookie的值发送给HTTP 服务器
4、Entity头域
4.1、Content-Length
作用:发送给HTTP服务器数据的长度。
例如: Content-Length: 38
4.2、Content-Type
作用:
例如:Content-Type: application/x-www-form-urlencoded
5、Miscellaneous 头域
5.1、Referer:
作用: 提供了Request的上下文信息的服务器,告诉服务器我是从哪个链接过来的,比如从我主页上链接到一个朋友那里,他的服务器就能够从HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。
例如: Referer:http://translate.google.cn/?hl=zh-cn&tab=wT
6、Transport 头域
6.1、Connection
例如: Connection: keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接
例如: Connection: close 代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭, 当客户端再次发送Request,需要重新建立TCP连接。
6.2、Host(发送请求时,该报头域是必需的)
作用: 请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的
例如: 我们在浏览器中输入:http://www.guet.edu.cn/index.html
浏览器发送的请求消息中,就会包含Host请求报头域,如下:
Host:http://www.guet.edu.cn
此处使用缺省端口号80,若指定了端口号,则变成:Host:指定端口号
三、响应的状态码
1、什么是response的状态码?
Response 消息中的第一行叫做状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。
状态码用来告诉HTTP客户端,HTTP服务器是否产生了预期的Response.
HTTP/1.1中定义了5类状态码, 状态码由三位数字组成,第一个数字定义了响应的类别
1XX 提示信息 - 表示请求已被成功接收,继续处理
2XX 成功 - 表示请求已被成功接收,理解,接受
3XX 重定向 - 要完成请求必须进行更进一步的处理
4XX 客户端错误 - 请求有语法错误或请求无法实现
5XX 服务器端错误 - 服务器未能实现合法的请求
2、一些常见的状态码
2.1、200 OK
最常见的就是成功响应状态码200了, 这表明该请求被成功地完成,所请求的资源发送回客户端
如下图, 打开博客园首页
2.2、302 Found
重定向,新的URL会在response 中的Location中返回,浏览器将会自动使用新的URL发出新的Request
例如在IE中输入, http://www.google.com. HTTP服务器会返回302, IE取到Response中Location header的新URL, 又重新发送了一个Request.
2.3、304 Not Modified
代表上次的文档已经被缓存了, 还可以继续使用,
例如打开博客园首页, 发现很多Response 的status code 都是304
提示: 如果你不想使用本地缓存可以用Ctrl+F5 强制刷新页面
2.4、400 Bad Request 客户端请求与语法错误,不能被服务器所理解
2.5、403 Forbidden 服务器收到请求,但是拒绝提供服务
2.6、404 Not Found
请求资源不存在(输错了URL)
比如在IE中输入一个错误的URL, http://www.cnblogs.com/tesdf.aspx
2.7、500 Internal Server Error 服务器发生了不可预期的错误
2.8、503 Server Unavailable 服务器当前不能处理客户端的请求,一段时间后可能恢复正常
四、响应的header
同样使用Fiddler 查看Response header, 点击Inspectors tab ->Response tab-> headers 如下图所示
我们也按照Fiddler那样把header 进行分类,这样比较清晰也容易记忆。
1、Cache头域
1.1、Date
作用: 生成消息的具体时间和日期
例如: Date: Sat, 11 Feb 2012 11:35:14 GMT
1.2、Expires
作用: 浏览器会在指定过期时间内使用本地缓存
例如: Expires: Tue, 08 Feb 2022 11:35:14 GMT
1.3、Vary
作用:
例如: Vary: Accept-Encoding
2、Cookie/Login 头域
2.1、P3P
作用: 用于跨域设置Cookie, 这样可以解决iframe跨域访问cookie的问题
例如: P3P: CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR
2.2、Set-Cookie
作用: 非常重要的header, 用于把cookie 发送到客户端浏览器, 每一个写入cookie都会生成一个Set-Cookie.
例如: Set-Cookie: sc=4c31523a; path=/; domain=.acookie.taobao.com
3、Entity头域
3.1、ETag
作用: 和If-None-Match 配合使用。 (实例请看上节中If-None-Match的实例)
例如: ETag: "03f2b33c0bfcc1:0"
3.2、Last-Modified:
作用: 用于指示资源的最后修改日期和时间。(实例请看上节的If-Modified-Since的实例)
例如: Last-Modified: Wed, 21 Dec 2011 09:09:10 GMT
3.3、Content-Type
作用:WEB服务器告诉浏览器自己响应的对象的类型和字符集,
例如:
Content-Type: text/html; charset=utf-8
Content-Type:text/html;charset=GB2312
Content-Type: image/jpeg
3.4、Content-Length
指明实体正文的长度,以字节方式存储的十进制数字来表示。在数据下行的过程中,Content-Length的方式要预先在服务器中缓存所有数据,然后所有数据再一股脑儿地发给客户端。
例如: Content-Length: 19847
3.5、Content-Encoding
WEB服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。
例如:Content-Encoding:gzip
3.6、Content-Language
作用: WEB服务器告诉浏览器自己响应的对象的语言者
例如: Content-Language:da
4、Miscellaneous 头域
4.1、Server:
作用:指明HTTP服务器的软件信息
例如:Server: Microsoft-IIS/7.5
4.2、X-AspNet-Version:
作用:如果网站是用ASP.NET开发的,这个header用来表示ASP.NET的版本
例如: X-AspNet-Version: 4.0.30319
4.3、X-Powered-By:
作用:表示网站是用什么技术开发的
例如: X-Powered-By: ASP.NET
5、Transport头域
5.1、Connection
例如: Connection: keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接
例如: Connection: close 代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭, 当客户端再次发送Request,需要重新建立TCP连接。
6、Location头域
6.1、Location
作用: 用于重定向一个新的位置, 包含新的URL地址
HTTP协议的请求头列表和分类描述
一、请求报头和响应报头列表
1、Requests 头列表
Header | 解释 | 示例 |
---|---|---|
Accept | 指定客户端能够接收的内容类型 | Accept: text/plain, text/html |
Accept-Charset | 浏览器可以接受的字符编码集。 | Accept-Charset: iso-8859-5 |
Accept-Encoding | 指定浏览器可以支持的web服务器返回内容压缩编码类型。 | Accept-Encoding: compress, gzip |
Accept-Language | 浏览器可接受的语言 | Accept-Language: en,zh |
Accept-Ranges | 可以请求网页实体的一个或者多个子范围字段 | Accept-Ranges: bytes |
Authorization | HTTP授权的授权证书 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Cache-Control | 指定请求和响应遵循的缓存机制 | Cache-Control: no-cache |
Connection | 表示是否需要持久连接。(HTTP 1.1默认进行持久连接) | Connection: close |
Cookie | HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 | Cookie: $Version=1; Skin=new; |
Content-Length | 请求的内容长度 | Content-Length: 348 |
Content-Type | 请求的与实体对应的MIME信息 | Content-Type: application/x-www-form-urlencoded |
Date | 请求发送的日期和时间 | Date: Tue, 15 Nov 2010 08:12:31 GMT |
Expect | 请求的特定的服务器行为 | Expect: 100-continue |
From | 发出请求的用户的Email | From: user@email.com |
Host | 指定请求的服务器的域名和端口号 | Host: www.zcmhi.com |
If-Match | 只有请求内容与实体相匹配才有效 | If-Match: “737060cd8c284d8af7ad3082f209582d” |
If-Modified-Since | 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 | If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT |
If-None-Match | 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 | If-None-Match: “737060cd8c284d8af7ad3082f209582d” |
If-Range | 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag | If-Range: “737060cd8c284d8af7ad3082f209582d” |
If-Unmodified-Since | 只在实体在指定时间之后未被修改才请求成功 | If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT |
Max-Forwards | 限制信息通过代理和网关传送的时间 | Max-Forwards: 10 |
Pragma | 用来包含实现特定的指令 | Pragma: no-cache |
Proxy-Authorization | 连接到代理的授权证书 | Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Range | 只请求实体的一部分,指定范围 | Range: bytes=500-999 |
Referer | 先前网页的地址,当前请求网页紧随其后,即来路 | Referer: http://www.zcmhi.com/archives/71.html |
TE | 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 | TE: trailers,deflate;q=0.5 |
Upgrade | 向服务器指定某种传输协议以便服务器进行转换(如果支持) | Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 |
User-Agent | User-Agent的内容包含发出请求的用户信息 | User-Agent: Mozilla/5.0 (Linux; X11) |
Via | 通知中间网关或代理服务器地址,通信协议 | Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) |
Warning | 关于消息实体的警告信息 | Warn: 199 Miscellaneous warning |
2、Responses 头列表
Header | 解释 | 示例 |
---|---|---|
Accept-Ranges | 表明服务器是否支持指定范围请求及哪种类型的分段请求 | Accept-Ranges: bytes |
Age | 从原始服务器到代理缓存形成的估算时间(以秒计,非负) | Age: 12 |
Allow | 对某网络资源的有效的请求行为,不允许则返回405 | Allow: GET, HEAD |
Cache-Control | 告诉所有的缓存机制是否可以缓存及哪种类型 | Cache-Control: no-cache |
Content-Encoding | web服务器支持的返回内容压缩编码类型。 | Content-Encoding: gzip |
Content-Language | 响应体的语言 | Content-Language: en,zh |
Content-Length | 响应体的长度 | Content-Length: 348 |
Content-Location | 请求资源可替代的备用的另一地址 | Content-Location: /index.htm |
Content-MD5 | 返回资源的MD5校验值 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
Content-Range | 在整个返回体中本部分的字节位置 | Content-Range: bytes 21010-47021/47022 |
Content-Type | 返回内容的MIME类型 | Content-Type: text/html; charset=utf-8 |
Date | 原始服务器消息发出的时间 | Date: Tue, 15 Nov 2010 08:12:31 GMT |
ETag | 请求变量的实体标签的当前值 | ETag: “737060cd8c284d8af7ad3082f209582d” |
Expires | 响应过期的日期和时间 | Expires: Thu, 01 Dec 2010 16:00:00 GMT |
Last-Modified | 请求资源的最后修改时间 | Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT |
Location | 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源 | Location: http://www.zcmhi.com/archives/94.html |
Pragma | 包括实现特定的指令,它可应用到响应链上的任何接收方 | Pragma: no-cache |
Proxy-Authenticate | 它指出认证方案和可应用到代理的该URL上的参数 | Proxy-Authenticate: Basic |
refresh | 应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持) |
Refresh: 5; url=
http://www.zcmhi.com/archives/94.html
|
Retry-After | 如果实体暂时不可取,通知客户端在指定时间之后再次尝试 | Retry-After: 120 |
Server | web服务器软件名称 | Server: Apache/1.3.27 (Unix) (Red-Hat/Linux) |
Set-Cookie | 设置Http Cookie | Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1 |
Trailer | 指出头域在分块传输编码的尾部存在 | Trailer: Max-Forwards |
Transfer-Encoding | 文件传输编码 | Transfer-Encoding:chunked |
Vary | 告诉下游代理是使用缓存响应还是从原始服务器请求 | Vary: * |
Via | 告知代理客户端响应是通过哪里发送的 | Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) |
Warning | 警告实体可能存在的问题 | Warning: 199 Miscellaneous warning |
WWW-Authenticate | 表明客户端请求实体应该使用的授权方案 | WWW-Authenticate: Basic |
二、普通报头
在普通报头中,有少数报头域用于所有的请求和响应消息,如:
1、Cache-Control
Cache-Control用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制),HTTP1.0使用的类似的报头域为Pragma。
1.1、请求时的缓存指令
- no-cache(用于指示请求或响应消息不能缓存)、
- no-store、
- max-age、
- max-stale、
- min-fresh、
- only-if-cached;
1.2、响应时的缓存指令
- public、
- private、
- no-cache、
- no-store、
- no-transform、
- must-revalidate、
- proxy-revalidate、
- max-age、
- s-maxage.
为了指示IE浏览器(客户端)不要缓存页面,服务器端的JSP程序可以编写如下:
response.sehHeader("Cache-Control","no-cache");
//response.setHeader("Pragma","no-cache");作用相当于上述代码,通常两者//合用
这句代码将在发送的响应消息中设置普通报头域:Cache-Control:no-cache
2、Date报头
Date普通报头域表示消息产生的日期和时间
3、Connection报头
Connection普通报头域允许发送指定连接的选项。
例如指定连接是连续,或者指定“close”选项,通知服务器,在响应完成后,关闭连接
三、请求报头
请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。
1、常用的请求报头Accept
1.1、Accept
Accept请求报头域用于指定客户端接受哪些类型的信息。
eg:Accept:image/gif,表明客户端希望接受GIF图象格式的资源;Accept:text/html,表明客户端希望接受html文本。
1.2、Accept-Charset
Accept-Charset请求报头域用于指定客户端接受的字符集。
eg:Accept-Charset:iso-8859-1,gb2312.如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。
1.3、Accept-Encoding
Accept-Encoding请求报头域类似于Accept,但是它是用于指定可接受的内容编码。
eg:Accept-Encoding:gzip.deflate.如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。
1.4、Accept-Language
Accept-Language请求报头域类似于Accept,但是它是用于指定一种自然语言。
eg:Accept-Language:zh-cn.如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。
1.5、Authorization
Authorization请求报头域主要用于证明客户端有权查看某个资源。
当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。
1.6、Host(发送请求时,该报头域是必需的)
Host请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的。
我们在浏览器中输入:http://www.xxx.com/index.html,浏览器发送的请求消息中,就会包含Host请求报头域,如下:Host:www.xxx.com,
此处使用缺省端口号80,若指定了端口号,则变成:Host:www.xxx.com:指定端口号
1.7、User-Agent
我们上网登陆论坛的时候,往往会看到一些欢迎信息,其中列出了你的操作系统的名称和版本,你所使用的浏览器的名称和版本,这往往让很多人感到很神奇,
实际上,服务器应用程序就是从User-Agent这个请求报头域中获取到这些信息。
User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。
不过,这个报头域不是必需的,如果我们自己编写一个浏览器,不使用User-Agent请求报头域,那么服务器端就无法得知我们的信息了。
2、请求报头举例
GET /form.html HTTP/1.1 (CRLF)
Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/* (CRLF)
Accept-Language:zh-cn (CRLF)
Accept-Encoding:gzip,deflate (CRLF)
If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF)
If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF)
User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF)
Host:www.xxx.com (CRLF)
Connection:Keep-Alive (CRLF)
(CRLF)
四、响应报头
响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息。
1、常用的响应报头
1.1、Location
Location响应报头域用于重定向接受者到一个新的位置。Location响应报头域常用在更换域名的时候。
1.2、Server
Server响应报头域包含了服务器用来处理请求的软件信息。与User-Agent请求报头域是相对应的。下面是
2、响应报头举例
Server:Apache-Coyote/1.1
WWW-Authenticate
WWW-Authenticate响应报头域必须被包含在401(未授权的)响应消息中,客户端收到401响应消息时候,并发送Authorization报头域请求服务器对其进行验证时,服务端响应报头就包含该报头域。
eg:WWW-Authenticate:Basic realm="Basic Auth Test!" //可以看出服务器对请求资源采用的是基本验证机制。
五、实体报头
请求和响应消息都可以传送一个实体。
一个实体由实体报头域和实体正文组成,但并不是说实体报头域和实体正文要在一起发送,可以只发送实体报头域。
实体报头定义了关于实体正文(eg:有无实体正文)和请求所标识的资源的元信息。
1、常用的实体报头
1.1、Content-Encoding
Content-Encoding实体报头域被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。Content-Encoding这样用于记录文档的压缩方法,eg:Content-Encoding:gzip
1.2、Content-Language
Content-Language实体报头域描述了资源所用的自然语言。没有设置该域则认为实体内容将提供给所有的语言阅读者。
eg:Content-Language:da
1.3、Content-Length
Content-Length实体报头域用于指明实体正文的长度,以字节方式存储的十进制数字来表示。
1.4、Content-Type
Content-Type实体报头域用语指明发送给接收者的实体正文的媒体类型。eg:
Content-Type:text/html;charset=ISO-8859-1
Content-Type:text/html;charset=GB2312
1.5、Last-Modified
Last-Modified实体报头域用于指示资源的最后修改日期和时间。
1.6、Expires
Expires实体报头域给出响应过期的日期和时间。
为了让代理服务器或浏览器在一段时间以后更新缓存中(再次访问曾访问过的页面时,直接从缓存中加载,缩短响应时间和降低服务器负载)的页面,我们可以使用Expires实体报头域指定页面过期的时间。
eg:Expires:Thu,15 Sep 2006 16:23:12 GMT
HTTP1.1的客户端和缓存必须将其他非法的日期格式(包括0)看作已经过期。
eg:为了让浏览器不要缓存页面,我们也可以利用Expires实体报头域,设置为0,jsp中程序如下:
response.setDateHeader("Expires","0");
HTTP协议进阶 - MIME类型
一、概念和原理
1、什么是MIME类型?
MIME类型,即多用途互联网邮件扩展,它是一个互联网标准,在1992年最早应用于电子邮件系统,但后来也应用到浏览器。
服务器会将它们发送的多媒体数据的类型告诉客户端,通知手段就是说明该多媒体数据的MIME类型,客户端根据MIME类型知道接收到的信息哪些是MP3文件,哪些是Shockwave文件等等,采用相应插件处理接收的数据。
2、MIME类型的引入
最早的HTTP协议没有附加数据类型信息,所有传送数据都被客户程序解释为HTML文档。为了支持多媒体数据类型,后来使用了附加在文档之前的MIME数据类型信息来标识数据类型。
MIME意为多功能Internet邮件扩展,设计的最初目的是为了在发送电子邮件时附加多媒体数据,让邮件客户程序能根据其类型进行处理。
然而当它被HTTP协议支持之后,它的意义就更为显著了。
它使得HTTP传输的不仅是普通的文本,而变得丰富多彩。
3、MIME类型的含义
每个MIME类型由两部分组成,前面是数据的大类别,例如声音audio、图象image等,后面定义具体的种类。
Internet中有一个专门组织IANA来确认标准的MIME类型,
但Internet发展的太快,很多应用程序等不及IANA来确认他们使用的MIME类型为标准类型。因此他们使用在类别中以x-开头的方法标识这个类别还没有成为标准,例如:x-gzip,x-tar等。事实上这些类型运用的很广泛,已经成为了事实标准。
只要客户机和服务器共同承认的MIME类型,即使不标准的类型也没有关系,客户程序能根据MIME类型采用相应处理手段处理数据即可。
在Web服务器和浏览器(包括操作系统)中,缺省都设置了标准的和常见的MIME类型,只有对于不常见的 MIME类型,才需要同时设置服务器和客户浏览器,以进行识别。
4、常见的MIME类型
其中,
- 超文本标记语言文本 .html text/html
- xml文档 .xml text/xml
- XHTML文档 .xhtml application/xhtml+xml
- 普通文本 .txt text/plain
- RTF文本 .rtf application/rtf
- PDF文档 .pdf application/pdf
- Microsoft Word文件 .word application/msword
- PNG图像 .png image/png
- GIF图形 .gif image/gif
- JPEG图形 .jpeg,.jpg image/jpeg
- au声音文件 .au audio/basic
- MIDI音乐文件 mid,.midi audio/midi,audio/x-midi
- RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
- MPEG文件 .mpg,.mpeg video/mpeg
- AVI文件 .avi video/x-msvideo
- GZIP文件 .gz application/x-gzip
- TAR文件 .tar application/x-tar
- 任意的二进制数据 application/octet-stream
二、应用场景
1、accept请求头
描述请求发起端(浏览器)能够接受的mime类型。
Accept:text/xml; 代表客户端希望接受的数据类型是xml类型
2、enctype=”multipart/form-data”
http协议本身的原始方法不支持multipart/form-data请求,那这个请求是由post方法演变而来,具体做法如下:
1、multipart/form-data的基础方法是post,就是说由post方法来组合实现
2、multipart/form-data与post方法的不同之处:请求头,请求体。
3、multipart/form-data的请求头必须包含一个特殊的头信息:Content-Type=multipart/form-data,同时还需要规定一个内容分割符用于分割请求体中的多个post的内容,如文件内容和文本内容自然需要分割开来,不然接收方就无法正常解析和还原这个文件了。
具体的头信息如下:
Content-Type: multipart/form-data; boundary=${bound}
//其中${bound} 是一个占位符,代表我们规定的分割符,可以自己任意规定,但为了避免和正常文本重复了,尽量要使用复杂一点的内容。如:--------------------56423498738365
4、multipart/form-data的请求体也是一个字符串,不过和post的请求体不同的是它的构造方式,post是简单的name=value值连接,而multipart/form-data则是添加了分隔符等内容的构造体。具体格式如下:
--${bound}
Content-Disposition: form-data; name="Filename"
HTTP.pdf
--${bound}
Content-Disposition: form-data; name="file000"; filename="HTTP协议详解.pdf"
Content-Type: application/octet-stream
%PDF-1.5
file content
%%EOF
--${bound}
Content-Disposition: form-data; name="Upload"
Submit Query
--${bound}--
其中${bound}为之前头信息中的分割符,如果头信息中规定为123,那么这里也要为123,;
可以很容易看出,这个请求体是多个相同的部分组成的:
每一个部分都是以--加分隔符开始的,然后是该部分内容的描述信息,然后一个回车,然后是描述信息的具体内容;
如果传送的内容是一个文件的话,那么还会包含文件名信息,以及文件内容的类型。
上面的第二个小部分其实是一个文件体的结构,最后会以--分割符--结尾,表示请求体结束。
综上,可以知道要发送一个multipart/form-data的请求,其实任何支持post请求的工具或语言都可以支持,只是自己要稍微包装一下便可。
3、input type="file" name="pic" id="pic" accept="image/gif, image/jpeg"(ff和chrome支持)
accept 属性规定了可通过文件上传提交的服务器接受的文件类型。
注意:accept 属性仅适用于 <input type="file">。
提示:请不要将该属性作为您的验证工具。应该在服务器上对文件上传进行验证。
实例
规定在文件上传中服务器只接受图像文件:
<form>
<input type="file" name="pic" id="pic" accept="image/gif, image/jpeg" />
</form>
HTTP请求防篡改
相关链接:
http://www.cnblogs.com/ziyi--caolu/p/4742577.html
一、概念和定义
1、什么是重放攻击?
我们在设计接口的时候,最担心一个接口被别有用心的用户截取后,用于重放攻击。重放攻击是什么呢?就是把请求被原封不动地重复发送,一次,两次...n次。
2、重放攻击造成的后果
一般的请求被提交到后台执行,先会经过【页面验证】后提交给【后台逻辑】,提交给【后台逻辑】过程中请求可能会被被拦截,
- 如果这个【后台逻辑】是插入数据库操作,就很容易造成多条重复的数据插入数据库。
- 如果这个【后台逻辑】是查询数据库操作,就可能导致反复查询,数据库被堵住等情况。
所以,我们需要一种防重放的机制来做请求验证。
二、解决方案
1、客户端(携带timestamp+nonce)
我们常用的防止重放的机制是使用timestamp和nonce来做的重放机制。
1.1、timestamp
timestamp用来表示请求的当前时间戳,这个时间要事先和服务器时间戳校正过。我们预期正常请求带的时间戳会是不同的,如:假设正常人每秒至多会做一个操作。
每个请求携带的时间戳不能和当前时间距离很近,即不能超过规定时间,如60s。这样请求即使被截取了,也只能在有限时间(如:60s)内进行重放,过期就会失效。
1.2、nonce
仅仅提供timestamp还是不够的,我们还是提供给攻击者60s的可攻击时间了。要避免60秒内发生攻击,我们还需要使用一个nonce随机数。
nonce是由客户端根据随机生成的,比如 md5(timestamp+rand(0, 1000),正常情况下,在短时间内(比如60s)连续生成两个相同nonce的情况几乎为0。
2、服务端(验证时间是否超限,检查签名)
服务端第一次在接收到这个nonce的时候做下面行为:
1 去redis中查找是否有key为nonce:{nonce}的string
2 如果没有,则创建这个key,把这个key失效的时间和验证timestamp失效的时间设置一致,比如是60s。
3 如果有,说明这个key在60s内已被使用过了,这个请求就可以判断为重放请求。
3、方案流程
http://www.xxxx.com?userId=123&userName=zhangsan×tamp=1480556543&nonce=43f34f33&sign=80b886d71449cb33355d017893720666
在这个请求中,userId和userName是真正需要传递的业务参数,timestamp,nonce,sign都是为了签名和防重放使用。
timestamp是发送请求时间,nonce是随机串,sign是对uid,timestamp,nonce(对于一些rest风格的api,建议业务参数一起签名)。
服务端接到这个请求的处理逻辑:
- 先验证sign签名是否合理,证明请求参数没有被中途篡改
- 再验证timestamp是否过期,证明请求是在最近60s被发出的
- 最后验证nonce是否已经有了,证明这个请求不是60s内的重放请求
4、方案分析
我们将每次请求的nonce参数存储到一个“集合”中,可以json格式存储到缓存中。
每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,如果存在则认为是非法请求。
我们在timestamp方案的基础上,加上nonce参数,因为timstamp参数对于超过60s的请求,都认为非法请求,所以我们只需要存储60s的nonce参数的“集合”即可。
nonce的一次性可以解决timestamp参数60s的问题,timestamp可以解决nonce参数“集合”越来越大的问题。
InputStream的复用
一、问题提出
在进行网关引擎开发时,获取到一个http请求的inputstream后,可能要多次利用它进行read操作。由于流读过一次就不能再读了,所以需要实现InputStream的复制。
而InputStream对象本身不能复制,因为它没有实现Cloneable接口。
二、解决方案
1、方案一:使用ByteArrayOutputStream<->InputStream
此时,可以先把InputStream转化成ByteArrayOutputStream,后面要使用InputStream对象时,再从ByteArrayOutputStream转化回来就好了。代码实现如下:
InputStream input = httpconn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1 ) {
baos.write(buffer, 0, len);
}
baos.flush();
InputStream stream1 = new ByteArrayInputStream(baos.toByteArray()); //TODO:显示到前台
InputStream stream2 = new ByteArrayInputStream(baos.toByteArray()); //TODO:本地缓存
这种适用于一些不是很大的流,因为缓存流是会消耗内存的。
2、方案二:使用流的mark 和 reset方法
参见链接:
inputstream复制:http://www.cnblogs.com/happyaday/p/4616023.html
对象克隆:https://zhidao.baidu.com/question/181598914089683044.html
请求报文之 GET、POST-FORM 和 POST-FILE
一、get
1、页面代码
2、请求报文
3、小结
- get请求没有报文体,所以请求报文没有content-type
- url上的query参数param11=val11¶m12=val12丢了?
二、post-form
1、页面代码
2、请求报文
3、小结
- post-form请求有报文体,content-type=application/x-www-form-urlencoded
- url上的query参数param11=val11¶m12=val12还在,但不在请求报文体中
- form参数param21=val21¶m22=val22在请求报文体中
三、post-file
1、页面代码
2、请求报文
3、小结
- post-file请求有报文体,content-type=multipart/form-data;boundary=----WebKitFormBoundaryWLeq4pVQtIoB8LbNk
- url上的query参数param11=val11¶m12=val12还在,但是不在请求报文体中
- form参数param21=val21¶m22=val22在请求报文体中,但是分隔开的!!!!!!
响应报文之 Transfer-Encoding=chunked方式
一、什么是chunked编码?
分块传输编码(Chunked transfer encoding)是只在HTTP协议1.1版本(HTTP/1.1)中提供的一种数据传送机制。以往HTTP的应答中数据是整个一起发送的,并在应答头里Content-Length字段标识了数据的长度,以便客户端知道应答消息的结束。
传统的Content-length解决方案:计算实体长度,并通过头部告诉对方。浏览器可以通过 Content-Length 的长度信息,判断出响应实体已结束
Content-length面临的问题:由于 Content-Length 字段必须真实反映实体长度,但是对于动态生成的内容来说,在内容创建完之前,长度是不可知的。
这时候要想准确获取长度,只能开一个足够大的 buffer,等内容全部生成好再计算。这样做一方面需要更大的内存开销,另一方面也会让客户端等更久。
我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界——分块编码(Transfer-Encoding: chunked)。
对于动态生成的应答内容来说,内容在未生成完成前总长度是不可知的。因此需要先缓存生成的内容,再计算总长度填充到Content-Length,再发送整个数据内容。这样显得不太灵活,而使用分块编码则能得到改观。
分块传输编码允许服务器在最后发送消息头字段。例如在头中添加散列签名。对于压缩传输传输而言,可以一边压缩一边传输。
二、如何使用chunked编码
如果在http的消息头里Transfer-Encoding为chunked,那么就是使用此种编码方式。
接下来会发送数量未知的块,每一个块的开头都有一个十六进制的数,表明这个块的大小,然后接CRLF("\r\n")。然后是数据本身,数据结束后,还会有CRLF("\r\n")两个字符。有一些实现中,块大小的十六进制数和CRLF之间可以有空格。
最后一块的块大小为0,表明数据发送结束。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段。
消息最后以CRLF结尾。
在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。
每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF(\r\n)。
最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。
例:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
23\r\n
This is the data in the first chunk\r\n
1A\r\n
and this is the second one\r\n
3\r\n
con\r\n
8\r\n
sequence\r\n
0\r\n
\r\n
Content-Encoding 和 Transfer-Encoding 二者经常会结合来用,其实就是针对 Transfer-Encoding 的分块再进行 Content-Encoding压缩。
三、响应报文
1、构建响应报文
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK\r\n");
sb.append("Content-Type: text/plain\r\n");
sb.append("Transfer-Encoding: chunked\r\n");
sb.append("\r\n");
// chunkData
{
String line = "Hello,";
byte[] lineBs = line.getBytes();
int iOct = lineBs.length;
String sHex = SocketUtil.octToHex(iOct);
sb.append(sHex).append("\r\n");
sb.append(line).append("\r\n");
System.out.println("[" + line + "],iOct=" + iOct + ",sHex=" + sHex);
}
// chunkData
{
String line = "中国";
byte[] lineBs = line.getBytes();
int iOct = lineBs.length;
String sHex = SocketUtil.octToHex(iOct);
sb.append(sHex).append("\r\n");
sb.append(line).append("\r\n");
System.out.println("[" + line + "],iOct=" + iOct + ",sHex=" + sHex);
}
// chunk-end:0
{
sb.append("0").append("\r\n");
sb.append("\r\n");
}
2、解析响应报文
ByteBuffer in = ByteBuffer.allocate(1024);
in.put(sb.toString().getBytes());
in.flip();
int start = in.position();
int end = in.limit();
ByteBuffer content = ByteBuffer.allocate(1024);
while (true) { // 封包循环
for (int i = start; i < end - 1; i++) {
if (in.get(i) == 0x0D && in.get(i + 1) == 0x0A) {
byte[] nums = new byte[i - start];
in.get(nums);
// 丢弃\r\n
in.get(new byte[2]);
int num = Integer.parseInt(new String(nums), 16);
byte[] strs = new byte[num];
in.get(strs);
content.put(strs);
// 丢弃\r\n
in.get(new byte[2]);
start = i + 4 + num;
break;
}
}
if (in.get(start) == 0x30 && in.get(start + 1) == 0x0D && in.get(start + 2) == 0x0A
&& in.get(start + 3) == 0x0D && in.get(start + 4) == 0x0A) {
content.flip();
in.get(new byte[5]);
break;
}
}
System.out.println(new String(content.array(), 0, content.limit()));
四、例子
1、get
2、post