python-3.xhttpbasehttprequesthandler

Add a new instance variable to subclass of http.server.BaseHTTPRequestHandler


I want to be able to add an instance variable to my subclass of http.server.BaseHTTPRequestHandler.

Here's my code:

from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib


class Server(BaseHTTPRequestHandler):

    def __init__(self, request, client_addr, server):
        super().__init__(request, client_addr, server)
        self.pathobj = urllib.parse.urlparse(self.path)

    def do_HEAD(self):
        self.send_response(200)

    def do_GET(self):
        print(self.pathobj)
        self.send_response(200)
        self.end_headers()

    def do_POST(self):
        print(self.pathobj)
        self.send_response(405)
        self.end_headers()   

def run(server_class=HTTPServer, handler_class=Server, port=8080):
    server_address = ("", port)
    httpd = server_class(server_address, handler_class)

    print("Starting httpd on port {}...".format(port))

    httpd.serve_forever()

if __name__ == "__main__":
    run()

I want to be able to access the ParseResult object returned by urllib.parse.urlparse in each class method without needing to rewrite self.pathobj = urllib.parse.urlparse(self.path) at the beginning of every class method.

The above code does not work -- when do_GET or do_POST are called it complains that 'Server' object has no attribute 'pathobj'.

The above docs for http.server.BaseHTTPRequestHandler say:

All of the relevant information is stored in instance variables of the handler. Subclasses should not need to override or extend the __init__() method.

But I don't see another way to do this. Is it possible?


Solution

  • The docs say

    The handler will parse the request and the headers, then call a method specific to the request type.

    As it turns out, http.server.BaseHTTPRequestHandler ultimately inherits from socketserver.BaseRequestHandler, and socketserver.BaseRequestHandler.__init__() (defined here) calls do_GET(). So the issue is that the instance variable is actually being set after do_GET() has already been called.

    So in order to make this work, you'll need to move the self.pathobj line above the super() line and then rewrite it to do the parsing that BaseHTTPRequestHandler does to construct self.path.