超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP) 是互联网上应用最为广泛的一种网络协议。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。通过HTTP或者HTTPS协议请求的资源由 统一资源标识符(Uniform Resource Identifiers,URI) 来标识。

HTTP协议标准是由万维网协会(World Wide Web Consortium,W3C)和互联网工程任务组(Internet Engineering Task Force,IETF)制定的,其中最著名的是RFC 2616,定义了HTTP协议中现今广泛使用的一个版本—HTTP 1.1。

第一个版本的HTTP(HTTP/0.9)用于Internet上的传输原始数据;HTTP/1.0加入了MIME支持、元数据、请求/应答限定符。

对web开发者而言HTTP协议的用法已耳熟能详,本文只记录与web开发相关的HTTP重要细节。

HTTP是一种通用的、无状态的应用层协议,适用于分布式、协同的、超媒体信息系统。通过一些扩展(如请求方法、错误码、头信息),HTTP可用于超文本外的其他用途,例如命名服务器、分布式对象管理。HTTP的特点在于数据表示的类型与协商,允许建立系统时不必考虑数据是如何传输的。

操作

多数HTTP通信由用户代理发起请求,服务器响应该请求。更复杂的情况则可能包括代理、网关和隧道。 代理 是指转发代理,它接受URI请求,重写部分消息,然后把重写过的消息转发至URI标识的服务器。 网关 是指接收代理,它运行在其他服务器之上,需要时可以为背后的服务器翻译请求。 隧道 相当于两个连接的中转站,但不会改变消息。当通信需要通过一个中介时可以使用隧道,即使该中介不理解消息内容。

   request chain -------------------------------------->
UA -----v----- A -----v----- B -----v----- C -----v----- O
   <------------------------------------- response chain

除隧道外的通信方都可能会采用内部缓存和处理消息,缓存可以缩短请求/应答链。但并不是所有消息都可以被缓存,缓存行为也在RFC2616中加以定义。

   request chain ---------->
UA -----v----- A -----v----- B - - - - - - C - - - - - - O
   <--------- response chain

HTTP通常发生在TCP/IP连接上,默认的TCP端口为80,当然也可以使用别的端口。这不会影响到HTTP实现,HTTP只假设有一个可靠的传输协议。

HTTP/1.1连接应该是持久连接。这不仅能够减小TCP连接的内存、CPU开销,减小了TCP包的数量,同时提供了更短的延迟和及时的错误反馈。此时,请求与应答可以形成管道而不必等上一个连接的关闭。

URI

在HTTP中,URI 是用来标识资源的字符串。

http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]

HTTP URI 是大小写敏感的,相同的URI应该每个字节都完全相同。除非:

  1. 端口空。此时相当于默认端口。
  2. 主机名必须大小写无关。
  3. 模式名必须大小写无关。
  4. 绝对路径为空。此时相当于/

HTTP方法

  • GET:获取URI制定的信息。
  • HEAD:同GET,但服务器不返回消息体。
  • POST:请求服务器接受请求中包装的实体,将其作为URI标识的资源的附属信息。
  • PUT:让服务器将请求中包装的实体存储为URI标识位置。
  • DELETE:让服务器将URI标识的资源删除。
  • TRACE:调用远程的应用层环回。最终服务器应将收到的消息包装为实体并返回状态码为200的应答。
  • CONNECT:保留,用作代理。

其中HEADGET被称为安全方法,即服务器只是返回所需的信息,不应该产生其他的副作用。

Request 头

request头部字段如下:

