HTTP学习笔记(一)
完整的HTTP报文格式:
示例:
响应报文的状态行、响应头、响应体分别和请求报文的起始行(请求行)、请求头、请求体对应。
或者说:请求头 = 请求行 + 请求头(头部字段);响应头 = 状态行 + 响应头(头部字段)
注意:头部字段是 key-value 的形式,用“:”分隔,不区分大小写,顺序任意,除了规定的标准头,也可以任意添加自定义字段,实现功能扩展。
常用头字段
通用字段
在请求头和响应头里都可以出现
- Date:但通常出现在响应头里,表示 HTTP 报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略。
请求字段
仅能出现在请求头里,进一步说明请求信息或者额外的附加条件
- Host:告诉服务器这个请求应该由哪个主机来处理,当一台计算机上托管了多个虚拟主机的时候,服务器端就需要用 Host 字段来选择。唯一一个 HTTP/1.1 规范里要求必须出现的字段,也就是说,如果请求头里没有 Host,那这就是一个错误的报文
- User-Agent:使用一个字符串来描述发起 HTTP 请求的客户端,服务器可以依据它来返回最合适此浏览器显示的页面。
- 但由于历史的原因,User-Agent 非常混乱,每个浏览器都自称是“Mozilla”“Chrome”“Safari”,企图使用这个字段来互相“伪装”,导致 User-Agent 变得越来越长,最终变得毫无意义。
- 不过有的比较“诚实”的爬虫会在 User-Agent 里用“spider”标明自己是爬虫,所以可以利用这个字段实现简单的反爬虫策略。
响应字段
仅能出现在响应头里,补充说明响应报文的信息;
- Server:告诉客户端当前正在提供 Web 服务的软件名称和版本号
- Server 字段也不是必须要出现的,因为这会把服务器的一部分信息暴露给外界,如果这个版本恰好存在 bug,那么黑客就有可能利用 bug 攻陷服务器。所以,有的网站响应头里要么没有这个字段,要么就给出一个完全无关的描述信息。
实体字段
它实际上属于通用字段,但专门描述 body 的额外信息。
- Content-Length:它表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度。服务器看到这个字段,就知道了后续有多少数据,可以直接接收。如果没有这个字段,那么 body 就是不定长的,需要使用 chunked 方式分段传输。
标准的请求方法
常用
- GET:获取资源,可以理解为读取或者下载数据;搭配 URI 和其他头字段就能实现对资源更精细的操作。
- HEAD:获取资源的元信息。HEAD 方法与 GET 方法类似,也是请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。HEAD的响应头与 GET 完全相同,所以可以用在很多并不真正需要资源的场合,避免传输 body 数据的浪费。
- POST:向资源提交数据,相当于写入或上传数据;
- PUT:类似 POST;POST 和 PUT 方法则是GET和HEAD的相反操作,向 URI 指定的资源提交数据,数据就放在报文的 body 里。
- 通常POST表示“新建”,PUT表示“修改”
其他
- DELETE:方法指示服务器删除资源,因为这个动作危险性太大,所以通常服务器不会执行真正的删除操作,而是对资源做一个删除标记。当然,更多的时候服务器就直接不处理 DELETE 请求。
- CONNECT:是一个比较特殊的方法,要求服务器为客户端和另一台远程服务器建立一条特殊的连接隧道,这时 Web 服务器在中间充当了代理的角色。
- OPTIONS:方法要求服务器列出可对资源实行的操作方法,在响应头的 Allow 字段里返回。它的功能很有限,用处也不大,有的服务器(例如 Nginx)干脆就没有实现对它的支持。
- TRACE:多用于对 HTTP 链路的测试或诊断,可以显示出请求 - 响应的传输路径。它的本意是好的,但存在漏洞,会泄漏网站的信息,所以 Web 服务器通常也是禁止使用。
这些方法有点像对文件或数据库的“增删改查”操作,只不过这些动作操作的目标不是本地资源,而是远程服务器上的资源,所以只能由客户端“请求”或者“指示”服务器来完成。
扩展方法
LOCK 方法锁定资源暂时不允许修改,或者使用 PATCH 方法给资源打个小补丁,部分更新数据。但因为这些方法是非标准的,所以需要为客户端和服务器编写额外的代码才能添加支持。
补充:
- 跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。 在现在前端最常用的 cors 跨域中,浏览器都是用 OPTIONS 方法发预检请求的
状态码
1××
提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
- 101:(Switching Protocols)客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了。
2××
成功,报文已经收到并被正确处理;
- 200:(OK)表示一切正常,服务器如客户端所期望的那样返回了处理结果,如果是非 HEAD 请求,通常在响应头后都会有 body 数据。
- 204:(No Content)含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的。
- 206:(Partial Content)HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,它与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分;状态码 206 通常还会伴随着头字段“Content-Range”,表示响应报文里 body 数据的具体范围,供客户端确认,例如“Content-Range: bytes 0-99/2000”,意思是此次获取的是总计 2000 个字节的前 100 个字节。
3××
重定向,资源位置发生变动,需要客户端重新发送请求;
- 301:(Moved Permanently)永久重定向”,含义是此次请求的资源已经不存在了,需要改用新的 URI 再次访问。
- 302:(Found)“临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问。
- 301 和 302 都会在响应头里使用字段 Location 指明后续要跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。两者的根本区别在于语义,一个是“永久”,一个是“临时”,所以在场景、用法上差距很大。
- 301和302还另有两个等价的状态码“308Permanent Redirect”和“307 TemporaryRedirect”,但这两个状态码不允许后续的请求更改请求方法。
- 304:(Not Modified)用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。
4××
客户端错误,请求报文有误,服务器无法处理;
- 400:(Bad Request)表示请求报文有错误,但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说,只是一个笼统的错误,客户端看到 400 只会是“一头雾水”“不知所措”。所以,在开发 Web 应用时应当尽量避免给客户端返回 400,而是要用其他更有明确含义的状态码。
- 403:(Forbidden)实际上不是客户端的请求出错,而是表示服务器禁止访问资源。原因可能多种多样,例如信息敏感、法律禁止等,如果服务器友好一点,可以在 body 里详细说明拒绝请求的原因。
- 404:(Not Found)原意是资源在本服务器上未找到,所以无法提供给客户端。但现在已经被“用滥了”,只要服务器“不高兴”就可以给出个 404,而我们也无从得知后面到底是真的未找到,还是有什么别的原因。
- 405:(Method Not Allowed)不允许使用某些方法操作资源,例如不允许 POST 只能 GET;
- 406:(Not Acceptable)资源无法满足客户端请求的条件,例如请求中文但只有英文;
- 408:(Request Timeout)请求超时,服务器等待了过长的时间;
- 409:(Conflict)多个请求发生了冲突,可以理解为多线程并发时的竞态;
- 413:(Request Entity Too Large)请求报文里的 body 太大;
- 414:(Request-URI Too Long)请求行里的 URI 太大;
- 429:(Too Many Requests)客户端发送了太多的请求,通常是由于服务器的限连策略;
- 431:(Request Header Fields Too Large)请求头某个字段或总体太大;
5××
服务器错误,服务器在处理请求时内部发生了错误。
- 500:(Internal Server Error)也是一个通用的错误码,服务器究竟发生了什么错误我们是不知道的。不过对于服务器来说这应该算是好事,通常不应该把服务器内部的详细信息,例如出错的函数调用栈告诉外界。虽然不利于调试,但能够防止黑客的窥探或者分析。
- 501:(Not Implemented)表示客户端请求的功能还不支持,这个错误码比 500 要“温和”一些,和“即将开业,敬请期待”的意思差不多,不过具体什么时候“开业”就不好说了。
- 502:(Bad Gateway)通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。
- 503:(Service Unavailable)表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。
- 503 是一个“临时”的状态,很可能过几秒钟后服务器就不那么忙了,可以继续提供服务,所以 503 响应报文里通常还会有一个“Retry-After”字段,指示客户端可以在多久以后再次尝试发送请求。
HTTP协议特点
- 灵活可扩展
- 可靠传输
- 应用层协议
- 请求 + 应答,客户端主动发起请求,服务器被动回复请求
- 无状态
- 其他特点
- 实体数据可缓存可压缩
- 可分段获取数据
- 支持身份认证
- 支持国际化语言等等
与UDP的对比:
UDP:无连接也无状态的,顺序发包乱序收包,数据包发出去后就不管了,收到后也不会顺序整理
TCP:有连接无状态,顺序发包顺序收包,按照收发的顺序管理报文。