阅读tinyhttpd前准备的知识

阅读tinyhttpd前准备的知识

这是以前写的,还没怎么了解HTTP就看源码,这是不对的。

关于HTTP协议

http工作流程

文章写的比较乱,并且引用了许多网上的资源,
只是想查找方便。。。
建议看:HTTP基本原理-简书

一次HTTP操作称为一个事务,其工作过程可分为四步:

1)首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。

2)建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。

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

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

一个客户机与服务器建立连接后,发送一个请求给服务器,请求方式的格式为,统一资源标识符url、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。服务器接到请求后,给予相应的响应信息,其格式为一个状态行包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。

HTTP请求过程

在这里插入图片描述在这里插入图片描述

1. 请求行 (请求方法 , 请求url , http版本)

2. 请求头 key- value形式

3. 空一行

4.请求体 ==> 参数

(get请求没有请求体)

(post请求将用户名id与密码…放入请求体)

在浏览器输入一个url,回车后在浏览器中观察到页面内容,其中的过程是浏览器向网站所在服务器发送一个Request,即请求,网站服务器接收到Request后进行处理和解析,然后返回对应的Response,即响应,然后传回浏览器,Response中包含了页面的源代码等内容,浏览器在对其进行解析便将网页呈现出来。

img

网络请求记录

这一个条目的各列分别代表:

第一列 Name,即 Request 的名称。一般会用URL的最后一部分内容当做名称。

第二列 Status,即 Response 的状态码。这里显示为 200,代表 Response 是正常的,通过状态码我们可以判断发送了 Request 之后是否得到了正常的 Response。

第三列 Type,即 Request 请求的文档类型。这里为 document,代表我们这次请求的是一个 HTML 文档,内容就是一些 HTML 代码。

第四列 Initiator,即请求源。用来标记 Request 是由哪个对象或进程发起的。

第五列 Size,即从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源则该列会显示 from cache。

第六列 Time,即发起 Request 到获取到 Response 所用的总时间。

第七列 Waterfall,即网络请求的可视化瀑布流。

点击条目可以看到更详细的信息,如图:

img

http报文格式

General 部分,Request URL 为 Request 的 URL,Request Method 为请求的方法,Status Code 为响应状态码,Remote Address 为远程服务器的地址和端口,Referrer Policy 为 Referrer 判别策略。

Response Headers 和一个 Request Headers,这分别代表响应头和请求头,请求头里面带有许多请求信息,例如浏览器标识、Cookies、Host 等信息,这是 Request 的一部分,服务器会根据请求头内的信息判断请求是否合法,进而作出对应的响应,返回 Response,那么在图中看到的 Response Headers 就是 Response 的一部分,例如其中包含了服务器的类型、文档类型、日期等信息,浏览器接受到 Response 后,会解析响应内容,进而呈现网页内容。

客户端请求消息

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:

请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。 img

Request,请求,由客户端向服务端发出。可以将Request划为四部分内容:Request Method、Request URL、Request Headers、Request Body,即请求方式、请求链接、请求头、请求体。

请求方式

常见两种GET和POST。

在浏览器中直接输入一个URL并回车,这便发起一个GET请求,请求参数会直接包含到URL里,如百度搜索Python,这就是一个GET请求,链接为:https://www.baidu.com/s?wd=Python,URL中包含了请求的参数信息,这里的参数wd就是要搜寻的关键词。POST请求大多为表单提交发起,如登录表单,输入用户名和密码,点击登录,通常会发起一个POST请求,其数据通常以Form Data即表单形式传输,不会体现在URL中。

GET和POST请求方法的区别:

  1. GET 方式请求中参数是包含在 URL 里面的,数据可以在 URL 中看到,而 POST 请求的 URL 不会包含这些数据,数据都是通过表单的形式传输,会包含在 Request Body 中。采用GET方法提交HTML表单数据的时候,客户机将把这些数据附加到由ACTION标记命名的URL的末尾,用一个包括把经过URL编码后的信息与CGI程序的名字分开:

  2. GET 方式请求提交的数据最多只有 1024 字节,而 POST 方式没有限制。get请求的参数在url的后面 ; post在请求体中间

  3. 以 GET 方式接收的数据是有长度限制,而用 POST 方式接收的数据是没有长度限制的。并且,以 GET 方式发送数据,可以通过URL 的形式来发送,但 POST方式发送的数据必须要通过 Form 才到发送。

  4. GET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.POST方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用.

