HTTP-Tinyhttpd解析

       Tinyhttpd是一个微型的web服务器,浏览器与Web服务器之间的通信采用的是Http,所以一开始的切入点是HTTP协议,这里说一点如果有做HTTP通信的开发还是看一下RFC中对不同版本HTTP的定义,以下原理部分都是从《后台开发:核心技术与应用实践》中HTTP协议章节中裁剪出来的,对后台感兴趣的同学可以看一下,讲述后台开发所需要具备的技术点一本很不错的书。

一、HTTP协议

HTTP工作流程:

  在OSI七层模型中,HTTP是基于TCP上的应用层协议而我们所说的HTTPS基于同处于应用层的TLS、SSL协议层之上。HTTP默认的端口号为80,HTTPS默认的端口号为443。在HTTP1.1中(通过Connection头设置)默认在HTTP传输完成后不断开TCP连接,在此之前的HTTP版本则默认是断开连接的,也就是说这次请求与上次请求是不同的两个TCP连接。一次HTTP操作称为一个事务,其工作过程可以分为以下四步。

  (1)首先客户机与服务器需要建立连接 。 只要单击某个超级链接, HTTP 的工作即开始 。
  (2)建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL )、协议版本号,后边是 MIME 信息(包括请求修饰符、客户机信息和可能的内容) 。

  (3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是 MTh伍信息(包括服务器信息、实体信息和可能的内容) 。

  (4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接 。

HTTP协议结构:

  HTTP 协议无论是请求报文还是回应报文,都分为以下 4 个部分 。

  (1)报文头( initial line ),上面的例子中的“ GET http://www.baidu.com/favicon.icoHTTP/1.1 ”表示用 GET 方法请求 http://www.baidu.com/favicon. ic。这个文件,用的是HTTP/1.1 协议。

  (2) 0 个或多个请求头( header line ),例如 Accept-Language: en

  (3)空行(作为 header lines 的结束) 。

  (4)可选的消息体 。

  HTTP 协议是基于行的协议,每一行 以 \r\n 作为分隔符 。 报文头通常表明报文的类型(例如请求类型),且报文头只占一行 ;请求头附带一些特殊信息,每一个请求头占一行,其格式为 name:value ,即以分号作为分隔; 空行也就以一个 \r\n 分隔;可选 body 通常包含数据,例如服务器返回的某个静态 HTML 文件的内容 。

  HTTP请求方法:

  HTTP/ 1.1 协议中共定义了 9 种方法(有时也叫“动作”)来表 明 Request-URI 指定的资源的不同操作方式,如下所述。

  ( 1 ) OPTIONS :返回服务器针对特定资源所支持的 HTTP 请求方法;也可以利用向 Web服务器发送“*”的请求来测试服务器的功能性。

  ( 2) HEAD :向服务器索要与 GET 请求相一致的响应,只不过响应体将不会被返回 。 这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息 。该方法常用于测试超链接的有效性,是否可以访问,以及最近是否更新等信息 。

  ( 3) GET :向特定的资源发出请求 。 注意 : GET 方法不应当被用于产生“副作用”的操作中,例如在 web app . 中的应用,其中一个原因是 GET 可能会被网络蜘蛛等随意访问 。

  ( 4 ) POST :向指定资源提交数据进行处理请求(例如提交表单或者上传文件) 。 数据被包含在请求体中 。 POST 请求可能会导致新的资源的建立或对已有资源的修改 。

  ( 5 ) PUT :向指定资源位置上传其最新内容 。

  ( 6) DELETE : 请求服务器删除 Request-URI 所标识的资源 。

  ( 7 ) TRACE :回显服务器收到的请求,主要用于测试或诊断。

  ( 8) CONNECT: HTTP/ 1.1 协议中预留给能够将连接改为管道方式的代理服务器 。

  ( 9) PATCH :用来将局部修改应用于某一资源,该操作添加于规范盯C5789 中 。

  HTTP 服务器至少应该实现 GET 和 HEAD 方法,其他方法都是可选的 。 此外,除了上述方法,特定的 HTTP 服务器还能够扩展自定义的方法 。

  HTTP 常见的请求头:

  在 HTTP/ l.l 协议中,所有的请求头(除 Host 外)都是可选的,因为Host主要用于请求的服务器的IP地址和端口号,请求头有Host、Connection、Accept、Accept-Encoding、User-Agent、Cookie等,请求头太多这里就不列出来了。

  HTTP回应报文:

  返回码由 3 位数字组成,第一个数字定义了响应的类别,且有 5 种可能的取值。

  ( 1 ) lxx :指示信息,表示请求已接收,继续处理。

  ( 2) 2xx :成功,表示请求已被成功接收、理解 、 接受 。

  ( 3) 3xx :重定向,要完成请求必须进行更进一步的操作 。

  ( 4 ) 4xx :客户端错误,请求有语法错误或请求无法实现。

  ( 5) 5xx :服务器端错误,服务器未能实现合法的请求 。

  Date :表示消息发送的时间,时间的描述格式由 rfc822 定义 。

  Server : 指明 Web 服务器用来处理请求的软件信息 。

  Accept-Ranges : Web 服务器表明自己是否接收获取其某个实体的一部分(比如文件的一部分)的请求 。 bytes 表示接收, none 表示不接收。

  Vary: Web 服务器用该头部的内容告诉 Cache 服务器,在什么条件下才能用本响应所返回的对象响应后续的请求 。

  Content-Encoding : Web 服务器表明自己使用了什么压缩方法( gzip, deflate)压缩响应中的对象。

  Content-Length: Web 服务器告诉浏览器自己响应的对象的长度 。

  Content-Type: Web 服务器告诉浏览器自己响应的对象的类型 。

二、CGI

  CGI ( Common Gateway Interface ,通用网关接口)是 HTTP 协议中最重要的技术之一,有着不可替代的重要地位 。 CGI 是一个 Web 服务器提供信息服务的标准接口 。 通过 CGI 接口, Web 服务器就能够获取客户端提交的信息,转交给服务器端的 CGI 程序进行处理,最后返回结果给客户端 。 组成 CGI 通信系统的是两部分: 一部分是 HTML 页面,就是在用户端浏览器上显示的页面;另一部分则是运行在服务器上的 CG I 程序 。

  浏览器只需要指定执行服务器上的哪个CGI程序就行,一般情况下,服务器和 CGI 程序之间是通过标准输入输出来进行数据传递的(就像tinyhttpd中调用CGI程序),而这个过程需要环境变茸的协作方可实现。环境变量在 CGI 中有着重要的地位,每个 CGI 程序只能处理一个用户请求,所以在激活一个CGI程序进程时也创建了属于该进程的环境变量,CGI程序能够用Python、PERL、Shell、C或C++等语言来实现。

  CGI 环境变量在 CGI 程序启动时初始化,在结束时销毁。当一个 CGI 程序不是被 HTTP 服务器调用时,它的环境变盘几乎是系统环境变量的复制 。当这个 CGI 程序被 HTTP 服务器调用时,它的环境变量就会多了以下关于HTTP 服务器、客户端、 CGI 传输过程等项目。CGI 相关的环境变量有 3 种:与请求相关的环境变量、与服务器相关的环境变量和与客户端相关的环境变量,,详细见表 12-1 。

  

  CGI 工作原理:每当客户请求 CGI 的时候 Web 服务器就请求操作系统生成一个新的CGI 进程,该进程处理完请求后退出,下一个请求来时再创建新进程 。 当然,这样在访问量很少没有并发的情况可行,可是当访问量增大且并发存在时,这种方式就不适合了,于是就有了FastCGI。

  一般情况下, FastCGI 的整个工作流程如下所述。

  ( 1 ) Web Server 启动时载入 FastCGI 进程管理器( IIS ISAPI 或 Apache Module ) 。

  ( 2 )FastCGI 进程管理器自身初始化,启动多个 CGI 进程并等待来自 Web 服务器的连接 。

  ( 3 )当客户端请求到达 Web Server 时, FastCGI 进程管理器选择并连接到一个 FastCGI进程 。 Web 服务器将 CGI 环境变量和标准输入发送到 FastCGI 进程 。

  ( 4) FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回 Web Server。当FastCGI 子进程关闭连接时,请求便被告知处理完成 。 FastCGI 进程接着等待并处理来自FastCGI 进程管理器(运行在 Web 服务器中)的下一个连接 。

三、tinyhttpd解析

1.main函数初始化套接口监听并接收连接,针对每个连接开启线程去处理

2,accept_request函数解析客户端请求,判断是请求静态文件还是cgi代码(通过请求类型以及参数来判定),如果是静态文件则将文件输出给前端,如果是cgi则进入cgi处理函数

3,execute_cgi函数负责将请求传递给cgi程序处理,服务器与cgi之间通过管道pip通信,首先初始化两个管道,并创建子进程去执行cgi函数

 子进程中,用刚才初始化的管道替换掉标准输入标准输出,将请求参数加到环境变量中,调用execl执行cgi程序获得输出。

 服务器"线程"继续将post的请求头发送给子进程(cgi程序),然后获取cgi程序的标准输出作为响应内容发送给客户端。

 

         502行代码都比较精炼,服务器在完成一些web功能外还处理了一些异常情况,500状态码cgi执行出错,404找不到文件错误之类的,开始以为putenv函数会设置个全局变量,当多用户访问的时候会互相影响,后来测试了一下发现设置的变量是互相隔离的,这些代码值得学习。

发布了36 篇原创文章 · 获赞 4 · 访问量 2790

猜你喜欢

转载自blog.csdn.net/qq845699/article/details/104167264
今日推荐