|
#!/usr/bin/env python
# author: Hua Liang [ Stupid ET ]
# email: et@everet.org
# website: http://EverET.org
#
# Rule Of Optimization: Prototype before polishing. Get it
# working before you optimize it.
import socket, os, threading, sys, signal, stat
import time, struct, re, traceback
import pprint
from collections import defaultdict
host = 'localhost'
port = 8080
timeout = 15
DOCUMENT_ROOT = os.getcwd() + '/'
HTTP_PROTOCOL = 'HTTP/1.1'
cgiexts = ['cgi', 'php', 'sh', 'py']
mimes = {"application/ogg": " ogg",
"application/pdf": " pdf",
"application/xml": " xsl xml",
"application/xml-dtd": " dtd",
"application/xslt+xml": " xslt",
"application/zip": " zip",
"audio/mpeg": " mp2 mp3 mpga",
"image/gif": " gif",
"image/jpeg": " jpeg jpe jpg",
"image/png": " png",
"text/css": " css",
"text/html": " html htm",
"text/javascript": " js",
"text/plain": " txt asc",
"video/mpeg": " mpeg mpe mpg",
"video/quicktime": " qt mov",
"video/x-msvideo": " avi",}
# refine mimes for better use
mm = {}
for t in mimes.keys():
for ext in mimes[t].split():
mm[ext] = t
mimes = mm
default_files = set([
'index.html',
'index.php',
])
def handle_php(conn):
handle_cgi(conn)
handlers = {}
class Request(object):
def __init__(self, header):
self.request = ''
self.uri = ''
self.orig_uri = ''
self.http_method = ''
self.http_version = ''
self.request_line = ''
self.headers = defaultdict(list)
self.content_length = -1
self.body = ''
self.query_string = ''
self._parse(header)
def _parse(self, header):
lines = header.splitlines()
self.request_line = lines[0]
method, uri, protocol = self.request_line.split()
self.orig_uri = self.uri = uri
qpos = uri.find('?')
if qpos != -1:
self.query_string = uri[qpos + 1:]
self.uri = uri[:qpos]
self.http_method = method
self.http_version = protocol
for i in range(1, len(lines)):
key, value = lines.split(': ')
self.headers[key].append(value)
self.content_length = self.headers.get('Content-Length', [-1])[0]
class Response(object):
RESPONSE_FROM_FILE = 0
RESPONSE_FROM_MEM = 1
def __init__(self):
self.content_length = -1
self.keepalive = False
self.headers = defaultdict(list)
self.response_type = Response.RESPONSE_FROM_MEM
self.response = ''
self.response_fd = -1
class Connection(object):
def __init__(self, sockfd, remote_ip):
self.sockfd = sockfd
self.remote_ip = remote_ip
self.keepalive = False
self.reset()
def reset(self):
self.state = None
self.keepalive = False
self.http_status = -1
self.request = None
self.response = None
self.environment = {}
class ThreadRun(threading.Thread):
def __init__(self, conn):
threading.Thread.__init__(self)
self.conn = conn
def run(self):
handle_connection(self.conn)
self.conn.sockfd.close()
print '[', self.getName(), ']', 'ended'
class MultiThreadServer(object):
def __init__(self, host, port):
self.listenfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.listenfd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.listenfd.bind((host, port))
self.listenfd.listen(5)
def serve_forver(self):
while True:
clientfd, clientaddr = self.listenfd.accept()
# timeout for 5 seconds
clientfd.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
struct.pack('ll', timeout, 0))
# select, fork or multithread
conn = Connection(clientfd, clientaddr[0])
th = ThreadRun(conn)
th.start()
def get_header(buf):
'return header and end pos of header'
r = re.search(r'\r*\n\r*\n', buf)
header = buf[:r.start()]
return header, r.end()
####################
def get_mime(ext):
'Get mime type by extension, ignore case'
return mimes.get(ext.lower(), 'application/octet-stream')
def cgi_response_parse(conn, cgi_response):
print '=' * 50
# print cgi_response
if cgi_response.startswith('HTTP/1.'):
if (cgi_response[7] == '1' or cgi_response[7] == '0') and cgi_response[8] == ' ':
status = cgi_response[8:10]
status = int(status)
if 0 0:
print 'fuck' * 411
tail = conn.sockfd.recv(to_read)
body += tail
request.body = body
conn.request = request
conn.keepalive = True if \
request.headers.get('Connection', [''])[0].lower() == 'keep-alive' else False
def response_request(conn):
r = conn.response
print '[response_request]'
# pprint.pprint(vars(r))
# pprint.pprint(vars(conn))
status_line = '%s %d %s\r\n' % (
HTTP_PROTOCOL, conn.http_status, 'Fuck')
headers = r.headers
# headers = '\r\n'.join((': '.join((key, headers[key])) for key in headers))
header_text = ''
for key in headers:
for v in headers[key]:
header_text += ''.join((key, ': ', v, '\r\n'))
header_text += '\r\n'
print 'X' * 100
print header_text
conn.sockfd.send(status_line)
conn.sockfd.send(header_text)
# conn.sockfd.send('\r\n\r\n')
if r.response_type == Response.RESPONSE_FROM_MEM:
conn.sockfd.send(r.response)
elif r.response_type == Response.RESPONSE_FROM_FILE:
while True:
data = r.response_fd.read(8192)
if len(data) == 0: break
conn.sockfd.send(data)
r.response_fd.close()
r.response_fd = -1
def handle_connection(conn):
try:
while True:
conn.reset()
read_request(conn)
handle_request(conn)
if conn.keepalive:
conn.response.headers['Connection'].append('Keep-Alive')
conn.response.headers['Keep-Alive'].append('timeout=%d' % (timeout, ))
response_request(conn)
if not conn.keepalive:
break
except socket.error:
print '{socket.error connection die}'
except Exception, e:
traceback.print_exc()
if __name__ == '__main__':
handlers = {
'php': handle_php,
'py': handle_cgi,
}
server = MultiThreadServer(host, port)
server.serve_forver() |
|