返回

ASIO-简单http服务器

介绍如何使用BOOST.ASIO实现一个简单的http服务器

本文将介绍如何使用 Boost.Asio 实现了一个简单的 http 服务器,可以简易的实现 GET 和 POST请求的处理,并且根据请求响应给客户端一个静态页面或者动态生成的页面。


客户端连接的处理

对于客户端连接的处理与上文 echo 服务器类似,使用 Server 类管理连接,( 本文中 ) 使用 Connection 类处理读写操作。使用智能指针延长对象的生命周期,继承 std::enable_shared_from_this 类解决两个智能指针指向同一个地址的问题。

本质上,http 服务器与 echo 服务器也十分相似,不同的只是发送的数据要是 HTTP 协议要求的格式。

客户端数据的处理

首先,要知道 HTTP 客户端发送的请求报文的基本格式如下:

————————————————————————————————
请 求 行| 请求方法 URI HTTP版本
————————————————————————————————
请求报头| key: value
请求报头| key: value
请求报头| key: value
请求报头| ...
————————————————————————————————
    空行|
————————————————————————————————
请求正文| ...

常用的请求方法是 GET 和 POST ,本文也只对这两类请求进行处理。

GET 请求一般没有正文内容,它会将信息放在 URI 中,所以以这种方式发送请求可能会导致信息泄露,如同登录操作就不推荐使用这种请求方式。

POST 请求会将请求信息存放在正文当中,因此,这种请求方式要比 GET 请求更加安全。为了能完整地获取请求信息,POST 请求的报头中会有一个 Content-Length 字段保存了正文的长度。

在 Connection 类读取到客户端发送的请求后,RequestParser 类会对接收到的报文进行处理。

请求报文每一行都以结束符 \r 和换行符 \n 为结尾。因此对于报文的处理可以一行行的进行。

首先是第一行:请求行。三个字段会以空格分隔,我们读取重要的信息:请求方法和 URI。

如若是 GET 方法,我们所需要的请求信息都在 URI 中,因此报头信息和正文就不需要进行处理了,只需要解读 URI;如若是 POST 方法,我们需要在报头中寻找 Content-Length 字段,获得正文长度,从而获得正文内容。

响应数据的处理

得到了请求的信息,我们使用 RequestHandler 类依据请求获得相应的文件,但是我们发送的 HTTP 响应也是要符合响应报文的格式的。

————————————————————————————————
状 态 行| 协议版本号 状态码 状态描述符
————————————————————————————————
响应报头| key: value
响应报头| key: value
响应报头| key: value
响应报头| ...
————————————————————————————————
    空行|
————————————————————————————————
响应正文| ...

我们通过 RequestHandler 类获取到的信息仅是正文部分,所以我们要增加状态行和响应报头的信息。其中响应报头部分需要的响应信息是 Content-Length 字段和 Content-Type 字段。

处理好的响应报文就可以使用 Connection 的写函数发送给客户端了。

CGI处理

CGI 是一种生成动态响应页面的技术。当客户端发来的请求报文中 URI 含有参数或者是 POST 请求时,我们可以使用 CGI 程序为客户响应他需要的页面。

如何执行 CGI 程序呢?

我们 fork 一个子进程,通过 exec 系列函数切换到 CGI 程序,通过匿名管道的技术进行父子进程的通信,从而获得响应页面的正文信息。

日志的处理

将日志划分为 debug、info、warn、error 四个等级。

通过宏 FILELINE 去获取当前运行到哪个文件哪个位置。


源码链接:HeiSeYuMiLi/httpserver

参考:Boost.Asio/examples

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy