python的SimpleHTTPServer do_GET和do_POST函数如何工作?

时间:2018-06-20 09:04:33

标签: python server simplehttpserver

出于学习目的,我创建了以下小型HTTP服务器:

import SimpleHTTPServer
import SocketServer

class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

    def do_GET(self):
        print(self.headers)
        SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)

    def do_POST(self):
        print(self.headers)
        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD':'POST',
                 'CONTENT_TYPE':self.headers['Content-Type'],

def main():
    port = 50738
    Handler = ServerHandler(1000)
    httpd = SocketServer.TCPServer(("192.168.X.Y", port), Handler)

    print "serving at port", port
    httpd.serve_forever()


if __name__ == "__main__":
    main()

我的假设如下:

  • 我的类“ ServerHandler”通过两个函数go_GET和do_POST扩展了SimpleHTTPServer.SimpleHTTPRequestHandler类。
  • main()函数创建一个绑定到我的I.P的服务器处理程序对象和服务器套接字。选择的地址和端口,并调用无限期服务/监听的功能。

放在一边:通过查看Python DOC https://docs.python.org/2/library/simplehttpserver.html,我知道SimpleHTTPServer.SimpleHTTPRequestHandler有一个名为do_GET的方法,我认为它会被ServerHandler类中的do_GET覆盖吗?

问题: 与do_GET和do_POST有关的幕后情况是什么?是否是这种情况,一旦我们让该服务器侦听指向特定IP:PORT的HTTP“活动”,它就会自动知道传入的信号是GET还是POST,并且一旦遇到该信号,服务器就会调用我的do_GET或do_POST功能?

1 个答案:

答案 0 :(得分:1)

呼叫SocketServer.TCPServer时,您将Handler类分配为接收传入请求的类。

SimpleHTTPServer模块为您提供的所有帮助都提供了基本的HTTP功能,但是您可以自己编写所有这些功能。

因此,正如您所说,在定义Handler时,您将从SimpleHTTPRequestHandler类继承所有方法,但随后覆盖了两个预定义方法:do_GETdo_POST。您还可以覆盖该类中的其他任何方法。

但是,如果不是do_*中定义的handle方法的话,这些SimpleHTTPRequestHandler方法就永远不会被调用,因为正是这个函数由socketserver模块调用

因此,如果您仅继承socketserver.BaseRequestHandler,则将失去所有功能,因为此类的handle()方法不会执行任何操作:

  

class socketserver.BaseRequestHandler

     

...

     

handle()

     
    

此功能必须完成维修保养所需的所有工作。     请求。默认实现不执行任何操作。几个实例     属性可用;该请求可作为     自我要求客户地址为self.client_address;和     服务器实例作为self.server,以防需要访问每个服务器     信息。

  
     

...

因此,通过从SimpleHTTPRequestHandler模块导入SimpleHTTPServer,您将立即获得HTTP服务器的基本功能。

所有这些功能都已记录在here中,并且在其handle方法上有重要的一点:

  

class http.server.BaseHTTPRequestHandler(request, client_address, server)

     

...

     

handle()

     
    

调用一次handle_one_request()(或者,如果持久的话)     多次启用连接)以处理传入的HTTP     要求。您永远不需要覆盖它;相反,实施     适当的do _ *()方法。

  
     

handle_one_request()

     
    

此方法将解析并调度请求     到适当的do _ *()方法。您永远不需要覆盖     它。

  
     

...

因此,最后,在您将socketserver.TCPServer传递给任何类的handle()方法的方式进行了细分之后,我们看到了SimpleHTTPRequestHandler如何实现此目的将请求传递到适当的do_GETdo_POST或其他取决于请求标头的方法上。


如果您想了解如何自己实现此功能,请查看/usr/lib/pythonX.Y/http/server.pyGitHub上的源代码。

我们可以在那里看到SimpleHTTPServer继承了BaseHTTPServer的内容,这是定义handle()handle_one_request()方法的地方:

因此,正如文档所述,handle只是将请求传递到handle_one_request直到连接关闭:

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

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

handle_one_request是调用do_*方法的地方:

def handle_one_request(self):
    """Handle a single HTTP request.
    You normally don't need to override this method; see the class
    __doc__ string for information on how to handle specific HTTP
    commands such as GET and POST.
    """
    try:
        self.raw_requestline = self.rfile.readline(65537)
        if len(self.raw_requestline) > 65536:
            self.requestline = ''
            self.request_version = ''
            self.command = ''
            self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
            return
        if not self.raw_requestline:
            self.close_connection = True
            return
        if not self.parse_request():
            # An error code has been sent, just exit
            return
        mname = 'do_' + self.command   ## the name of the method is created
        if not hasattr(self, mname):   ## checking that we have that method defined
            self.send_error(
                HTTPStatus.NOT_IMPLEMENTED,
                "Unsupported method (%r)" % self.command)
            return
        method = getattr(self, mname)  ## getting that method
        method()                       ## finally calling it
        self.wfile.flush() #actually send the response if not already done.
    except socket.timeout as e:
        #a read or a write timed out.  Discard this connection
        self.log_error("Request timed out: %r", e)
        self.close_connection = True
        return

(请注意,我将自己的评论加了双阴影线(##,以将其与原始作者的评论区分开来)