Introduce old redir program
[lcapit-junk-code.git] / pet-projects / http / tiny-http
blob7c9266e71824ed7852c4a095ba2ea90819e316b0
1 #!/usr/bin/python
3 # A Tiny HTTP Server: a stupid project to learn about HTTP.
5 # Status: can retrieve a few things :)
7 # TODO:
9 # - Improve error handling
10 # - Generate good HTTP errors
11 # - Interpret client's headers
13 # Luiz Capitulino <lcapitulino@gmail.com>
15 from sys import argv,exit
16 import socket, os, time, mimetypes
18 class HttpRequestMethod:
19 HEAD = 1
20 GET = 2
21 POST = 3
22 PUT = 4
23 TRACE= 5
24 OPTIONS = 6
25 CONNECT = 7
26 DELETE = 8
28 class HttpResource:
29 def __get_length(self):
30 return os.path.getsize(self.path)
32 def __get_content(self):
33 f = open(self.path)
34 content = f.read()
35 f.close()
36 return content
38 def __get_content_type(self):
39 ctype = mimetypes.guess_type(self.path)[0]
40 if not ctype:
41 ctype = 'application/octet-stream'
42 return ctype
44 def __build_resource(self, path):
45 self.path = path
46 try:
47 self.length = self.__get_length()
48 except:
49 # XXX: all errors? really?
50 self.error = True
51 return
52 self.error = False
53 self.content = self.__get_content()
54 self.content_type = self.__get_content_type()
56 def __init__(self, root, abs_path):
57 path = root + '/' + abs_path
58 self.__build_resource(path)
60 class HttpResponse:
61 def __build_response(self, resource, method):
62 resp = ''
63 time_str = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime())
64 if resource.error:
65 # FIXME: There are number of possible errors, but we generate
66 # 404 only
67 resp += 'HTTP/1.1 404 Not Found\r\n'
68 else:
69 resp += 'HTTP/1.1 200 OK\r\n'
70 resp += 'Date: %s\r\n' % time_str
71 resp += 'Server: TinyHttp/0.1\r\n'
72 resp += 'Connection: close\r\n'
73 if not resource.error:
74 resp += 'Content-Length: %d\r\n' % resource.length
75 resp += 'Content-Type: %s\r\n' % resource.content_type
76 resp += '\r\n'
77 if method == HttpRequestMethod.GET:
78 resp += resource.content
79 self.response = resp
81 def __init__(self, resource, method):
82 self.__build_response(resource, method)
84 class HttpRequest:
85 def __get_method(self, type_str):
86 if type_str == 'HEAD':
87 return HttpRequestMethod.HEAD
88 elif type_str == 'GET':
89 return HttpRequestMethod.GET
90 elif type_str == 'POST':
91 return HttpRequestMethod.POST
92 elif type_str == 'PUT':
93 return HttpRequestMethod.PUT
94 elif type_str == 'TRACE':
95 return HttpRequestMethod.TRACE
96 elif type_str == 'OPTIONS':
97 return HttpRequestMethod.OPTIONS
98 elif type_str == 'CONNECT':
99 return HttpRequestMethod.CONNECT
100 elif type_str == 'DELETE':
101 return HttpRequestMethod.DELETE
103 def __get_version(self, ver_str):
104 if ver_str == 'HTTP/1.0':
105 return 10
106 elif ver_str == 'HTTP/1.1':
107 return 11
109 def __parse_request(self, buf):
110 in_request = True
111 (method, resource, version) = range(0, 3)
112 req_pos = method
113 headers_list = []
114 header_cur = ''
115 met_str = uri_str = ver_str = ''
116 for c in buf:
117 if c == '\r':
118 continue
119 if in_request:
120 if c == ' ':
121 req_pos += 1
122 continue
123 elif c == '\n':
124 in_request = False
125 continue
126 if req_pos == method:
127 met_str += c
128 elif req_pos == resource:
129 uri_str += c
130 elif req_pos == version:
131 ver_str += c
132 else:
133 if c == '\n':
134 if header_cur:
135 headers_list.append(header_cur)
136 header_cur = ''
137 continue
138 header_cur += c
139 self.headers = headers_list
140 self.uri = uri_str
141 self.method = self.__get_method(met_str)
142 self.version = self.__get_version(ver_str)
144 def __repr__(self):
145 s = '%d %s %d\n' % (self.method, self.uri, self.version)
146 for header in self.headers:
147 s += header + '\n'
148 return s
150 def __init__(self, buf):
151 self.__parse_request(buf)
153 class TinyHttp:
154 def __serve_request(self, request, csock):
155 assert request.method == HttpRequestMethod.GET or \
156 request.method == HttpRequestMethod.HEAD
157 resource = HttpResource(self.root, request.uri)
158 resp = HttpResponse(resource, request.method)
159 csock.sendall(resp.response)
160 print '-> served: ' + request.uri
162 def __read_request(self, csock):
163 buf = ''
164 while True:
165 buf += csock.recv(4096)
166 # XXX: Too lazy, supports only GET and HEAD
167 if buf.endswith('\r\n\r\n'):
168 return HttpRequest(buf)
170 def __create_sock(self, domain, port):
171 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
172 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
173 s.bind((domain, port))
174 s.listen(1)
175 return s
177 def main_loop(self):
178 while True:
179 (csock, addr) = self.sock.accept()
180 print '-> Connection from: %s:%d' % (addr[0],addr[1])
181 r = self.__read_request(csock)
182 assert r
183 self.__serve_request(r, csock)
184 csock.close()
185 print
187 def __init__(self, domain, port, root):
188 self.root = root
189 self.sock = self.__create_sock(domain, port)
191 def main():
192 if len(argv) != 4:
193 print 'tiny-http <domain> <port> <root>'
194 exit(1)
196 server = TinyHttp(argv[1], int(argv[2]), argv[3])
197 server.main_loop()
199 if __name__ == '__main__':
200 main()