|
| 1 | +# |
| 2 | +# ws30 -- the thirty minute web server |
| 3 | +# author: Wilhelm Fitzpatrick (rafial@well.com) |
| 4 | +# date: August 3rd, 2002 |
| 5 | +# |
| 6 | +# Written after attending a Dave Thomas talk at PNSS and hearing about |
| 7 | +# his "write a web server in Ruby in one hour" challenge. |
| 8 | +# |
| 9 | +# Actual time spent: |
| 10 | +# 30 minutes reading socket man page |
| 11 | +# 30 minutes coding to first page fetched |
| 12 | +# 3 hours making it prettier & more pythonic |
| 13 | +# |
| 14 | +# updated by Brian Dorsey |
| 15 | +# |
| 16 | + |
| 17 | +import os |
| 18 | +import socket |
| 19 | + |
| 20 | +HOST = "" |
| 21 | +PORT = 8080 |
| 22 | +MIME_TYPES = {'.jpg': 'image/jpg', |
| 23 | + '.gif': 'image/gif', |
| 24 | + '.png': 'image/png', |
| 25 | + '.html': 'text/html', |
| 26 | + '.pdf': 'application/pdf'} |
| 27 | + |
| 28 | + |
| 29 | +RESPONSE_HEADERS = {} |
| 30 | + |
| 31 | +RESPONSE_HEADERS[200] =\ |
| 32 | +"""HTTP/1.0 200 Okay |
| 33 | +Server: ws30 |
| 34 | +Content-type: %s |
| 35 | +
|
| 36 | +%s |
| 37 | +""" |
| 38 | + |
| 39 | +RESPONSE_HEADERS[301] =\ |
| 40 | +"""HTTP/1.0 301 Moved |
| 41 | +Server: ws30 |
| 42 | +Content-type: text/plain |
| 43 | +Location: %s |
| 44 | +
|
| 45 | +moved |
| 46 | +""" |
| 47 | + |
| 48 | +RESPONSE_HEADERS[404] =\ |
| 49 | +"""HTTP/1.0 404 Not Found |
| 50 | +Server: ws30 |
| 51 | +Content-type: text/plain |
| 52 | +
|
| 53 | +%s not found |
| 54 | +""" |
| 55 | + |
| 56 | +DIRECTORY_LISTING =\ |
| 57 | +"""<html> |
| 58 | +<head><title>%s</title></head> |
| 59 | +<body> |
| 60 | +<a href="%s..">..</a><br> |
| 61 | +%s |
| 62 | +</body> |
| 63 | +</html> |
| 64 | +""" |
| 65 | + |
| 66 | +DIRECTORY_LINE = '<a href="%s">%s</a><br>' |
| 67 | + |
| 68 | + |
| 69 | +def server_socket(host, port): |
| 70 | + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 71 | + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 72 | + s.bind((host, port)) |
| 73 | + s.listen(1) |
| 74 | + return s |
| 75 | + |
| 76 | + |
| 77 | +def parse_request(sock): |
| 78 | + data = sock.recv(4096) |
| 79 | + if not data: |
| 80 | + print "Bad request: no data" |
| 81 | + return '' |
| 82 | + line = data[0:data.find("\r")] |
| 83 | + print line |
| 84 | + method, uri, protocol = line.split() |
| 85 | + #headers = data[0:data.find("\r\n\r\n")] |
| 86 | + #print headers |
| 87 | + return uri |
| 88 | + |
| 89 | + |
| 90 | +def list_directory(uri): |
| 91 | + entries = os.listdir('.' + uri) |
| 92 | + entries.sort() |
| 93 | + return DIRECTORY_LISTING % (uri, uri, '\n'.join( |
| 94 | + [DIRECTORY_LINE % (e, e) for e in entries])) |
| 95 | + |
| 96 | + |
| 97 | +def get_file(path): |
| 98 | + f = open(path) |
| 99 | + try: |
| 100 | + return f.read() |
| 101 | + finally: |
| 102 | + f.close() |
| 103 | + |
| 104 | + |
| 105 | +def get_content(uri): |
| 106 | + try: |
| 107 | + path = '.' + uri |
| 108 | + if os.path.isfile(path): |
| 109 | + return (200, get_mime(uri), get_file(path)) |
| 110 | + if os.path.isdir(path): |
| 111 | + if(uri.endswith('/')): |
| 112 | + return (200, 'text/html', list_directory(uri)) |
| 113 | + else: |
| 114 | + return (301, uri + '/') |
| 115 | + else: |
| 116 | + return (404, uri) |
| 117 | + except IOError, e: |
| 118 | + return (404, e) |
| 119 | + |
| 120 | + |
| 121 | +def get_mime(uri): |
| 122 | + return MIME_TYPES.get(os.path.splitext(uri)[1], 'text/plain') |
| 123 | + |
| 124 | + |
| 125 | +def send_response(sock, content): |
| 126 | + template = RESPONSE_HEADERS[content[0]] |
| 127 | + data = template % content[1:] |
| 128 | + sock.sendall(data) |
| 129 | + |
| 130 | + |
| 131 | +if __name__ == '__main__': |
| 132 | + server = server_socket(HOST, int(PORT)) |
| 133 | + print 'starting %s on %s...' % (HOST, PORT) |
| 134 | + try: |
| 135 | + while True: |
| 136 | + sock, client_address = server.accept() |
| 137 | + uri = parse_request(sock) |
| 138 | + if uri: |
| 139 | + content = get_content(uri) |
| 140 | + send_response(sock, content) |
| 141 | + sock.close() |
| 142 | + except KeyboardInterrupt: |
| 143 | + print 'shutting down...' |
| 144 | + server.close() |
0 commit comments