HTTP基础

本文深入介绍了HTTP的基础知识,包括HTTP的定义、URI、HTTP报文结构、请求方法、状态码及DNS解析过程。HTTP是一个无状态、应用层的请求/响应协议,通过URI定位资源,使用各种方法如GET、POST等进行交互。状态码分为五类,2xx表示成功,3xx表示重定向,4xx表示客户端错误,5xx表示服务器错误。DNS则是将域名解析为IP地址的系统,包括根域名、顶级域名和权威域名服务器等层次。

本篇文章基于HTTP/1.1的RFC 7231协议标准,介绍一些基本却必备的HTTP知识,主要包括URI、HTTP报文、请求方法、响应状态码、DNS域名。由于头字段包含的内容太多,比如条件请求、范围请求、缓存控制、代理等,下一篇文章专门讲解。

需要说明的是,协议标准好比接口,Okhttp或HttpUrlConncetion就是接口的具体实现,一般而言,实现会遵循接口的规则,但是也有可能修改甚至违背标准。

HTTP的定义

The Hypertext Transfer Protocol (HTTP) is a stateless application-level request/response protocol that uses extensible semantics and self-descriptive message payloads for flexible interaction with network-based hypertext information systems.

从协议标准的定义里面我们可以看到HTTP具备的一些特点:

  • 无状态(stateless):指每个请求都是互相独立、毫无关联,协议不要求客户端或服务端记录请求相关的信息。
  • 应用层(application-level):位于7层或4层网络分层模型的最上层,直接面向具体应用的协议。比如除了HTTP外,还有FTP、SMTP等
  • 请求-应答(request/response):永远是请求方先主动发起连接和请求,而应答方只有在收到请求后才能答复,如果没有请求那不会有任何动作。
  • 自描述信息(self-descriptive message):我们总是听说HTTP是明文传输,但其实是因为最开始的HTTP是通过明文传输,因此造成我们对HTTP传输信息的误解,其实从HTTP可以很方便的转换到加密传输这点来看,HTTP传输的是自描述信息。
  • 不安全:不支持身份认证和完整性校验。同样,这点也是早期HTTP暴露出来的问题,不过可以通过扩展的HTTPS进行解决。
  • 简单、灵活、可扩展:比如一开始只规定了报文的基本格式,后来增加了请求方法、版本号、状态码、头字段、TLS等,还可以添加任意功能。

URI 统一资源定位符

主要用于唯一地标记资源的位置或名字。

URI格式

URI格式

  • scheme:协议名,表示资源应该用哪种协议来访问,比如http、https、jar、file、ftp等等。
  • : // :特定的符号,不能改变。
  • user:passwd@:身份信息,表示登录主机时的用户名和密码,但现在已经不推荐使用这种形式了,因为它把敏感信息以明文形式暴露出来,存在严重的安全隐患。
  • authority:包含host和port,表示资源所在的主机名host必须有,port可以省略,使用默认的端口号
  • path:标记资源所在位置。
  • query:URI的查询参数。
  • #fragment:片段标识符,它是 URI 所定位的资源内部的一个“锚点”或者说是“标签”,浏览器可以在获取资源后直接跳转到它指示的位置,仅客户端使用。
  • 一个完整的例子 http://www.baidu.com:8080/user?uid=1234&name=mario&referer=xxx
URI编码

HTTP协议要求对 ASCII 码以外的字符集和特殊字符做一个特殊的操作,把它们转换成与 URI 语义不冲突的形式,被称为"escape"和"unescape",俗称"转义"。

URI 转义的规则有点“简单粗暴”,直接把非 ASCII 码或特殊字符转换成十六进制字节值,然后前面再加上一个“%”。例如,空格被转义成“%20”,“?”被转义成“%3F”。而中文、日文等则通常使用 UTF-8 编码后再转义。

HTTP报文

HTTP的通用报文结构如下图所示,其中请求报文对应的起始行为请求行,而响应报文对应的起始行为状态行,而且空行是分割头部和实体数据的关键,是必不可少的。

HTTP/1.1明确要求提供的头字段是Host,它必须出现在请求头里,标记虚拟主机名。

HTTP报文可以没有body,但是一定要有header,而且header后面必须有CRLF。

HTTP报文

请求行

简要地描述了客户端想要如何操作服务器端的资源,由三个部分构成,如下图,其中SP是指空格。

请求行

  • 请求方法:是一个动词,比如GET/POST,表示对资源的操作。
  • 请求目标:通常是一个URI,标记了请求方法要操作的资源。
  • 版本号:表示报文所使用的HTTP协议版本。
  • 比如 GET / HTTP/1.1
状态行

表示服务器响应的状态,由三个部分构成,如下图

状态行

  • 版本号:表示报文所使用的HTTP协议版本。
  • 状态码:一个三位数,用代码的形式表示处理的结果。
  • 原因:更详细的解释文字。
  • 比如 HTTP/1.1 200 OK
头部

用于说明此次请求的相关信息,比如主机名、缓存、范围请求等。

