一个简单的 Python Http 服务器

#!/usr/bin/env python
# encoding: utf-8
import BaseHTTPServer

def run(server_class=BaseHTTPServer.HTTPServer,
        handler_class=BaseHTTPServer.BaseHTTPRequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()

run()


这就是一个非常简单地 http 服务器了,将这段代码保存为 server.py,然后使用

python server.py


运行起来,在浏览器中输入:

http://localhost:8000


之后,你应该看到的是:

Error response
Error code 501.
Message: Unsupported method ('GET').
Error code explanation: 501 = Server does not support this operation.


这段错误的提示。

我们仔细得看一下这段提示,可以发现原来是服务器不支持 GET 方法。OK,我们直接输入网址默认使用的就是 GET,那既然提示的是 501,那么就说明这个 URL 是有效地,但是方法不被支持。

秉着求知的态度我们来看一下 HTTPServer 的文档。

打开文档网址:

    https://docs.python.org/2/library/basehttpserver.html


我们通过阅读文档可以发现:

HttpServer 本身不处理请求,它仅用于接收请求,真正的处理逻辑是由 HttpRequestHandler 进行的。但事实上,HttpRequestHandler 是不能响应任何 http 请求的,必须使用继承子类的方式去实现对应 http 请求方法的响应(GET/POST)。HttpRequestHandler 提供了大量类变量,实例变量以及方法给子类使用。


okay,既然如此,那我们就来看看 HttpRequestHandler 有哪些方法是可以给子类使用的。

扫了一遍文档,我发现了这两个函数,他的表述是这样的:

handle()
    调用一次 handle_one_request() 去处理进来的 http 请求(如果是持久连接的话,将会调用多次),你绝对不要试图重写这个函数,而是应该实现各种 do_* 方法。

handle_one_request()
    这个函数将会解析并且将请求分发给对应的 do_* 方法,你真的不需要重写这个函数。


好,很好,非常好,感觉有点头绪了,那文档都这样说了,我们就看看人家的代码是怎么实现的,我本地的环境是 python 2.7.10,所以看得是 2.7.10 的源码:

首先是 handle 的源码:

def handle(self):
    """Handle multiple requests if necessary."""
    self.close_connection = 1

    self.handle_one_request()
    while not self.close_connection:
        self.handle_one_request()


很好,正如文档里面所说,真的就调用了 handle_one_request 函数,那咱就继续看 handle_one_request 函数的源码:

def handle_one_request(self):
    try:
        self.raw_requestline = self.rfile.readline(65537)
        if len(self.raw_requestline) > 65536:
            self.requestline = ''
            self.request_version = ''
            self.command = ''
            self.send_error(414)
            return
        if not self.raw_requestline:
            self.close_connection = 1
            return
        if not self.parse_request():
            # An error code has been sent, just exit
            return
        mname = 'do_' + self.command
        if not hasattr(self, mname):
            self.send_error(501, "Unsupported method (%r)" % self.command)
            return
        method = getattr(self, mname)
        method()
        self.wfile.flush() #actually send the response if not already done.
    except socket.timeout, e:
        #a read or a write timed out.  Discard this connection
        self.log_error("Request timed out: %r", e)
        self.close_connection = 1
        return


也和文档说的一致,解析参数,然后查找 do_* 方法,查找到之后调用了,然后就这样了。

在阅读完文档以及简单得跟踪了一下代码之后,我们就修改一下开头的服务器,使它能够返回一些内容,这里就假设为最简单的 "Hello Python"为例,可以改写成这样:

#!/usr/bin/env python
# encoding: utf-8
import BaseHTTPServer


class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
       self.send_response(200)
       self.send_header('Content-type', 'text/html')
       self.end_headers()
       self.wfile.write('Hello Python')


def run(server_class=BaseHTTPServer.HTTPServer,
      handler_class=MyHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()

run()


这下再启动起来,再浏览器上访问:

http://localhost:8000


你应该就可以看到 Hello Python 了。