打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
python实现的http服务器
#!/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, statimport time, struct, re, tracebackimport pprint, ettoolsfrom collections import defaultdicthost = ''port = 12345timeout = 15DOCUMENT_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 usemm = {}for t in mimes.keys():    for ext in mimes[t].split():        mm[ext] = tmimes = 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[i].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 <= status < 1000:                # good                conn.http_status = status    else:        separtor = re.search(r'\r?\n\r?\n', cgi_response)        header = cgi_response[:separtor.start()]        headers = defaultdict(list)        for line in header.splitlines():            key, value = line.split(': ')            headers[key].append(value)        print 'cgi_response_parse', '|' * 100        pprint.pprint(headers)        value = headers.get('Status')        if value:            value = value[0]            conn.http_status = int(value[:3])            conn.http_msg = value[4:]            del headers['Status']        value = headers.get('Connection', [''])[0]        conn.keepalive = True if value.lower() == 'keep-alive' else False         response = Response()        response.response_type = Response.RESPONSE_FROM_MEM        response.keepalive = conn.keepalive        response.headers = headers        response.response = cgi_response[separtor.end():]        conn.response = responsedef handle_cgi(conn):    print 'handle_cgi'    from_cgi_read_end, from_cgi_write_end = os.pipe()    try:        to_cgi_read_end, to_cgi_write_end = os.pipe()    except:        os.close(from_cgi_read_end)        os.close(from_cgi_write_end)    try:        pid = os.fork()    except:        return -1    if pid == 0: # child        filename = os.path.normpath(DOCUMENT_ROOT + conn.request.uri)        print 'CC' * 50        print filename        cgienv = {'SERVER_SOFTWARE': 'ethttpd-py',                  'SERVER_NAME': 'localhost',                  'SERVER_PORT': str(port),                  'GATEWAY_INTERFACE': 'CGI/1.1',                  'SERVER_PROTOCOL': 'HTTP/1.1',}        cgienv['REQUEST_METHOD'] = conn.request.http_method        # SCRIPT_FILENAME is VERY important.        cgienv['SCRIPT_FILENAME'] = filename        cgienv['SCRIPT_NAME'] = os.path.basename(filename)        cgienv['REMOTE_ADDR'] = conn.remote_ip        cgienv['DOCUMENT_ROOT'] = DOCUMENT_ROOT        cgienv['REDIRECT_STATUS'] = '200'        cgienv['REQUEST_URI'] = conn.request.orig_uri        cgienv['HTTP_HOST'] = conn.request.headers['Host'][0]        cgienv['HTTP_CONNECTION'] = 'Keep-Alive' if conn.keepalive else 'close'        attrs = conn.request.headers        if conn.request.query_string:            cgienv['QUERY_STRING'] = conn.request.query_string        if attrs.get('Content-Length'):            cgienv['CONTENT_LENGTH'] = attrs['Content-Length'][0]        if attrs.get('Content-Type'):            cgienv['CONTENT_TYPE'] = attrs['Content-Type'][0]        if attrs.get('Referer'):            cgienv['HTTP_REFERER'] = attrs['Referer'][0]        if attrs.get('Cookie'):            cgienv['HTTP_COOKIE'] = attrs['Cookie'][0]        # pprint.pprint(vars(conn))        pprint.pprint(cgienv)        # move stdout to from_cgi_write_end        os.close(sys.stdout.fileno())        os.dup2(from_cgi_write_end, sys.stdout.fileno())        os.close(from_cgi_write_end)        # not needed        os.close(from_cgi_read_end)        # move stdin to to_cgi_read_end        os.close(sys.stdin.fileno())        os.dup2(to_cgi_read_end, sys.stdin.fileno())        os.close(to_cgi_read_end)        # not needed        os.close(to_cgi_write_end)        if filename.endswith('.php'):            os.execve('/usr/bin/php5-cgi', ['php5-cgi', filename], cgienv)        else:            os.execve(filename, (filename, ), cgienv)        os.abort()    else: # parent        try:            os.close(to_cgi_read_end)            os.close(from_cgi_write_end)            if conn.request.http_method == 'POST':               # print 'post ' * 50                # print conn.body               # print '#' * 30               os.write(to_cgi_write_end, conn.request.body)            response = ''            isfirst = True            while True:                data = os.read(from_cgi_read_end, 4096)                # print '+-' * 40                # print data                # print '+=' * 40                if not data:                    print 'return not data'                    break                response += data            os.close(to_cgi_write_end)            os.close(from_cgi_read_end)            print '_+' * 20        except:            traceback.print_exc()        # print '[response]' * 5        # print response        cgi_response_parse(conn, response)    return 0 def make_direct_reply(conn, http_status, msg, html):    response = Response()    response.response_type = Response.RESPONSE_FROM_MEM    response.response = html    response.headers['Content-Type'].append('text/html')    response.headers['Content-Length'].append(str(len(html)))    response.content_length = len(html)    conn.http_status = http_status    conn.response = response def handle_request(conn):    filename = os.path.normpath(DOCUMENT_ROOT + conn.request.uri)    print 'AB' * 50    print filename    # check whether the file exists    if not os.path.isfile(filename):        # try to add normal file to the end of uri        for name in default_files:            test_name = os.path.join(filename, name)            if os.path.isfile(test_name):                conn.request.uri = os.path.join(conn.request.uri, name)                filename = test_name                break        else:            make_direct_reply(conn, 404, 'Not Found',                              '404 Not Found You Wanted')            return    ext = os.path.splitext(filename)[1]    ext = ext[1:] if ext.startswith('.') else ext    # check if there's special handler for this file    # ......     if ext in handlers.keys():        handlers[ext](conn)        return    # ok, it's a normal static file    # privilege    try:        f = open(filename, 'rb')    except IOError, e:        make_direct_reply(conn, 403, 'Forbidden',                          'Permision Denied')        return    file_status = os.stat(filename)     file_size = file_status[stat.ST_SIZE]    modified_date = file_status[stat.ST_MTIME]    # static file    conn.http_status = 200     response = Response()    response.response_type = Response.RESPONSE_FROM_FILE    response.response_fd = f    response.content_length = file_size    response.headers['Content-Type'].append(get_mime(ext))    response.headers['Content-Length'].append(str(file_size))    conn.response = responsedef read_request(conn):    data = conn.sockfd.recv(4096)    header, header_end_pos = get_header(data)    request = Request(header)    if request.http_method == 'POST':        weWant = int(request.content_length)        weHad = len(data) - header_end_pos        print 'weWant', weWant        print 'weHad', weHad        to_read = weWant - weHad        body = data[header_end_pos:]         if to_read > 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 Falsedef 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 = -1def 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()
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
python 登陆网站(转)
云计算学习路线教程大纲课件:关于HTTP Server
安恒X计划12月月赛
python爬虫-使用cookie登录
Python获取直播主播照片, 实现颜值检测, 进行排名~
基于C#动手实现网络服务器Web Server
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服