原文地址:HTTP servers
本文档介绍的是实现HTTP server相关的类。
HTTPServer是socketserver.TCPServer的子类,它创建并监听一个HTTP socket,将请求分发给handler。创建并运行server的代码如下:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
其中:
class http.server.HTTPServer(server_address, RequestHandlerClass)
这个类会建立一个TCPServer并存储server地址(存储在名称为server_name和server_port的变量中)。这个类建立的server对于handler是可见的,handler有一个名称为server的变量指向它。
HTTPServer需要传入RequestHandlerClass作为参数,其有三个变体:
class http.server.BaseHTTPRequestHandler(request, client_address, server)
这个类可以处理发送到server的HTTP请求,它自己并不会响应任何HTTP请求,你需要在它的子类中处理这些请求。但是它提供了很多有用的类、变量和方法。在这个类中,它会解析请求和header,然后根据请求类型调用相应方法(这些方法的名称是由请求指定的,例如,请求中指定方法SPAM,对应的方法即为没有参数的do_SPAM()方法)。这个类中还保存了请求的相关信息。另外,子类不需要重写__init__()
方法。
BaseHTTPRequestHandler包含以下变量:
- client_address
元组,(host,port),表示client地址 - server
表示server实例 - close_connection
boolean,表示是否接受其他的请求,即连接是否会在收到一个请求后关闭,此值应该在handle_one_request()方法返回前设置 - requestline
指HTTP请求的string内容。会去掉回车换行符。这个值通过handle_one_request()方法设置。如果没有可用的请求行,它应该被设置为空 - command
表示请求类型。例如‘GET’ - path
表示请求路径 - request_version
表示请求的版本信息。例如’HTTP/1.0’ - headers
表示一个MessageClass实例。它会解析并管理HTTP请求的头信息。其内部是使用http.client的parse_headers()方法解析头信息的,其要求HTTP请求的头信息支持RFC 2822格式 - rfile
io.BufferedIOBase类型的输入流,用于读取输入数据 - wfile
表示返回给client的响应的输出流。向此输出流写数据时必须符合HTTP协议
BaseHTTPRequestHandler包含以下属性:
- server_version
表示server的版本。你可以重写它,格式为多个空格分割的字符串,每个字符串为“name[/version]”形式,如 ‘BaseHTTP/0.2’ - sys_version
表示Python系统的版本,如’Python/1.4’ - error_message_format
表示错误信息的格式。send_error()方法会使用它格式化错误信息并将其发送给client。其默认值为基于response的状态码的字符串 - error_content_type
表示发送给client的错误响应的内容类型。默认值为’text/html’ - protocol_version
表示response的HTTP协议版本。如果设置为’HTTP/1.1’,那么server会允许长连接,但是server必须在每个响应中通过send_header()指定确切的头部信息:Content-Length。默认值为’HTTP/1.0’ - MessageClass
表示一个email.message.Message,其用于解析HTTP头信息。通常你不需要修改它,使用默认值http.client.HTTPMessage即可。 - responses
其包含错误码与错误信息的映射,错误信息为包含两个元素的元组。例如{code: (shortmessage, longmessage)}。其中,shortmessage通常用来表示错误信息的message key,longmessage表示explain key。这个属性会被send_response_only()和send_error()使用
BaseHTTPRequestHandler包含以下方法:
- handle()
调用handle_one_request()处理传入的HTTP请求(如果是长连接,可以调用多次,否则只需调用一次)。你不应该重写这个方法,只需要实现适当的do_*()方法即可 - handle_one_request()
这个方法会解析并分发请求到相应的do_*()方法。你不应该重写这个方法 - handle_expect_100()
当支持HTTP/1.1的server收到Expect: 100-continue的请求头时,它会返回一个100 Continue和200 OK。如果server不希望client能够continue,可以重写这个方法,返回一个异常。例如,sever可以返回417 Expectation Failed作为响应 - send_error(code, message=None, explain=None)
发送错误信息给client。其参数code表示HTTP错误码,message表示简短的错误描述,explain表示错误的相关信息。explain会被error_message_format格式化,然后添加到组装完成的头信息后面,作为响应的body。如果未指定message和explain,此方法会使用responses属性指定的值,对于responses属性中没有的code,默认message和explain为“???”。如果方法为HEAD那么body会为空。如果响应码为1xx, 204 No Content, 205 Reset Content, 304 Not Modified,body也为空 - send_response(code, message=None)
向header buffer添加响应头信息。HTTP响应会写入内部buffer,后面是Server和Date头信息(这两个信息通过version_string()和date_time_string()方法获得)。如果server不使用send_header()发送头信息,那么在send_response()后需要调用end_headers() - send_header(keyword, value)
向内部buffer添加HTTP头信息。当调用end_headers()或者flush_headers()时,添加到内部buffer的HTTP头信息会被写到输出流。注意,在调用send_header后必须调用end_headers() - send_response_only(code, message=None)
仅发送response头信息,当server向client发送100 Continue时使用。头信息不会立刻缓存并发送到输出流。如果message未指定,那么将会发送code对应的HTTP信息 - end_headers()
向头信息buffer添加一个空行,以指明response的HTTP头信息已结束,然后会调用flush_headers() - flush_headers()
将头信息发送到输出流,并刷新内部头信息buffer - log_request(code=’-‘, size=’-‘)
日志输出进入的请求 - log_error(…)
- log_message(format, …)
- version_string()
返回server的版本信息,包括server_version和sys_version - date_time_string(timestamp=None)
返回指定的时间戳,如果为空,那么将使用当前时间。示例:’Sun, 06 Nov 1994 08:49:37 GMT’ - log_date_time_string()
- address_string()
返回client地址
class http.server.SimpleHTTPRequestHandler(request, client_address, server)
这个类用于从当前目录及子目录提供文件,会将目录结构映射到HTTP request。
很多工作,如解析request,已经在BaseHTTPRequestHandler中完成了。本类则实现了do_GET()和do_HEAD()方法。
SimpleHTTPRequestHandler的类级属性如下:
- server_version
格式为"SimpleHTTP/" + __version__
,其中__version__
定义在module级 - extensions_map
表示MIME类型与后缀映射的字典。默认值为空,表示“application/octet-stream”。这个映射是大小写不敏感的,所以key请使用小写
SimpleHTTPRequestHandler的方法如下:
- do_HEAD()
这个方法会处理‘HEAD’请求。它发送的头信息与“GET”请求一样,在do_GET()可以看到更多的头信息相关内容 - do_GET()
请求会被映射为本地目录。它会将请求解析为表示当前工作目录的地址。
当请求映射到某个地址时,会在这个地址寻找“index.html”或“index.htm”文件。如果找到了,会返回找到的文件;如果没找到,会调用list_directory()方法列举当前目录(list_directory()方法使用os.listdir()方法扫描当前目录,如果listdir()失败,会返回错误码404)。
如果请求映射的是文件,那么会打开这个文件并返回。如果在打开过程中出现“OSError”,会返回“404, ‘File not found’”。如果打开文件成功,那么会通过guess_type()方法查找文件类型(通过extensions_map查找)。
返回的头信息包括:“Content-type:”表示文件类型,“Content-Length:”表示文件大小,“Last-Modified:”表示最后修改时间。
然后加一个空行表示头信息结束,之后提交到输出流。如果文件的MIME类型以“text/”开头,那么作为文本打开,否则作为二进制文件打开。
使用示例请看http.server的test()部分
下面的代码使用SimpleHTTPRequestHandler实现了一个非常简单的webserver:
import http.server
import socketserver
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
http.server也可以通过解释器执行:
python -m http.server 8000
默认情况下,server会绑定到所有接口。通过-b/--bind
可以指定绑定到哪个接口。例如,下列代码会让server绑定到localhost:
python -m http.server 8000 --bind 127.0.0.1
class http.server.CGIHTTPRequestHandler(request, client_address, server)
这个类支持文件输出和CGI接口输出。对于文件输出,它的文件结构与HTTP结构的映射方式,跟SimpleHTTPRequestHandler是完全一样的。
注意:CGIHTTPRequestHandler运行的CGI脚本不支持重定向(返回HTTP code 302),因为每次访问CGI脚本都会发送200,会重置状态码
然而,如果请求被认为是访问CGI脚本,那么就不会以文件的方式访问它,而是以CGI脚本的方式访问。仅支持基于目录的CGI脚本,其他的server配置会将特殊扩展视为某种CGI脚本。
如果请求链接到cgi_directories路径下,那么do_GET()和do_HEAD()将会被修改为CGI脚本的形式为output提供服务,而不是以文件的方式提供服务。
CGIHTTPRequestHandler提供以下数据成员:
- cgi_directories
默认为['/cgi-bin', '/htbin']
,表示以CGI脚本方式提供服务的目录
CGIHTTPRequestHandler提供以下方法:
- do_POST()
这个方法对应于“POST”请求,仅应用与CGI脚本。如果通过POST方式访问非CGI目录,那么会返回错误码501,“Can only POST to CGI scripts”
注意:出于安全性考虑,CGI脚本运行时会与用户UID绑定。CGI脚本如果发生错误会抛出错误码403。
CGIHTTPRequestHandler可以使用命令行启动:
python -m http.server --cgi 8000