toyws 玩具式 web server

2015-07-18 Mithrilwoodrat 更多博文 » 博客 » GitHub »

原文链接 http://woodrat.xyz/2015/07/18/toyws%e7%8e%a9%e5%85%b7%e5%bc%8f-web-server/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


csapp 上网络编程一章里有一个 tiny.c ,实现了 HTTP 的 GET 方法,支持 cgi 。照着书上自己敲了一个,取名 toyws 。后续准备把其他几个 HTTP METHOD 实现一下。

代码地址 toyws

根据 RFC-2616
HTTP messages 分为 request 和 response
HTTP-message = Request | Response ; HTTP/1.1 messages

server 接受client的连接后,client 发送一个 request message 到 server。server 处理后回复一个 response。

所以程序大致的流程就是 server start listen -> client connect -> server accept -> client send message -> server response message。 server 开始监听后进入一个死循环,不断处理客户端的连接和消息。
流程和普通的 socket 程序一样,只是需要按照 HTTP 协议 发送和回复消息。

request 和 response 消息使用 RFC-822 generic message format。
格式如下

   generic-message = start-line
                     *(message-header CRLF)
                     CRLF
                     [ message-body ]

两种类型的请求都包含一个 start-line,一行或多行 message-header,一个空行表示 heade r结束(每一行由 CRLF 即 \r\n 结束。),也许有一个 body。

一条 start-line 可能为 Request-Line 或者 Status-Line

   start-line      = Request-Line | Status-Line

若为 Request-Line,则格式如下

Request-Line = Method SP Request-URI SP HTTP-Version CRLF 例如

GET / HTTP/1.0

由于在我们的toyws暂时只实现 GET 方法,在 Request 中,只需要读取 request-line 中的 URI 一项,检查是否合法是否存在,然后返回 URI 指定的资源即可。

在服务器返回的 Response-Line 中,start-line 则为 Status-Line, Status-Line 的格式如下 :

Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

Status-Code 代表的意义在 RFC-2616 中可以找到。在这里我们只用管 URI 是否合法,是否存在,存在则返回 200 OK ,不存在或者不能读取就返回 404 Not Found,若存在而无权限访问则返回 403 Forbidden。

Response 的完整定义如下 :

  Response      = Status-Line               ; Section 6.1
                   *(( general-header        ; Section 4.5
                    | response-header        ; Section 6.2
                    | entity-header ) CRLF)  ; Section 7.1
                   CRLF
                   [ message-body ]          ; Section 7.2

在返回的 Response 中,我们只实现 message-header 中的 entity-header 里 Content-Length 和 Content-Type 两项,告诉客户端返回的文件的大小和文件的类型。

最后,只用将内容加入到 message-body 中即可。

实现 POST 方法时,需要读取 Request Header 里面的 Content-Type 一项,默认为application/x-www-form-urlencoded

同时也需要读取 Content-Length,得到 Request Body 的长度。

根据 generic message format,连续两个 CRLF后即是 Request Body,按得到的 Content-Length 读取即可。