img

get与post实例

下面实例是一点典型的使用GET来传递数据的实例:

客户端请求:

GET /hello.txt HTTP/1.1        //请求行:Get请求方法 .txt->URL
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com         //请求头部
Accept-Language: en, mi      

服务端响应:

HTTP/1.1 200 OK     			//状态行
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT		
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain		//消息报头   纯文本格式

输出结果:

Hello World! My payload includes a trailing CRLF.
  • GET:向特定的资源发出请求。

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

1 GET 请求指定的页面信息,并返回实体主体。
2 HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
请求头
image-20200325214952099

用来说明服务器要使用的附加信息,一些常用头:

Accept,请求报头域,用于指定客户端可接受那些类型的信息。

Accept-Language,指定客户端可接受语言类型。

Accept-Encoding,指定客户端可接受的内容编码。

Host,用于指定请求资源的主机IP和端口号,其内容为请求URL的原始服务器或网关的位置。

Cookie/Cookies,是网站为了辨别用户进行Session跟踪而存储在本地的数据。Cookies 的主要功能就是维持当前访问会话,例如我们输入用户名密码登录了某个网站,登录成功之后服务器会用 Session 保存我们的登录状态信息,后面我们每次刷新或请求该站点的其他页面时会发现都是保持着登录状态的,在这里就是 Cookies 的功劳,Cookies 里有信息标识了我们所对应的服务器的 Session 会话,每次浏览器在请求该站点的页面时都会在请求头中加上 Cookies 并将其发送给服务器,服务器通过 Cookies 识别出是我们自己,并且查出当前状态是登录的状态,所以返回的结果就是登录之后才能看到的网页内容。

Referer,此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如做来源统计、做防盗链处理等。

User-Agent,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息可以伪装为浏览器,如果不加很可能会被识别出为爬虫。

Content-Type,即 Internet Media Type,互联网媒体类型,也叫做 MIME 类型,在 HTTP 协议消息头中,使用它来表示具体请求中的媒体类型信息。例如 text/html 代表 HTML 格式,image/gif 代表 GIF 图片,application/json 代表 Json 类型

Request Headers 是 Request 等重要组成部分,在写爬虫的时候大部分情况都需要设定 Request Headers。

请求体

请求体,一般承载的内容是POST请求中的Form Data,即表单数据,而对于GET请求Request Body则为空。

在Request Headers中指定Content-Type

img

Content-Type和POST提交数据方式的关系

在爬虫中如果要构造POST请求需注意这几种Content-Type,了解请求库的各个参数设置时使用的是哪种Content-Type,不然可能会导致POST提交后得不到正常的Response。

Response

Response,即响应,由服务端返回给客户端。Response 可以划分为三部分,Response Status Code、Response Headers、Response Body。

服务器响应消息

响应报文

1.状态行 (http协议版本 , 状态码 ,状态描述OK)

2.消息报头(响应头)

3.空一行

4.响应正文

img在这里插入图片描述

在这里插入图片描述

响应状态码

响应状态码,状态码表示了服务器响应状态,如200表示服务器正常响应,404代表页面未找到,500代表服务器内部发生错误。在爬虫中可以根据状态码来判断服务器响应状态,如判断为200,则证明成功返回数据,再进行进一步处理,否则直接忽略。

img

常见的错误代码及错误原因

响应头

包含服务器对请求的应答信息,如Context-Type、Server、Set-Cookie等,一些常见的头信息:

Date,标识Response产生的时间

Last-Modified,指定资源的最后修改时间

Content-Encoding,指定Response内容的编码

Serve,包含了服务器的信息,名称,版本号等

Context-Type,文档类型,指定了返回的数据类型是什么。

Set-Cookie,设置Cookie,告诉浏览器要将此内容放在Cookies中,下次请求携带Cookies请求

关于HTTPS

