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.
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()