Table Of Contents

Previous topic

PyMOTW: zipfile

Next topic

PyMOTW: pprint

This Page

PyMOTW: BaseHTTPServer

  • 模块: BaseHTTPServer
  • 目的: 提供实现web服务的基础类.
  • python版本: 1.4+

BaseHTTPServer模块包括了能够构成基本的web服务器的类.

描述

BaseHTTPServer使用 SocketServer 中的类来创建HTTP服务基本类, HTTPServer可以被直接使用, 但是其中的BaseHTTPRequestHandler需要被扩展去处理各种协议方法(如GET, POST等).

简单的GET请求例子

实现 do_METHOD() 方法可以让你的请求类支持HTTP方法, 这里的METHOD用具体的HTTP方法名字代替. 例如, do_GET(), do_POST() 等. 为了保持一致, 这些方法不含参数. 用于请求的所有的参数被BaseHTTPRequestHandler解析, 然后被存储在一个实例属性集合中, 这样你可以很方便地检索它们.

下面的例子中, 请求处理对象说明了如何返回一个响应对象给客户端, 一些本地属性在构造响应对象中很有用的.

from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse

class GetHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        parsed_path = urlparse.urlparse(self.path)
        message = '\n'.join([
          '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,
          '',
        ])
        self.send_response(200)
        self.end_headers()

        self.wfile.write(message)

        return

消息正文被组装好后就写入到self.wfile中, 这个文件处理对象封装了整个响应socket. 每个响应对象需要一个响应代码, 它可以通过self.send_response()来设置. 如果使用了一个错误代码(如404, 501等), 那么对应的默认错误信息会被包含在消息头中, 或者, 你能使用错误码来传递信息.

在服务器中运行请求处理对象, 将这个请求处理对象传递给HTTPServer构造器.

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_GET.py
Starting server, use <Ctrl-C> to stop

在另外一终端中, 使用curl访问:

$ 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

线程和进程

HTTPServer是SocketServer.TCPServer的一个简单子类. 它不使用多线程或多进程来处理请求. 要增加多线程和多进程, 可以使用SocketServer中的合适的混用类来创建一个新的类.

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() ## 这里threading就可以自己处理
        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()

每次当一个请求过来的时候, 一个新的线程或进程会被创建来处理它:

$ curl http://localhost:8080/
Thread-1
$ curl http://localhost:8080/
Thread-2
$ curl http://localhost:8080/
Thread-3

如果把上面的ThreadingMixIn换成ForkingMixIn, 也可以获得类似的结果, 但是后者是使用了独立的进程而不是线程.

POST

支持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('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 (%d bytes)\n' % (field, 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, use <Ctrl-C> to stop'
    server.serve_forever()

再次使用curl, 我们可以包含表单数据, 它自动以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', 51128)
Path: /
Form data:
         name=dhellmann
         foo=bar
         Uploaded datafile (2222 bytes)

Errors

错误处理可以使用 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()

在这种情况下, 一直返回404错误.

$ curl -i http://localhost:8080/
HTTP/1.0 404 Not Found
Server: BaseHTTP/0.3 Python/2.5.1
Date: Sun, 09 Dec 2007 15:49:44 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>