HTTP的全称Hyper Text Transfer Protocol,中文名称叫超文本传输协议,HTTP协议是用于从网络传输超文本数据到本地浏览器的传输协议,它能保证传送高效而准确的传送超文本文档。

HTTPS的全称是Hyper Text Transfer Protocol over Secure Socket Layer,是以安全为目标的HTTP通道,就是HTTP安全版,即HTTP下加入SSL层,简称HTTPS。

HTTPS的安全基础是SSL,通过它传输的内容都是SSL加密的,主要作用有两种:

建立一个信息安全通道,保证数据传输的安全。

确认网站的真实性,凡是使用HTTPS的网站都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息,也可通过CA机构颁发的安全签章来查询。

关于URL

image-20200325214156931

img

在WWW上,每一信息资源都有统一的且在网上唯一的地址,该地址就叫URL(Uniform Resource Locator,统一资源定位符),它是WWW的统一资源定位标志,就是指网络地址

URL特殊字符需转义

1、空格换成加号(+)
2、正斜杠(/)分隔目录和子目录
3、问号(?)分隔URL和查询
4、百分号(%)制定特殊字符
5、#号指定书签 井号在URL中指定的是页面中的一个位置
6、&号分隔参数(键值对列表)

关于CGI

cgi服务器和客户端之间的通信,是客户端的浏览器和服务器端的http服务器之间的HTTP通信,我们只需要知道浏览器请求执行服务器上哪个CGI程序就可以了,其他不必深究细节,因为这些过程不需要程序员去操作。

服务器和CGI程序之间的通讯才是我们关注的。一般情况下,服务器和CGI程序之间是通过标准输入输出来进行数据传递的,而这个过程需要环境变量的协作方可实现。

1. 服务器将URL指向一个应用程序

2. 服务器为应用程序执行做准备

3. 应用程序执行,读取标准输入和有关环境变量

4. 应用程序进行标准输出

对于Windows系统而言,还可以通过profile文件进行数据传输(如ini文件),但在这里不做研究。环境变量在CGI中有着重要的地位!每个CGI程序只能处理一个用户请求,所以在激活一个CGI程序进程时也创建了属于该进程的环境变量。

cgi详解(转载)

cgi:公共网关接口(Common Gateway Interface,CGI)是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据API与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。

使用CGI实现客户端与服务器的交互有以下几个标准步骤,具体步骤如下:

  1. Web 客户端的浏览器将URL的第一部分解码与Web服务器相连。

  2. Web 浏览器将URL的其余部分提供给服务器。

  3. Web 服务器将URL转换成路径和文件名。

  4. Web 服务器发送 HTML 和别的组成请求页面的文件给客户。一旦页面内容传送完,这个连接自动断开。

  5. 在客户端,HTML脚本提示用户做动作或输入。当用户响应后,客户请求Web服务器建立一个新的连接。

  6. Web 服务器把这些信息和别的进程变量传送给由HTML以URL的形式指定CGI程序。

  7. CGI 根据输入作出响应,把响应结果传送给 Web 服务器。

  8. Web 服务器把响应的数据传给客户,完成后关闭连接。

当我们在谈到cgi的时候,我们在讨论什么

最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html。事物总是不断发展,网站也越来越复杂,所以出现动态技术。但是服务器并不能直接运行 php,asp这样的文件,自己不能做,外包给别人吧,但是要与第三做个约定,我给你什么,然后你给我什么,就是握把请求参数发送给你,然后我接收你的处理结果给客户端。那这个约定就是 common gateway interface,简称cgi。这个协议可以用vb,c,php,python 来实现。cgi只是接口协议,根本不是什么语言。下面图可以看到流程

img

WEB服务器与cgi程序交互

 WEB服务器将根据CGI程序的类型决定数据向CGI程序的传送方式,一般来讲是通过标准输入/输出流和环境变量来与CGI程序间传递数据。 如下图所示:

img

CGI程序通过标准输入(STDIN)和标准输出(STDOUT)来进行输入输出。此外CGI程序还通过环境变量来得到输入,操作系统提供了许多环境变量,它们定义了程序的执行环境,应用程序可以存取它们。Web服务器和CGI接口又另外设置了一些环境变量,用来向CGI程序传递一些重要的参数。CGI的GET方法还通过环境变量QUERY-STRING向CGI程序传递Form中的数据。 下面是一些常用的CGI环境变量:

