Search code examples
pythonhttpwerkzeug

Minimal HTTP server with Werkzeug - Internal Server Error


To demonstrate basics HTTP handling, I'm in the process of trying to define a really minimal HTTP server demonstration. I have been using the excellent werkzeug library that I'm trying to "dumb" down a bit more. My current server does too much :)

#!/usr/bin/env python2.7
# encoding: utf-8

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('127.0.0.1', 6969, application=None)

run_simple is handling already too many things. When making a request for this server,

→ http GET http://127.0.0.1:6969/

we get:

HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Type: text/html
Content-Length: 291
Server: Werkzeug/0.8.3 Python/2.7.1
Date: Tue, 08 Jan 2013 07:45:46 GMT

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was 
unable to complete your request.  Either the server 
is overloaded or there is an error in the application.</p>

I would love to reduce it to the bare minimum. And use the 500 Internal Server Error as a catch all. Ideally the Response from the server should be 500 for any HTTP requests and just that, because the server doesn't know anything about the requests

HTTP/1.0 500 INTERNAL SERVER ERROR

Then in a second stage I will probably add

HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Type: text/plain

Internal Server Error

Then start to handle requests by understanding them. The goal is to be educative in the process. Any suggestions for taking over the default answers is welcome.

Update 001

with:

#!/usr/bin/env python2.7
# encoding: utf-8

from werkzeug.wrappers import BaseResponse as Response


def application(environ, start_response):
    response = Response('Internal Server Error', status=500)
    return response(environ, start_response)

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('127.0.0.1', 6969, application)

It will return

HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Type: text/plain; charset=utf-8
Content-Length: 21
Server: Werkzeug/0.8.3 Python/2.7.1
Date: Tue, 08 Jan 2013 07:55:10 GMT

Internal Server Error

I want to remove at least the server and the date which are optional.


Solution

  • As basic example, I wouldn't use a third-party library. You can use the BaseHTTPServer-module that comes with Python.

    import BaseHTTPServer
    
    PORT = 8000
    
    class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
        def send_response(self, code, message=None):
            """Send the response header and log the response code.
    
            In contrast to base class, do not send two standard headers 
            with the server software version and the current date.
            """
            self.log_request(code)
            if message is None:
                if code in self.responses:
                    message = self.responses[code][0]
                else:
                    message = ''
            if self.request_version != 'HTTP/0.9':
                self.wfile.write("%s %d %s\r\n" %
                                 (self.protocol_version, code, message))
    
        def do_GET(self):
            self.send_response(500)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write("Internal Server Error\n")
    
    
    httpd = BaseHTTPServer.HTTPServer(("", PORT), MyHandler)
    print "serving at port", PORT
    httpd.serve_forever()
    

    This will give us the following response:

    HTTP/1.0 500 Internal Server Error
    Content-type: text/html
    
    Internal Server Error
    

    The place where you can do all your changes now is in the do_GET-method. I think it is quite obvious what each line does.

    Alternative 1:

    Even more basic is

    import SocketServer
    
    response = """HTTP/1.0 500 Internal Server Error
    Content-type: text/html
    
    Invalid Server Error"""
    
    class MyTCPHandler(SocketServer.BaseRequestHandler):
        """
        The RequestHandler class for our server.
    
        It is instantiated once per connection to the server, and must
        override the handle() method to implement communication to the
        client.
        """
    
    
        def handle(self):
            # self.request is the TCP socket connected to the client
            self.data = self.request.recv(1024).strip()
            self.request.sendall(response)
    
    if __name__ == "__main__":
        HOST, PORT = "localhost", 8000
        server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
        server.serve_forever()