头部字段是 key-value 的形式,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字段结束。比如在“Host: 127.0.0.1”这一行里 key 就是“Host”,value 就是“127.0.0.1”。如下图:

一个头部字段

头部字段需要注意的地方:

  • 字段名不区分大小写,但首字母大写可读性更好
  • 字段名不允许使用空格和"_",但是可以使用"-"
  • 字段名后必须紧跟":",不能有空格,但是冒号后的字段值前可以有任意空格
  • 字段的顺序是没有意义的
  • 字段原则上不能重复,除非这个字段本身的语义允许,比如 Set-Cookie
实体
数据类型

HTTP参考MIME来标记body的数据类型。

多用途互联网邮件扩展(Multipurpose Internet Mail Extensions),简称为 MIME。MIME共分为8大类,形式是“type/subtype”的字符串,下面列举一下常用的类型:

  • text:即文本格式的可读数据,我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等。
  • image:即图像文件,有 image/gif、image/jpeg、image/png 等。
  • audio/video:音频和视频数据,例如 audio/mpeg、video/mp4 等。
  • application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有 application/json,application/javascript、application/pdf 等,另外,如果实在是不知道数据是什么类型,就会是 application/octet-stream,即不透明的二进制数据。
编码类型

HTTP在传输时为了节约带宽,有时还会压缩数据,这时需要明确编码类型,主要有三种:

  • gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式
  • deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip;
  • br:一种专门为 HTTP 优化的新压缩算法(Brotli)。

跟实体相关的头字段有 Accept、Accept-Encoding、Content-Type、Content-Encoding、Accept-Language、Content-Language、Accept-Charset、Vary,在头字段文字专门讲解。

请求方法

协议标准定义了8种方法
  • GET:获取资源,可以理解为读取或下载数据
  • HEAD:获取资源的元信息,没有body实体返回。使用场景,比如检查服务器是否存在某个文件,只要发个HEAD请求就好了,没必要用GET把整个文件下载下来
  • POST:向资源提交数据,相当于写入或上传数据,相当于数据库的INSERT
  • PUT:类似POST,相当于数据库的UPDATE
  • DELETE:删除资源
  • CONNECT:建立特殊的连接隧道,比如在建立HTTPS连接的隧道
  • OPTIONS:列出可对资源实行的方法
  • TRACE:追踪请求-响应的传输路径
  • 扩展方法:MKCOL、COPY、MOVE、LOCK、UNLOCK、PATCH

注意:虽然HTTP协议定义了这些方法,但是具体的实现还是得靠客户端和服务器协商定夺。比如Okhttp默认提供了6种方法,比如GET、HEAD、POST、PUT、DELETE、PATCH,而且在进行HTTPS连接的时候会通过CONNECT方法尝试建立HTTPS隧道。

方法的属性
  • 安全:指请求方法不会破坏服务器上的资源,即不会对服务器上的资源做实质性的修改。HEAD、GET、OPTION、TRACE都是是安全的。
  • 幂等:多次执行相同的操作,结果也是相同的。安全方法、PUT、DELETE是幂等的。
  • 可缓存:协议定义了GET、HEAD和POST是可缓存的,但大多数实现仅支持GET和HEAD。(后面会专门讲解HTTP的缓存)

状态码

表示服务器对请求的处理结果。目前 RFC 标准里规定的状态码是三位数,所以取值范围就是从 000 到 999。RFC标准顺便也把状态码进行了分类,主要有五类,用数字的第一位表示分类。

1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作
  • 100 Continue:表示服务器已经接受到了请求的开始部分,没有拒绝这个请求,并且正在等着客户端发送接下来的请求。一般是客户端使用了Expect头字段的时候出现。
  • 101 Switching Protocols:客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了。
2××:成功,报文已经收到并被正确处理
  • 200 OK:最常见的成功状态码
  • 201 Created:请求已经完成,并且新建了一个或多个资源,该资源通过Location字段标记返回给客户端,跟缓存的ETag和Last-Modified字段相关。
  • 202 Accepted:请求被接受,但服务器正在处理,没有完成,这个请求也许不会被服务器执行完成。
  • 203 Non-Authoritative Information:请求已经完成,但是中间代理修改了源服务器发出的数据。
  • 204 No Content:也是成功状态码,只是响应头后没有body数据。
  • 205 Reset Content:请求已经被成功处理,但是没有返回新文档,客户端应该清空输入内容。
  • 206 Partial Content:是断点续传和分片下载的基础,通常还会伴随着头字段“Content-Range”,表示响应报文里 body 数据的具体范围,供客户端确认,例如“Content-Range: bytes 0-99/2000”,意思是此次获取的是总计 2000 个字节的前 100 个字节。
3××:重定向,资源位置发生变动,需要客户端重新发送请求
  • 300 Multiple Choices:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。
  • 301 Moved Permanently:永久重定向,含义是此次请求的资源已经不存在了,需要改用新的 URI 再次访问,在响应头里使用字段Location指明后续要跳转的 URI。
  • 302 Found:临时重定向,意思是请求的资源还在,但需要暂时用另一个 URI 来访问,在响应头里使用字段Location指明后续要跳转的 URI。
  • 303 See Other:类似 302,但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作。
  • 304 Not Modified:用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。
  • 307 Temporary Redirect:类似 302,但重定向后请求里的方法和实体不允许变动,含义比 302 更明确。
  • 308 Permanent Redirect:类似 307,不允许重定向后的请求变动,但它是 301“永久重定向”的含义。