变量名 描述
CONTENT_TYPE 这个环境变量的值指示所传递来的信息的MIME类型。目前,环境变量CONTENT_TYPE一般都是:application/x-www-form-urlencoded,他表示数据来自于HTML表单。
CONTENT_LENGTH 如果服务器与CGI程序信息的传递方式是POST,这个环境变量即使从标准输入STDIN中可以读到的有效数据的字节数。这个环境变量在读取所输入的数据时必须使用。
HTTP_COOKIE 客户机内的 COOKIE 内容。
HTTP_USER_AGENT 提供包含了版本数或其他专有数据的客户浏览器信息。
PATH_INFO 这个环境变量的值表示紧接在CGI程序名之后的其他路径信息。它常常作为CGI程序的参数出现。
QUERY_STRING 如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值即使所传递的信息。这个信息经跟在CGI程序名的后面,两者中间用一个问号’?'分隔。
REMOTE_ADDR 这个环境变量的值是发送请求的客户机的IP地址,例如上面的192.168.1.67。这个值总是存在的。而且它是Web客户机需要提供给Web服务器的唯一标识,可以在CGI程序中用它来区分不同的Web客户机。
REMOTE_HOST 这个环境变量的值包含发送CGI请求的客户机的主机名。如果不支持你想查询,则无需定义此环境变量。
REQUEST_METHOD 提供脚本被调用的方法。对于使用 HTTP/1.0 协议的脚本,仅 GET 和 POST 有意义。
SCRIPT_FILENAME CGI脚本的完整路径
SCRIPT_NAME CGI脚本的的名称
SERVER_NAME 这是你的 WEB 服务器的主机名、别名或IP地址。
SERVER_SOFTWARE 这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(Unix)

TinyHttpd

建议源码阅读顺序: main -> startup -> accept_request -> execute_cgi, 通晓主要工作流程后再仔细把每个函数的源码看一看。

https://img-blog.csdn.net/20180803011306188?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzE5NTk2ODM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-34CBFpNI-1585471342901)(阅读tinyhttpd前准备的知识.assets/image-20200324200502514.png)]

Lighttpd

从图中,我们可以看到对于客户端的每一个请求(动态页面,也就是需要CGI处理的),Lighttpd进程都是先fork一个CGI进程,然后将请求头**(通过环境参数)和请求体(通过管道)**传递给CGI程序,等待CGI处理并将从CGI进程那接收到的处理结果再响应给客户端,然后将CGI终止(通过发送SIGTERM信号)。

CGI的处理有一些缺点,先不说其它的,单从图中可以看到的就有:首先,对于客户端的每一个请求都需要fork一个CGI进程,然后在该请求处理结束后又将该CGI进程kill掉,性能自然是不高的。其次,Web Server(这里指Lighttpd)和CGI之间通信采用无名管道(PIPE)进行通信,因此具有无名管道的所有缺点(比如:Web Server进程和CGI必须具有亲缘关系;管道是半双工的,数据只能向一个方向流动,因此为了使Web Server和CGI进行双方通信,必须建立起两个管道等等)。由这两个缺点就衍生出很多其它的缺点,比如基本无法进行分布式部署和在CGI侧进行负载均衡等。

复刻(英语:fork,又译作派生分支)是UNIX或类UNIX中的分叉函数,fork函数将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。

1.POST方法
如果采用POST方法,那么客户端来的用户数据将存放在CGI进程的标准输入中,同时将用户数据的长度赋予环境变量中的CONTENT_LENGTH。客户端用POST方式发送数据有一个相应的MIME类型。目前,MIME类型一般是:application/x-wwww-form-urlencoded,该类型表示数据来自HTML表单。该类型记录在环境变量CONTENT_TYPE中,CGI程序应该检查该变量的值。

2.GET方法
在该方法下,CGI程序无法直接从服务器的标准输入中获取数据,因为服务器把它从标准输入接收到得数据编码到环境变量QUERY_STRING(或PATH_INFO)。【问号后面】

