6.3 Web服务器程序解释请求消息并作出响应
将请求的URI转换为实际的文件名
图6.8 Web 的基本工作方式.png
图6.9 客户端看到的目录结构和实际目录结构是不同的.png
Web服务器中,图6.7的read获取的数据内容就是HTTP请求消息。服务器程序会根据收到的请求消息中的内容进行相应的处理,并生成响应消息,再通过write返回给客户端。请求消息包括一个称为“方法”的命令,以及表示数据源的URI(文件路径名),服务器程序会根据这些内容向客户端返回数据,但对于不同的方法和URI,服务器内部的工作过程会有所不同。下面我们从简单的开始依次进行介绍。
最简单的一种情况如图6.8中的例子所示,请求方法为GET, URI为一个HTML文件名。这种情况只要从文件中读出HTML文档,然后将其作为响应消息返回就可以了。不过,按照URI从磁盘上读取文件并没有这么简单。如果完全按照URI中的路径和文件名读取,那就意味着磁盘上所有的文件都可以访问,Web服务器的磁盘内容就全部暴露了,这很危险。因此,这里需要一些特殊的机制。
Web服务器公开的目录其实并不是磁盘上的实际目录,而是如图6.9这样的虚拟目录,而URI中写的就是在这个虚拟目录结构下的路径名。因此,当读取文件时,需要先查询虚拟目录与实际目录的对应关系,并将URI转换成实际的文件名后,才能读取文件并返回数据。
运行CGI程序
图6.11 数据如何传递给 Web 服务器上运行的程序.png
Web服务器会检查URI指定的文件名,看一看这个文件是不是一个程序。这里的判断方法是在Web服务器中事先设置好的,一般是通过文件的扩展名来进行判断,例如将.cgi、.php等扩展名的文件设置为程序,当遇到这些文件时,Web服务器就会将它们作为程序来对待。也可以设置一个存放程序的目录,将这个目录下的所有文件都作为程序来对待。此外,还可以根据文件的属性来进行判断。
如果判断要访问的文件为程序文件,Web服务器会委托操作系统运行这个程序,然后从请求消息中取出数据并交给运行的程序。如果方法为GET,则将URI后面的参数传递给程序;如果方法为POST,则将消息体中的数据传递给程序(图6.11)。
Web服务器程序在组装网络包、还原数据之后,会运行其中指定的程序(实际是委托操作系统来运行),然后将数据传递给已运行的程序。
运行的程序收到数据后会进行一系列处理,并将输出的数据返回给Web服务器。
Web 服务器得访问控制
Web服务器的访问控制规则主要有以下3种。
(1)客户端IP地址
(2)客户端域名
(3)用户名和密码
图6.12 根据域名进行访问控制.png
当根据客户端域名设置规则时,需要先根据客户端IP地址查询客户端域名,这需要使用DNS服务器。一般我们使用DNS服务器都是根据域名查询IP地址,其实根据IP地址反查域名也可以使用DNS服务器。具体来说,这个过程是这样的。
收到客户端的请求消息后,Web服务器(图6.12①)会委托协议栈告知包的发送方IP地址,然后用这个IP地址生成查询消息并发送给最近的DNS服务器(图6.12②)。
接下来,DNS服务器找出负责管辖该IP地址的DNS服务器,并将查询转发给它(图6.12③),查询到相应的域名之后返回结果(图6.12④),然后Web服务器端的DNS服务器再将结果转发给Web服务器(图6.12⑤)。这样一来,我们就可以根据发送方IP地址查询到域名。
接下来,为了保险起见,还需要用这个域名查询一下IP地址,看看结果与发送方IP地址是否一致(图6.12⑥)。这是因为有一种在DNS服务器上注册假域名的攻击方式,因此我们需要进行双重检查,如果两者一致则检查相应的访问控制规则,判断是否允许访问。从图6.12中可以看出,这种方式需要和DNS服务器进行多次查询,整个过程比较耗时,因此Web服务器的响应速度也会变慢。
图6.13 利用 HTTP 验证用户名和密码.png
如果用户名和密码已设置好,那么情况如图6.13。通常的请求消息中不包含用户名和密码,因此无法验证用户名和密码(图6.13①)。
因此,Web服务器会向用户发送一条响应消息,告诉用户需要在请求消息中放入用户名和密码(图6.13②)。
浏览器收到这条响应消息后,会弹出一个输入用户名和密码的窗口,用户输入用户名和密码后(图6.13③),浏览器将这些信息放入请求消息中重新发送给服务器(图6.13④)。
然后,Web服务器查看接收到的用户名和密码与事先设置好的用户名和密码是否一致,以此判断是否允许访问,如果允许访问,则返回数据(图6.13⑤)。
返回响应消息
首先,Web服务器调用Socket库的write,将响应消息交给协议栈。这时,需要告诉协议栈这个响应消息应该发给谁,但我们并不需要直接告知客户端的IP地址等信息,而是只需要给出表示通信使用的套接字的描述符就可以了。套接字中保存了所有的通信状态,其中也包括通信对象的信息,因此只要有描述符就万事大吉了。
接下来,协议栈会将数据拆分成多个网络包,然后加上头部发送出去。这些包中包含接收方客户端的地址,它们将经过交换机和路由器的转发,通过互联网最终到达客户端。