HTTP

Web客户端和服务器的交互用的是一个基于文本的应用级协议 —— HTTP(Hypertext Transfer Protocol)。客户端与服务器的一个典型交互过程为:一个Web客户端(即浏览器)打开一个到服务器的因特网连接,并且请求某些内容;服务器响应所请求的内容,然后关闭连接;浏览器读取这些内容,并把它显示在屏幕上。

Web与常规的文件检索服务(如FTP)的主要区别是: Web内容可以使用HTML(Hypertext Markup Language)来编写,提供格式和布局的控制,并且包含超链接。

Web内容

对于Web客户端和服务器而言,内容是与一个MIME(Multipurpose Internet Mail Extensions)类型相关的字节序列。

Web服务器以两种不同的方式向客户端提供内容:

  1. 取一个磁盘文件,并将它的内容返回给客户端。
  2. 运行一个可执行文件,并将它的输出返回给客户端。

每条由Web服务器返回的内容都是和它管理的的某个文件相关联的。这些文件中的每一个都有唯一的名字,叫做URL(Universal Resource Locator)。例如:

http://www.google.com:80/index.html

标识因特网主机www.google.com上一个称为/index.html的HTML文件,它是由一个监听端口80的Web服务器管理的。

可执行文件的URL可以在文件名后包括程序参数。?分隔文件名和参数,而且每个参数都用&分隔开。例如:

http://example.com:80/cig-bin/adder?123&345

标识了一个叫做/cig-bin/adder的可执行文件,会带两个参数123和345来调用它。在事务过程中,客户端和服务器使用的URL的不同部分。例如,客户端使用前缀

http://www.google.com:80

来决定与哪类服务器联系,服务器在哪,以及它监听的端口号是多少。服务器使用后缀

/index.html

来发现在它的文件系统中的文件,并确定请求的是动态内容还是静态内容。

HTTP事务

HTTP事务是指由客户端发起HTTP请求,服务器返回HTTP响应,然后关闭连接。

1.HTTP请求

HTTP请求的组成:

  1. 一个请求行(request line)
  2. (紧跟着)零个或多个请求报头(request header)
  3. (最后以)一个空的文本行来终止报头列表
  4. (POST方法可能会在这增加一个请求主体)

每个文本行都由一对回车和换行符结束\r\n

请求行的形式是:

<method> <uri> <version>

<method>是HTTP支持的方法,包括GET、POST、OPTIONS、HEAD、PUT、DELETE和TRACE。应用最多的GET方法指导服务器生成和返回<URI>(Uniform Resource Identifier)标识的内容。POST方法经常用于提交表单。HEAD方法类似与GET方法,当服务器接受到HEAD方法的请求时,将会用一个HTTP报文进行响应,但是并不返回请求主体。PUT方法常与Web发行工具联合使用,用户使用它上传对象到指定的Web服务器上的指定路径。DELETE方法允许用户删除Web服务器上的内容。

<uri>是相应URL的后缀,包括文件名和可选的参数。

<version>表明了该请求遵循的HTTP版本。

请求报头为服务器提供了额外的信息,例如浏览器的商标名,或者浏览器理解的MIME类型。请求报头的格式为:

<header name>: <header data>

针对HTTP/1.1请求,我们需要HOST报头,如Host: www.google.com,它的数据指示了原始服务器的域名。

2.HTTP响应

HTTP响应的组成:

  1. 一个响应行(response line)
  2. (紧跟着)零个或更多的响应报头(response header)
  3. (再紧跟着)一个终止报头的空白行
  4. (再紧跟着)一个响应主体(response body)

一个响应行的格式是:

<version> <status code> <status message>

<version>描述了响应所遵循的HTTP版本。<status code>是一个三位的正整数,指明对请求的处理,<status message>给出与错误码等价的英文描述。以下是一些常见的状态码:

Status Code Status Message 描述
200 成功 处理请求无误
301 永久移动 内容已移至由响应报头Location指明的新URL上
400 错误请求 服务器不能理解请求
403 禁止 服务器无权访问请求的文件
404 未发现 服务器找不到所请求的文件
501 未实现 服务器不支持请求的方法
505 HTTP版本不支持 服务器不支持请求的版本

状态码分类:1xx消息,2xx成功,3xx重定向,4xx客户端错误,5xx服务器错误

响应报头提供了关于响应的附加信息。比如Content-Type告诉客户端响应主体中内容的MIME类型;Content-Length指示响应主体的大小。

终止响应报头的空文本行之后,跟随的是响应主体,响应主体中包含着被请求的内容。

服务动态内容

CGI(Common Gateway Interface)的实际标准用来解决如下问题:

  1. 客户端如何将参数传递给服务器

    Get请求的参数在URL中传递。一个?分割文件名和参数,而每个参数都用一个&字符隔开。参数中不允许有空格,而必须用字符串%20来表示,对于其他特殊字符,也存在着相似的编码。POST请求的参数是在请求主体中而不是在URI中传递的。

  2. 服务器如何将参数传递给子进程

    在服务器接收到一个如下的请求后

     GET /cgi-bin/adder?123&345 HTTP/1.1
    

    它调用fork创建一个子进程,并调用execve在子进程的上下文中执行/cgi-bin/adder程序。像adder这样的程序,常常被称为CGI程序,因为它们遵守CGI标准。在调用execve之前,子进程将CGI环境变量QUERY_STRING设置为123&345adder程序在运行时可以使用Unix getenv函数来引用它。

  3. 服务器如何将其他信息传递给子进程

    CGI定义了大量的环境变量,一个CGI程序在它运行时可以设置这些环境变量。

    环境变量 描述
    QUERY_STRING 程序参数
    SERVER_PORT 父进程监听的端口
    REQUEST_METHOD GET或POST
    REMOTE_HOST 客户端的域名
    REMOTE_ADDR 客户端的点分十进制IP地址
    CONTENT_TYPE 只对POST而言: 请求体的MIME类型
    CONTEMT_LENGTH 只对POST而言: 请求体的字节大小
  4. 子进程将它的输出发送到哪里

    一个CGI程序将它的动态内容发送到标准输出。在子进程中加载并运行CGI程序之前,它使用Unix dup2函数将标准输出重定向到和客户端相关联的已连接描述符。因此,任何CGI程序写到标准输出的内容都会直接到达客户端。 因为父进程不知道子进程生成的内容的类型和大小,所以子进程负责生成Content-typeContent-length响应报头,以及终止报头的空行。

HTTP服务器是无状态的,也就是说,同一个用户向一个服务器发送不同的请求,服务器不能识到这些请求来自来自同一个用户。

然而一个Web站点通常希望能够识别用户,为此HTTP使用了cookie。cookie的工作过程为:

  1. 用户浏览器向服务器发送请求
  2. 服务器接收的请求后,产生一个唯一的标识码<identification code>,并以此为索引在它的后端数据库中产生一个表项
  3. 服务器用一个包含Set-cookie: <identification code>响应报头的响应报文对用户浏览器进行响应
  4. 用户浏览器收到该HTTP响应报文时,它会看到Set-cookie报头,该浏览器在它管理的特定cookie文件中添加一行,改行包含服务器的主机名和在Set-cookie报头中声明的<identification code>
  5. 之后当用户浏览器继续向相同服务器发送请求时,HTTP请求报文都包含Cookie: <identification code>请求报头,服务器可以据此跟踪用户,保存用户状态