BaseHTTPServer类是在SocketServer的基础上创建出的一个简单的HTTP servers应用类,而通过BaseHTTPRequestHandler方法我们可以直接实现GET、POST等请求。由于其只是一个简单的SocketServer.TCPServer子类,它本身并不支持多线程或多进程,如果想使用多线程或多进程,需要结合threading模块去fork,这在后面也会提到。
一、HTTP GET请求
代码如下:
from BaseHTTPServer import BaseHTTPRequestHandler import urlparse class GetHandler(BaseHTTPRequestHandler): def do_GET(self): parsed_path = urlparse.urlparse(self.path) message_parts = [ 'CLIENT VALUES:', 'client_address=%s (%s)' % (self.client_address, self.address_string()), 'command=%s' % self.command, 'path=%s' % self.path, 'real path=%s' % parsed_path.path, 'query=%s' % parsed_path.query, 'request_version=%s' % self.request_version, '', 'SERVER VALUES:', 'server_version=%s' % self.server_version, 'sys_version=%s' % self.sys_version, 'protocol_version=%s' % self.protocol_version, '', 'HEADERS RECEIVED:', ] for name, value in sorted(self.headers.items()): message_parts.append('%s=%s' % (name, value.rstrip())) message_parts.append('') message = '\r\n'.join(message_parts) self.send_response(200) self.end_headers() self.wfile.write(message) return if __name__ == '__main__': from BaseHTTPServer import HTTPServer server = HTTPServer(('localhost', 8080), GetHandler) print 'Starting server, useto stop' server.serve_forever()
其中wfile方法用于向客户端返回信息,send_response返回对应的http代码。上面的监听地址可以跟据自已的需求进行修改。当我们运行以上代码时,客户端可以通过浏览器或CURL命令返回对应的结果。这里以CURL命令执行。其返回结果如下:
<br />
$ curl -i http://localhost:8080/?foo=bar HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.15 Date: Thu, 30 Nov 2017 15:21:05 GMT CLIENT VALUES: client_address=('127.0.0.1', 54886) (localhost.localdomain) command=GET path=/?foo=bar real path=/ query=foo=bar request_version=HTTP/1.1 SERVER VALUES: server_version=BaseHTTP/0.3 sys_version=Python/2.7.15 protocol_version=HTTP/1.0 HEADERS RECEIVED: accept=*/* host=localhost:8080 user-agent=curl/7.59.0
二、HTTP POST请求
BaseHTTPServer模块有提供<a href="https://docs.python.org/2/library/basehttpserver.html" target="_blank" rel="noopener">rfile方法</a>读取POST数据,但功能较弱,对于文件上传类的操作,需要借助CGI模块处理。先看下rfile方法,读取POST数据的操作如下:
<br />
datas = self.rfile.read(int(self.headers['content-length'])) datas = urllib.unquote(datas).decode("utf-8", 'ignore')
再看个复杂的,基于cgi模块的:
<br />
from BaseHTTPServer import BaseHTTPRequestHandler import cgi class PostHandler(BaseHTTPRequestHandler): def do_POST(self): # Parse the form data posted form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], }) # Begin the response self.send_response(200) self.end_headers() self.wfile.write('Client: %s\n' % str(self.client_address)) self.wfile.write('User-agent: %s\n' % str(self.headers['user-agent'])) self.wfile.write('Path: %s\n' % self.path) self.wfile.write('Form data:\n') # Echo back information about what was posted in the form for field in form.keys(): field_item = form[field] if field_item.filename: # The field contains an uploaded file file_data = field_item.file.read() file_len = len(file_data) del file_data self.wfile.write('\tUploaded %s as "%s" (%d bytes)\n' % \ (field, field_item.filename, file_len)) else: # Regular form value self.wfile.write('\t%s=%s\n' % (field, form[field].value)) return if __name__ == '__main__': from BaseHTTPServer import HTTPServer server = HTTPServer(('localhost', 8080), PostHandler) print 'Starting server, useto stop' server.serve_forever()
按如下命令请求后返回的结果如下:
<br />
# curl http://localhost:8080/ -F name=www.361way.com -F foo=bar -F datafile=@test.py Client: ('127.0.0.1', 55418) User-agent: curl/7.59.0 Path: / Form data: Uploaded datafile as "test.py" (1442 bytes) foo=bar name=www.361way.com
不过这个示例也是非常简单的只是读取了上传文件的大小,直接就进行了数据的del,并未将上传的文件进行保存。
<br />
三、多线程的使用
这里使用了SocketServer里的ThreadingMixIn方法和threading模块进行的实现。具体代码如下:
<br />
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn import threading class Handler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() message = threading.currentThread().getName() self.wfile.write(message) self.wfile.write('\n') return class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): """Handle requests in a separate thread.""" if __name__ == '__main__': server = ThreadedHTTPServer(('localhost', 8080), Handler) print 'Starting server, useto stop' server.serve_forever()
如下每执行一次会打印一次线程信息:
<br />
$ curl http://localhost:8080/ Thread-1 $ curl http://localhost:8080/ Thread-2 $ curl http://localhost:8080/ Thread-3
四、错误响应
错误响应使用的send_error方法,具体示例如下:
<br />
from BaseHTTPServer import BaseHTTPRequestHandler class ErrorHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_error(404) return if __name__ == '__main__': from BaseHTTPServer import HTTPServer server = HTTPServer(('localhost', 8080), ErrorHandler) print 'Starting server, useto stop' server.serve_forever()
这里不再写输出结果,因为直接返回的404。
五、设置响应头
通过send_header方法,可以增加响应头内容。这里假设要发送一个Last-Modified的响应头,其内容是当前的时间戳,操作方法如下:
<br />
from BaseHTTPServer import BaseHTTPRequestHandler import urlparse import time class GetHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Last-Modified', self.date_time_string(time.time())) self.end_headers() self.wfile.write('Response body\n') return if __name__ == '__main__': from BaseHTTPServer import HTTPServer server = HTTPServer(('localhost', 8080), GetHandler) print 'Starting server, useto stop' server.serve_forever()
本篇内容和代码主要基于pymotw翻译学习总结而来。
<br />