环境变量是一个保存用户信息的内存区。当客户端的用户通过浏览器发出CGI请求时,服务器就寻找本地的相应CGI程序并执行它。在执行CGI程序的同时,服务器把该用户的信息保存到环境变量里。接下来,CGI程序的执行流程是这样的:查询与该CGI程序进程相应的环境变量:第一步是request_method,如果是POST,就从环境变量的len,然后到该进程相应的标准输入取出len长的数据。如果是GET,则用户数据就在环境变量的QUERY_STRING里。


流程介绍:

https://www.cnblogs.com/wanpengcoder/p/5304521.html

(1) 服务器启动,等待客户端请求到来;

(2) 客户端请求到来,创建新线程处理该请求;

(3) 读取httpHeader中的method,截取url,其中GET方法需要记录url问号之后的参数串;

(4) 根据url构造完整路径,如果是/结尾,则指定为该目录下的index.html;

(5) 获取文件信息,如果找不到文件,返回404,找到文件则判断文件权限;

(6) 如果是GET请求并且没有参数,或者文件不可执行,则直接将文件内容构造http信息返回给客户端;

(7) 如果是GET带参数,POST,文件可执行,则执行CGI;

(8) GET请求略过httpHeader,POST方法需要记录httpHeader中的Content-Length:xx;

(9) 创建管道用于父子进程通信,fork产生子进程;

(10) 子进程设置环境变量,将标准输入和输出与管道相连,并且通过exec执行CGI;

(11) 如果是POST,父进程将读到post内容发送给子进程,并且接收子进程的输出,输出给客户端;

img

tinyhttpd工作流程

​ (1) 服务器启动,在指定端口或随机选取端口绑定 httpd 服务。
​ (2)收到一个 HTTP 请求时(其实就是 listen 的端口 accpet 的时候),派生一个线程运行 accept_request 函数。
​ (3)取出 HTTP 请求中的 method (GET 或 POST) 和 url,。对于 GET 方法,如果有携带参数,则 query_string 指针指向 url 中 ? 后面的 GET 参数。
​ (4) 格式化 url 到 path 数组,表示浏览器请求的服务器文件路径,在 tinyhttpd 中服务器文件是在 htdocs 文件夹下。当 url 以 / 结尾,或 url 是个目录,则默认在 path 中加上 index.html,表示访问主页。
​ (5)如果文件路径合法,对于无参数的 GET 请求,直接输出服务器文件到浏览器,即用 HTTP 格式写到套接字上,跳到(10)。其他情况(带参数 GET,POST 方式,url 为可执行文件),则调用 excute_cgi 函数执行 cgi 脚本。
​ (6)读取整个 HTTP 请求并丢弃,如果是 POST 则找出 Content-Length. 把 HTTP 200 状态码写到套接字。
​ (7) 建立两个管道,cgi_input 和 cgi_output, 并 fork 一个进程。
(8)(处理cgi) 在子进程中,把 STDOUT 重定向到 cgi_output 的写入端,把 STDIN 重定向到 cgi_input 的读取端,关闭 cgi_input 的写入端 和 cgi_output 的读取端,设置 request_method 的环境变量,GET 的话设置 query_string 的环境变量,POST 的话设置 content_length 的环境变量,这些环境变量都是为了给 cgi 脚本调用,接着用 execl 运行 cgi 程序。
​ (9)(浏览器) 在父进程中,关闭 cgi_input 的读取端 和 cgi_output 的写入端,如果 POST 的话,把 POST 数据写入 cgi_input,已被重定向到 STDIN,读取 cgi_output 的管道输出到客户端,该管道输入是 STDOUT。接着关闭所有管道,等待子进程结束。这一部分比较乱,见下图说明**



图 1    管道初始状态


图 2  管道最终状态 


    (10) 关闭与浏览器的连接,完成了一次 HTTP 请求与回应,因为 HTTP 是无连接的。


http://www.51testing.com/html/43/489243-3715857.html)

猜你喜欢

转载自blog.csdn.net/qq_45737419/article/details/105180860
今日推荐