request-header = Accept                   ; Section 14.1
               | Accept-Charset           ; Section 14.2
               | Accept-Encoding          ; Section 14.3
               | Accept-Language          ; Section 14.4
               | Authorization            ; Section 14.8
               | Expect                   ; Section 14.20
               | From                     ; Section 14.22
               | Host                     ; Section 14.23
               | If-Match                 ; Section 14.24
  • Accept:可接受的应答的媒体类型,可使用通配符*

    Accept: autio/*; q=0.2, autio/basic
    # 最好是basic音频,如果没有的话任何音频都可以接受(这时我认为质量打了80%的折扣)
    

  • Accept-Charset:可接受的应答的字符集。

    Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
    # 最好是iso-8859-5,不过unicode-1-1也还好(80%的质量)
    

  • Accept-Encoding:可接受的应答的内容编码。

    Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
    

  • Accept-Language:可接受的应答的自然语言集合。

    Accept-Language: da, en-gb;q=0.8, en;q=0.7
    

  • Authorization:用户想要让服务器给自己授权,通常在接受到401之后发生。

  • Expect:客户需要的服务器行为。

    该字段是逐跳发生的,即代理如不能满足该期望应返回417;但请求头是端到端的,该请求仍然必须被转发。

  • From:该字段应为控制发送请求的HTTP代理(agent)的用户电子邮箱名。

  • Host:目标主机名与端口,来自URI。

  • If-Match:与HTTP方法一起用,使得条件性执行该方法。条件为该实体匹配该列表,即没有发生用户所不知道的变化。

在HTTP中,质量值qvalue作为协商参数的限定符,表示客户端认为该参数的重要性(权重)。如果一个参数qvalue为0,表示该参数对客户端来讲是不可接受的。参数之间用,分隔,参数值与质量值之间用;分隔。

Response 头

response头部字段如下:

response-header = Accept-Ranges           ; Section 14.5
                | Age                     ; Section 14.6
                | ETag                    ; Section 14.19
                | Location                ; Section 14.30
                | Proxy-Authenticate      ; Section 14.33
                | Retry-After             ; Section 14.37
                | Server                  ; Section 14.38
                | Vary                    ; Section 14.44
                | WWW-Authenticate        ; Section 14.47
  • Accept-Ranges

    在HTTP/1.1中,客户端可以请求部分的应答(range),服务器将根据指定的Range Unitrange将实体划分为subrange返回给客户端。

    该字段允许服务器指定接受怎样的range请求。例如:

    Accept-Ranges: bytes
    # 接受的range unit为byte
    

  • Age:生成该response以来,服务器估计已经过去了多少时间。该字段用于缓存机制。

  • Etag:指定请求变量的当前实体Tag。该字段用来比较来自同一资源的变化的实体。

  • Location:为完成请求或标识新的资源,服务器用该字段将接收者重定向。此处应指定绝对路径。

  • Proxy-Authenticate407(Proxy Authentication Required)响应必须填写该字段。该字段应指出授权模式和该URI代理的参数。收到该响应后代理应寻找自己的授权证书。该字段只用于当前连接(点到点),不应传到下游的客户端。

  • Retry-After:用于503(Service Unavailable)响应。指示客户端服务预期多久不可用。可以是绝对时间,也可以是代表时间间隔的整数(单位为秒)。

  • Server:原始服务器处理该请求的软件信息。

  • Vary:指示不可缓存的请求头列表(大小写不敏感)。

  • WWW-Authenticate401(Unauthorized)响应必须填写该字段。同Proxy-Authenticate,只不过该字段作用于用户代理(User Agent)。

缓存

HTTP常用语信息系统,此时应答缓存可以提高性能。缓存应该保证是正确的,如果一个应答的缓存既非一手的,又不够新,则必须在应答中添加警告。

HTTP/1.1的基本缓存机制只有隐式的缓存指示,在有些系统中需要给出明确的缓存指示。此时要用到缓存控制(Cache-Control)头。

最好的缓存应完全避免从原始服务器发送请求,主要的机制是服务器明确给定应答的过期时间。相反地,服务其可以将过期时间设为过去的时间, 以此强制得到最新的请求。过期时间的计算有两种方法:

  1. now - Age Header,其中Age Header是生成应答时服务器会设置的字段。
  2. age_value,等于该应答在所有缓存中驻留的时间和,加上所有路径上传输所用的时间。

当然,缓存服务器之间需要通过类似NTP的协议来同步他们的时钟。

在Web应用中,缓存通常通过EtagExpires响应头来实现。Etag表示资源的ID,Expires表示资源的过期事件。 User-Agent(比如浏览器)可以通过这两个字段来恰当地缓存这些HTTP响应。

状态码

HTTP状态码(HTTP Status Code)是用以表示Web服务器HTTP响应状态的3位数字代码,分为5大类。 以下给出状态码说明,其描述参见wikipedia:HTTP状态码

  • 1xx:表示请求已被接受,但需要后续处理。
  • 2xx:请求已成功被服务器接收、理解、并接受。
  • 3xx:这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向, 重定向目标在本次响应的Location头字段中指明。
  • 4xx:这类的状态码代表了客户端看起来可能发生了错误,妨碍了服务器的处理。 除非响应的是一个HEAD请求,否则服务器就应该返回一个解释当前错误状况的实体。
  • 5xx:这类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。 并且响应消息体中应当给出理由,除非是HEAD请求。

HTTP表单

在web开发中最常见的莫过于GET和POST,其中GET一般将参数编码在url中(HTTP header)来传递数据; 而POST或PUT数据必须放在消息主体(entity-body)中,这样的数据便是HTTP表单,表单数据的编码方式应在HTTP头中进行设置(Content-Type header字段),常见的编码方式有(HTTP采用MIME框架,编码方式可以是任何MIME类型):

  • urlencoded
  • multipart
  • json
  • plain text
  • xml

在Web开发中,前三种格式非常常见。HTML中<form>支持urlencoded,multipart,plain text,通过enctype属性来进行设置。AJAX中默认的则是JSON编码格式。

urlencoded

HTML中<form>标签的enctype属性用来指定表单编码格式,默认为application/x-www-form-urlencoded,即以下两个表单完全等价。

<form method='post'>
  <input type="text" name='title'>
  <input type="text" name='subtitle'>
  <input type="submit">
</form>
<form  method='post' enctype='application/x-www-form-urlencoded'>
  <input type="text" name='title'>
  <input type="text" name='subtitle'>
  <input type="submit">
</form>

上述表单将会显示为两个文本框和一个提交按钮。我们在文本框中分别写入test中国后,点击提交按钮。产生的HTTP请求可能是这样的:

可以打开Chrome控制台的Network标签,找到这次请求,便可以看到下面的信息。

请求头(这里只给出了Content-Type字段):

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded

请求体:

title=test&subtitle=%E4%B8%AD%E5%9B%BD

这里你看到的%E4%B8%AD%E5%9B%BD即是中国按照base64编码(url通用的编码方式)后的结果。可以在Chrome Console中通过decodeURI('%E4%B8%AD%E5%9B%BD')来解码。

multipart

multipart编码方式则需要设置enctypemultipart/form-data

<form method="post" enctype="multipart/form-data">
    <input type="text" name="title" value="harttle">
    <input type="file" name="avatar">
    <input type="submit">
</form>

这里我们还设置了<input type='text'>的默认值为harttle

该表单将会显示为一个文本框、一个文件按钮、一个提交按钮。然后我们选择一个文件:chrome.png,点击表单提交后产生的请求可能是这样的:

请求头:

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

请求体:

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="title"

harttle
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="avatar"; filename="chrome.png"
Content-Type: image/png

 ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

这便是一个multipart编码的表单。Content-Type中还包含了boundary的定义,它用来分隔请求体中的每个字段。正是这一机制,使得请求体中可以包含二进制文件(当然文件中不能包含boundary)。

除了application/x-www-form-urlencodedmultipart/form-data,HTML的<form>还支持text/plain。此外,如果想提交其他编码类型的表单,必须通过AJAX技术,接下来我们介绍一个常用的JSON数据的提交。

json

从JavaScript中提交JSON数据真是再方便不过了,jquery、angularJS等框架都封装了更好用的AJAX方法。例如:

$.post('/xxx', {
        title: 'test',
        content: [1,2,3]
    });

该JavaScript执行后可能生成如下的HTTP请求:

请求头:

POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8

请求体:

{"title":"test","content":[1,2,3]}

xml

请求头:

POST http://www.example.com HTTP/1.1
Content-Type: text/xml

请求体:

<!--?xml version="1.0"?-->
<methodcall>
    <methodname>examples.getStateName</methodname>
    <params>
        <param>
            <value><i4>41</i4></value>
    </params>
</methodcall>

参考:RTF 2616

同时发表在:http://harttle.com/2014/10/01/http.html

登录发表评论 注册

反馈意见