这篇文章主要介绍 HTTP 请求格式信息,为后面自己写 HTTP server 做理论基础。
1. HTTP 请求格式
每一个HTTP包都分为HTTP头和HTTP体两部分,消息体是可选的,而消息头是必须的。
客户端通过发送 HTTP 请求向服务器请求对资源的访问。 它向服务器传递了一个数据块,也就是请求信息,HTTP 请求由三部分组成:请求行、 请求头和请求正文。
下面我们使用 Firefox
的开发者工具查看浏览器向服务器发送 HTTP 请求这一过程时怎样完成的。
首先需要使用 Apache
搭建测试环境,详细见我之前的一篇文章 ubuntu apache 执行 cgi。当然这一步不是必须的,我们只是希望能通过这个工具来直观认识 HTTP 请求发送与接收这一过程。
打开浏览器地址栏输入:127.0.0.1。f12
键打开开发者工具,点击网络
标签页。可以看到如下效果:
左下角列了请求方法 GET
,文件 /
,域名 127.0.0.1
等等信息。
右边则列出了我们想知道的 HTTP 请求行,请求头,分别如下:
请求行:
GET http://127.0.0.1 HTTP/1.1
请求头:
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:42.0) Gecko/20100101 Firefox/42.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive (告诉服务器发送完文档后保持连接)
由于没有GET 方法参数,也就是说没有请求正文。那么请求行+请求头就构成了一个 HTTP 请求报文,底层使用tcp
进行传输。
抽象出来,可以得到HTTP请求的标准格式如下所示:
<request-line>
<headers>
<blank line>
[<request-body>]
2. 请求行(request-line)
请求的第一行是“方法 URL 协议/版本”,并以回车换行作为结尾。请求行以空格分隔。
下面只介绍 GET
和 POST
方法。
2.1 GET 方法
GET 方法用于获取由 Request-URL 所标识的资源的信息,常见的形式是:
GET Request-URL HTTP/1.1
GET方法是默认的HTTP请求方法,例如当我们通过在浏览器的地址栏中直接输入网址的方式去访问网页的时候,浏览器采用的就是 GET 方法向服务器获取资源。
我们可以使用GET方法来提交表单数据,用GET方法提交的表单数据只经过了简单的编码,同时它将作为URL的一部分向服务器发送,因此,如果使用GET方法来提交表单数据就存在着安全隐患上。例如
Http://localhost/login.php?username=aa&password=1234
问号 ?
之后的所有内容表示请求正文,以字符 &
分隔,等号前面表示变量参数名,后面表示参数值。
2.2 POST 方法
POST方法是GET方法的一个替代方法,它主要是向Web服务器提交表单数据,尤其是大批量的数据。 在请求头信息结束之后的两个回车换行之后(实际是空一行),就是表单提交的数据。如上面提到的post表单数据:
username=aa&password=1234
2.3 GET 与 POST 区别
- 从编程的角度来讲,如果用户通过GET方法提交数据,则数据存放在QUERY_STRING环境变量中,而POST方法提交的数据则可以从标准输入流中获取。
- 安全角度看,POST 方法由于 GET。
- 在客户端,Get方式在通过URL提交数据,数据在URL中可以看到;POST方式,数据放在HTTP包的body中。
- GET方式提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST则没有此限制。
3. 响应格式
在接收和解释请求消息后,服务器会返回一个 HTTP 响应消息。与 HTTP 请求类似,HTTP 响应也是由三个部分组成,分别是:状态行、消息报头和响应正文。如最上面测试时的截图:
响应头如下:
Connection: Keep-Alive
Content-Encoding: gzip
Content-Length: 431
Content-Type: text/html;charset=UTF-8
Date: Sun, 13 Dec 2015 13:29:18 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.7 (Ubuntu)
Vary: Accept-Encoding
响应正文如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table>
<tr><th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr>
<tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="cgi-bin/">cgi-bin/</a></td><td align="right">2015-12-13 21:24 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="web/">web/</a></td><td align="right">2015-12-09 14:06 </td><td align="right"> - </td><td> </td></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
<address>Apache/2.4.7 (Ubuntu) Server at 127.0.0.1 Port 80</address>
</body></html>
3.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 //请求资源不存在,eg:输入了错误的URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
eg:HTTP/1.1 200 OK (CRLF)
4. TODO
有了上面的介绍与理论知识,接下来我们就可以通过TCP,socket 编程来自己实现一个 HTTP server
,详情见后文。
5. 参考
http://blog.csdn.net/hguisu/article/details/8683290
http://botao900422.blog.51cto.com/4747129/1557599