http://pymotw.com/2/BaseHTTPServer/
中文
https://pymotwcn.readthedocs.org/en/latest/documents/BaseHTTPServer.html
BaseHTTPServer – base classes for implementing web servers¶
Purpose: | BaseHTTPServer includes classes that can form the basis of a web server. |
---|---|
Available In: | 1.4 and later |
BaseHTTPServer uses classes from SocketServer to create base classes for making HTTP servers. HTTPServer can be used directly, but the BaseHTTPRequestHandler is intended to be extended to handle each protocol method (GET, POST, etc.).
HTTP GET¶
To add support for an HTTP method in your request handler class, implement the methoddo_METHOD(), replacingMETHOD with the name of the HTTP method. For example, do_GET(),do_POST(), etc. For consistency, the method takes no arguments. All of the parameters for the request are parsed byBaseHTTPRequestHandler and stored as instance attributes of the request instance.
This example request handler illustrates how to return a response to the client and some of the local attributes which can be useful in building the response:
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, use <Ctrl-C> to stop'
server.serve_forever()
The message text is assembled and then written towfile, the file handle wrapping the response socket.Each response needs a response code, set via send_response(). If an error code is used (404, 501, etc.), an appropriate default error message is included in the header, or a message can be passed with the error code.
To run the request handler in a server, pass it to the constructor of HTTPServer, as in the__main__ processing portion of the sample script.
Then start the server:
$ python BaseHTTPServer_GET.py
Starting server, use <Ctrl-C> to stop
In a separate terminal, use curl to access it:
$ curl -i http://localhost:8080/?foo=barHTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.5.1
Date: Sun, 09 Dec 2007 16:00:34 GMT
CLIENT VALUES:
client_address=('127.0.0.1', 51275) (localhost)
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.5.1
protocol_version=HTTP/1.0
HTTP POST ¶
基类不会自动解析表单数据,需要借助cgi模块的FieldStorage类。
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(('0.0.0.0', 8080), PostHandler)
print 'Starting server, use <Ctrl-C> to stop'
server.serve_forever()
#python BaseHTTPServer_POST.py
Starting server, use <Ctrl-C> to stop
curl的-F可以包含表单数据以POST方式发送。参数-F datafile=@BaseHTTPServer_GET.py提交文件BaseHTTPServer_GET.py的内容。
#curl http://localhost:8080/ -F name=dhellmann -F foo=bar -F datafile=@BaseHTTPServer_GET.py
Client: ('127.0.0.1', 27019)
User-agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Path: /
Form data:
Uploaded datafile as "BaseHTTPServer_GET.py" (2514 bytes)
foo=bar
name=dhellmann
线程和进程 ¶
HTTPServer是SocketServer.TCPServer的简单子类,不使用多线程或多进程来处理请求。可以使用SocketServer中的mix-in来支持多线程或多进程。
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, use <Ctrl-C> to stop'
server.serve_forever()
#python BaseHTTPServer_threads.py
Starting server, use <Ctrl-C> to stop
每次请求过来的时候, 会被创建新的线程或进程:
#curl http://localhost:8080/
Thread-1
#curl http://localhost:8080/
Thread-2
#curl http://localhost:8080/
Thread-3
#curl http://localhost:8080/
Thread-4
如果把上面的ThreadingMixIn换成ForkingMixIn, 也可以获得类似的结果, 但是后者是使用进程而不是线程。
错误处理 ¶
错误处理使用send_error()方法,传递错误码及可选的错误信息后, 整个响应对象(包括头, 状态码, 消息体)都会自动生成。
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, use <Ctrl-C> to stop'
server.serve_forever()
#python BaseHTTPServer_errors.py
Starting server, use <Ctrl-C> to stop
客户端收到HTML的错误信息,头部也包含了错误码。
#curl -i http://localhost:8080/
HTTP/1.0 404 Not Found
Server: BaseHTTP/0.3 Python/2.7.3
Date: Sun, 17 Aug 2014 14:29:19 GMT
Content-Type: text/html
Connection: close
<head>
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code 404.
<p>Message: Not Found.
<p>Error code explanation: 404 = Nothing matches the given URI.
</body>
设置消息头 ¶
send_header设置消息头,参数头名和值。
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, use <Ctrl-C> to stop'
server.serve_forever()
#python BaseHTTPServer_send_header.py
Starting server, use <Ctrl-C> to stop
这里参照RFC 2822设置了Last-Modified为当前时间戳。
#curl -i http://localhost:8080/
HTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.7.3
Date: Sun, 17 Aug 2014 14:34:20 GMT
Last-Modified: Sun, 17 Aug 2014 14:34:20 GMT
Response body