4××:客户端错误,请求报文有误,服务器无法处理
  • 400 Bad Request:通用的错误码,表示请求报文有错误,但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说,只是一个笼统的错误。
  • 403 Forbidden:实际上不是客户端的请求出错,而是表示服务器禁止访问资源。
  • 404 Not Found:资源在源服务器上未找到,所以无法提供给客户端或者是不愿意告知外部存在。
  • 405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET。
  • 406 Not Acceptable:资源无法满足客户端请求的条件,例如请求中文但只有英文。
  • 408 Request Timeout:请求超时,服务器等待了过长的时间。
  • 409 Conflict:多个请求发生了冲突,可以理解为多线程并发时的竞态。
  • 410 Gone:资源在源服务器上找不到,而且这种情况是永远的。跟404有点像,所以现在基本都是用404替代。
  • 411 Length Required:只响应特定长度的请求。
  • 413 Request Entity Too Large:请求报文里的 body 太大。
  • 414 Request-URI Too Long:请求行里的 URI 太大。
  • 415 Unsupported Media Type:服务器不支持该媒体类型。
  • 417 Expectation Failed:服务器不能满足客户端在请求中指定的请求头,与Expect头字段相关。
  • 426 Upgrade Required:服务器拒绝服务,除非客户端根据服务器要求的协议版本进行升级,服务器会在Upgrade头字段返回要求客户端升级的协议版本,比如当前客户端使用HTTP/1.1,服务器要求使用HTTP/3.0。
  • 429 Too Many Requests:客户端发送了太多的请求,通常是由于服务器的限连策略。
  • 431 Request Header Fields Too Large:请求头某个字段或总体太大。
5××:服务器错误,服务器在处理请求时内部发生了错误
  • 500 Internal Server Error:一个通用的错误码,服务器究竟发生了什么错误我们是不知道的,用于隐藏信息。
  • 501 Not Implemented:表示客户端请求的功能还不支持。
  • 502 Bad Gateway:通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。
  • 503 Service Unavailable:表示服务器当前很忙,暂时无法响应服务,这是一个临时状态,所以 503 响应报文里通常还会有一个“Retry-After”字段,指示客户端可以在多久以后再次尝试发送请求。
  • 504 Gateway Timeout:通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了超时。
  • 505 HTTP Version Not Supported:服务器不支持或拒绝服务该HTTP协议版本。

DNS

网络地址是通过IP地址进行标记,比如IPv4或IPv6,但是这些数字不方便人类记忆,所以想出了把IP地址和特定的字符串映射依赖,通过某个字符串就能访问某个IP地址。增加的这个映射关系需要通过某种方式解析出来,就衍生出了DNS。

域名的形式,用“.”分隔,最右边的被称为“顶级域名”,然后是“二级域名”,层级关系向左依次递减,比如 www.baidu.com

域名的总长度限制在253个字符以内,每一级域名长度不能超过63个字符,域名是大小写无关的。

DNS的核心系统是一个三层的树状、分布式服务

  • 根域名服务器: 管理顶级域名的服务器,返回“com”、“net”等顶级域名的IP地址
  • 顶级域名服务器: 管理各自域名下的权威域名服务器,比如com顶级域名服务器可以返回baidu.com域名服务器的IP地址
  • 权威域名服务器: 管理自己域名下主机的IP地址,比如baidu.com可以返回www.baidu.com的IP地址

除了核心系统之外,还有一类被称为非权威域名服务器,这些都是大公司或网络运行商自己建立的DNS服务器,作为用户DNS查询的代理,代替用户访问核心DNS系统,它们可以缓存之前的查询结果,如果有记录就直接返回。

如果每次网络请求都通过查询根域名服务器进行DNS查询,那么会造成网络请求慢且增加域名解析的压力,所以引入了缓存机制。主要的缓存有四级

  • 客户端缓存,比如java.net.InetAddress里面就会进行DNS缓存
  • 操作系统缓存,会对DNS解析结构进行缓存
  • 本地hosts文件缓存,操作系统还有一个特殊的主机映射文件“/etc/hosts”,如果在操作系统缓存中找不到,那么就会来找这个文件
  • 非权威域名服务器缓存

DNS的解析过程:客户端缓存 --> 操作系统缓存 --> 本地hosts文件缓存 --> 非权威域名服务器缓存 --> 根域名服务器(非缓存) --> 顶级域名服务器(com) --> 二级域名服务器地址(apple.com)。当每一级找到了目标地址会立即返回,所以有一种tricky的HTTP性能优化手段,HTTP的请求不使用域名而使用IP地址,这就绕过了DNS解析,但是这就无法享受到基于域名实现的负载均衡。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值