web.py

来源:互联网 发布:淘宝数据包怎么用 编辑:程序博客网 时间:2024/05/05 11:06
import stacklessimport re, sys, copyfrom _weakref import proxyfrom string import Templatefrom cgi import parse_headerfrom sys import stdout, stderrfrom urllib import unquote_plusfrom traceback import print_excfrom time import gmtime, strftime, timefrom stackless import channel, getcurrent, schedule, taskletfrom select import poll as Poll, error as SelectError, /POLLIN, POLLPRI, POLLOUT, POLLERR, POLLHUP, POLLNVALfrom errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, /ENOTCONN, ESHUTDOWN, EINTR, EISCONN, errorcodefrom _socket import socket as Socket, error as SocketError, /AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR, SO_REUSEADDRfrom BaseHTTPServer import BaseHTTPRequestHandlerRESPONSES = dict((key, value[0]) for key, value in BaseHTTPRequestHandler.responses.items())del BaseHTTPRequestHandler, sys.modules['BaseHTTPServer']class SocketFile:fileno = lambda self: self.piddef __init__(self, sock, addr):self.socket = sockself.address = addrself.pid = sock.fileno()self._rbuf = self._wbuf = ''self.read_channel = channel()self.write_channel = channel()socket_map[self.pid] = proxy(self)def __del__(self):if hasattr(self, 'pid'):try: pollster.unregister(self.pid)except KeyError: passtry: del socket_map[self.pid]except KeyError: passself.socket.close()del self.piddef read(self, size=-1):if not hasattr(self, 'pid'):raise Disconnectread = self.read4raw(size).nextdata = read()if data is None:self.handle_read = readpollster.register(self.pid, RE)return self.read_channel.receive()return datadef readline(self, size=-1):if not hasattr(self, 'pid'):raise Disconnectread = self.read4line(size).nextdata = read()if data is None:self.handle_read = readpollster.register(self.pid, RE)return self.read_channel.receive()return datadef write(self, data):if not hasattr(self, 'pid'):raise Disconnectself._wbuf        = dataself.tasklet      = getcurrent()self.handle_write = self.write4raw().nextpollster.register(self.pid, WE)return self.write_channel.receive()def close(self):if hasattr(self, 'pid'):try: pollster.unregister(self.pid)except KeyError: passtry: del socket_map[self.pid]except KeyError: passself.socket.close()del self.piddef shutdown(self):if hasattr(self, 'pid'):try: pollster.unregister(self.pid)except KeyError: passtry: del socket_map[self.pid]except KeyError: passself.socket.close()del self.piddef read4raw(self, size=-1):data = self._rbufif size < 0:buffers = data and [data] or []self._rbuf   = ''self.tasklet = getcurrent()yieldwhile True:try:data = self.socket.recv(8192)except SocketError, why:if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:data = ''self.close()else:print >> stderr, 'error: socket error, client down'try: self.close()except: passself.read_channel = channel()self.tasklet.raise_exception(Disconnect)raise StopIterationif not data:breakbuffers.append(data)yieldtry: pollster.unregister(self.pid)except (KeyError, AttributeError): passself.read_channel.send(''.join(buffers))else:buf_len = len(data)if buf_len >= size:self._rbuf = data[size:]yield data[:size]raise StopIterationbuffers = data and [data] or []self._rbuf   = ''self.tasklet = getcurrent()yieldwhile True:left = size - buf_lentry:data = self.socket.recv(max(8192, left))except SocketError, why:if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:data = ''self.close()else:print >> stderr, 'error: socket error, client down'try: self.close()except: passself.read_channel = channel()self.tasklet.raise_exception(Disconnect)raise StopIterationif not data:breakbuffers.append(data)n = len(data)if n >= left:self._rbuf = data[left:]buffers[-1] = data[:left]breakbuf_len += nyieldtry: pollster.unregister(self.pid)except (KeyError, AttributeError): passself.read_channel.send(''.join(buffers))def read4line(self, size=-1):data = self._rbufif size < 0:nl = data.find('/n')if nl >= 0:nl += 1self._rbuf = data[nl:]yield data[:nl]raise StopIterationbuffers = data and [data] or []self._rbuf   = ''self.tasklet = getcurrent()yieldwhile True:try:data = self.socket.recv(8192)except SocketError, why:if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:data = ''self.close()else:print >> stderr, 'error: socket error, client down'try: self.close()except: passself.read_channel = channel()self.tasklet.raise_exception(Disconnect)raise StopIterationif not data:breakbuffers.append(data)nl = data.find('/n')if nl >= 0:nl += 1self._rbuf = data[nl:]buffers[-1] = data[:nl]breakyieldtry: pollster.unregister(self.pid)except (KeyError, AttributeError): passself.read_channel.send(''.join(buffers))else:nl = data.find('/n', 0, size)if nl >= 0:nl += 1self._rbuf = data[nl:]yield data[:nl]raise StopIterationbuf_len = len(data)if buf_len >= size:self._rbuf = data[size:]yield data[:size]raise StopIterationbuffers = data and [data] or []self._rbuf   = ''self.tasklet = getcurrent()yieldwhile True:try:data = self.socket.recv(8192)except SocketError, why:if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:data = ''self.close()else:print >> stderr, 'error: socket error, client down'try: self.close()except: passself.read_channel = channel()self.tasklet.raise_exception(Disconnect)raise StopIterationif not data:breakbuffers.append(data)left = size - buf_lennl = data.find('/n', 0, left)if nl >= 0:nl += 1self._rbuf = data[nl:]buffers[-1] = data[:nl]breakn = len(data)if n >= left:self._rbuf = data[left:]buffers[-1] = data[:left]breakbuf_len += nyieldtry: pollster.unregister(self.pid)except (KeyError, AttributeError): passself.read_channel.send(''.join(buffers))def write4raw(self):while self._wbuf:try:num_sent = self.socket.send(self._wbuf[:8192])except SocketError, why:if why[0] == EWOULDBLOCK:num_sent = 0else:print >> stderr, 'error: socket error, client down'try: self.close()except: passself.write_channel = channel()self.tasklet.raise_exception(Disconnect)raise StopIterationif num_sent:self._wbuf = self._wbuf[num_sent:]yieldtry: pollster.unregister(self.pid)except (KeyError, AttributeError): passself.write_channel.send(None)def handle_error(self):print >> stderr, 'error: fatal error, client down'self.close()self.tasklet.raise_exception(Disconnect)class HttpFile(dict):def __init__(self, sockfile):first  = sockfile.readline(8192)try:method, self.path, version = R_FIRST(first).groups()except AttributeError:sockfile.close()raise IOErrorself.version = version.upper()line = sockfile.readline(8192)counter = len(first) + len(line)while True:try:key, value = R_HEADER(line).groups()except AttributeError:if line in ('/r/n', '/n'):breaksockfile.close()raise IOErrordict.__setitem__(self, '-'.join(i.capitalize() for i in key.split('-')), value)line = sockfile.readline(8192)counter += len(line)if counter > 10240:sockfile.close()raise IOErrorself.method = method.upper()if self.method == 'GET':self.left = 0else:try:self.left = int(self['Content-Length'])except:sockfile.close()raise IOErrorself.content  = []self.respader = {}self.sockfile = sockfileself.keep_alive = channel()self.write = self.content.appenddef __del__(self):if hasattr(self, 'keep_alive'):return self.keep_alive and self.keep_alive.send(0)def __setitem__(self, key, value):self.respader['-'.join(i.capitalize() for i in key.split('-'))] = valuepid     = property(lambda self: self.sockfile.pid)address = property(lambda self: self.sockfile.address)fileno  = lambda self: self.sockfile.pidnocache = lambda self: self.respader.update(NOCACHEHEADERS)def getuid(self):try:return R_UID(self['Cookie']).groups()[0]except:return Nonedef setuid(self, uid):self.respader['Set-Cookie'] = 'uid=%s; path=/; expires=%s' %(uid, strftime('%a, %d-%b-%Y %H:%M:%S GMT', gmtime(time() + 157679616)))uid = property(getuid, setuid)del getuid, setuiddef setstatus(self, status):self._status  = statusself._message = RESPONSES[status]getstatus = lambda self: self._statusstatus, _status, _message = property(getstatus, setstatus), 200, RESPONSES[200]del getstatus, setstatusdef read(self, size=-1):if size == -1 or size >= self.left:try:data = self.sockfile.read(self.left)except Disconnect:self.keep_alive = self.keep_alive and self.keep_alive.send(0)raiseself.left = 0return dataelse:try:data = self.sockfile.read(size)except Disconnect:self.keep_alive = self.keep_alive and self.keep_alive.send(0)raiseself.left -= len(data)return datadef readline(self, size=-1):if size == -1 or size >= self.left:try:data = self.sockfile.readline(self.left)except Disconnect:self.keep_alive = self.keep_alive and self.keep_alive.send(0)raiseelse:try:data = self.sockfile.readline(size)except Disconnect:self.keep_alive = self.keep_alive and self.keep_alive.send(0)raiseself.left -= len(data)return datadef begin(self):self.keep_alive = self.keep_alive and self.keep_alive.send(0)data = ['%s: %s' %(key, value) for key, value in self.respader.items()]data.insert(0, '%s %s %s' %(self.version, self._status, self._message))data.append('/r/n')self.write = self.sockfile.writeself.write('/r/n'.join(data))self.close = self.end = self.sockfile.closedef wbegin(self, data=''):self.keep_alive = self.keep_alive and self.keep_alive.send(0)respader = ['%s: %s' %(key, value) for key, value in self.respader.items()]respader.insert(0, '%s %s %s' %(self.version, self._status, self._message))respader.append('/r/n')self.write = self.sockfile.writeself.write('/r/n'.join(respader) + data)self.close = self.end = self.sockfile.closedef close(self):if not self.keep_alive:raise Disconnectdata = ''.join(self.content)self.respader['Content-Length'] = str(len(data))respader = ['%s: %s' %(key, value) for key, value in self.respader.items()]respader.insert(0, '%s %s %s' %(self.version, self._status, self._message))respader.append('/r/n')data = '/r/n'.join(respader) + datatry:self.sockfile.write(data)except Disconnect:self.keep_alive = self.keep_alive and self.keep_alive.send(0)raiseself.keep_alive = self.keep_alive.send(1) if self.get('Connection', '').lower() == 'keep-alive' /else self.sockfile.close() or self.keep_alive.send(0)def wshutdown(self, data=''):try:self.sockfile.write(data)except Disconnect:self.keep_alive = self.keep_alive and self.keep_alive.send(0)raiseself.sockfile.close()self.keep_alive = self.keep_alive and self.keep_alive.send(0)def shutdown(self):self.sockfile.close()self.keep_alive = self.keep_alive and self.keep_alive.send(0)class Comet(object):def __init__(self, httpfile):self.httpfile = httpfilehttpfile.respader.update(COMETHEADERS)def __getattr__(self, name):return RemoteCall(self.httpfile, name)def __getitem__(self, key):return self.httpfile[key]def __setitem__(self, key, value):self.httpfile.respader['-'.join(i.capitalize() for i in key.split('-'))] = valuepid     = property(lambda self: self.httpfile.pid)address = property(lambda self: self.httpfile.address)uid     = property(lambda self: self.httpfile.uid, /lambda self, uid: setattr(self.httpfile, 'uid', uid))def fileno(self):return self.httpfile.sockfile.piddef begin(self):self.httpfile.wbegin(COMET_BEGIN)def end(self):self.httpfile.wshutdown(COMET_END)def close(self):self.httpfile.wshutdown(COMET_END)def shutdown(self):self.httpfile.shutdown()class RemoteCall(object):def __init__(self, httpfile, name):self._name    = nameself.httpfile = httpfiledef __call__(self, *args):self.httpfile._write(T_REMOTECALL(function = self._name,arguments = args and ', '.join([json(arg) for arg in args]) or ''))def __getattr__(self, name):return RemoteCall(self.httpfile, '%s.%s' %(self._name, name))def __getitem__(self, name):if isinstance(unicode):return RemoteCall(self.httpfile, '%s[%s]' %(self._name, repr(name)[1:]))return RemoteCall(self.httpfile, '%s[%s]' %(self._name, repr(name)))def Form(httpfile, max_size=1048576):if httpfile.method == 'POST':length = httpfile['Content-Length']if int(length) > max_size:httpfile.close()raise IOError('overload')data = httpfile.read(length)else:data = ''p = httpfile.path.find('?')if p != -1:data = '%s&%s' %(httpfile.path[p+1:], data)d = {}for item in data.split('&'):try:key, value = item.split('=', 1)value = unquote_plus(value)try:if isinstance(d[key], list):d[key].append(value)else:d[key] = [d[key], value]except KeyError:d[key] = valueexcept ValueError:continuereturn dclass SimpleUpload(dict):def __init__(self, httpfile):try: next = '--' + parse_header(httpfile['Content-Type'])[1]['boundary']except: raise IOErrorlast, c = next + '--', 0while True:line = httpfile.readline(65536)c += len(line)if not line:raise IOErrorif line[:2] == '--':strpln = line.strip()if strpln == next:c1 = (line[-2:] == '/r/n' and 2 or 1) << 1c_next = c1 + len(next)breakif strpln == last:raise IOErrorfilename = Nonewhile True:name = Nonefor i in xrange(32):line = httpfile.readline(65536)c += len(line)line = line.strip()if not line:if not name: raise IOErrorif filename:self.buff      = ''self.httpfile  = httpfileself.filename  = filenameself._readline = self._readline(next, last).nexttry: size = int(httpfile['Content-Length'])except:returnself.size = size - c - c1 - len(last)returndata = self.read()c += c_next + len(data)try: self[name].append(data)except KeyError: self[name] = dataexcept AttributeError: self[name] = [self[name], data]breakt1, t2 = line.split(':', 1)if t1.lower() != 'content-disposition': continue t1, t2 = parse_header(t2)if t1.lower() != 'form-data': raise IOErrortry: name = t2['name']except KeyError: raise IOErrortry: filename = t2['filename']except KeyError: continuem = R_UPLOAD(filename)if not m: raise IOErrorfilename = m.groups()[0]def _readline(self, next, last):httpfile = self.httpfileline = httpfile.readline(65536)if not line:raise IOErrorif line[:2] == '--':strpln = line.strip()if strpln == next or strpln == last:raise IOErrorel = line[-2:] == '/r/n' and '/r/n' or (line[-1] == '/n' and '/n' or '')while True:line2 = httpfile.readline(65536)if not line2: raise IOErrorif line2[:2] == '--' and el:strpln = line2.strip()if strpln == next or strpln == last:yield line[:-len(el)]breakyield lineline = line2el = line[-2:] == '/r/n' and '/r/n' or (line[-1] == '/n' and '/n' or '')while True:yield Nonedef read(size=None):buff = self.buffif size:while len(buff) < size:line = self._readline()if not line:self.buff = ''return buffbuff += lineself.buff = buff[size:]return buff[:size]d, self.buff = [buff], ''while True:line = self._readline()if not line:return ''.join(d)d.append(line)def readline(size=None):buff = self.buffif size:nl = buff.find('/n', 0, size)if nl >= 0:nl += 1self.buff = buff[nl:]return buff[:nl]elif len(buff) > size:self.buff = buff[size:]return buff[:size]t = self._readline()if not t:self.buff = ''return buffbuff = buff + tif len(buff) > size:self.buff = buff[size:]return buff[:size]self.buff = ''return buffnl = buff.find('/n')if nl >= 0:nl += 1self.buff = buff[nl:]return buff[:nl]t = self._readline()self.buff = ''return buff + t if t else buffdef HttpHandler(controller):def handler(sock, addr):sockfile = SocketFile(sock, addr)try:httpfile = HttpFile(sockfile)except IOError:returntasklet(controller)(httpfile)while httpfile.keep_alive.receive():try:httpfile = HttpFile(sockfile)except IOError:returntasklet(controller)(httpfile)return handlerdef TcpHandler(controller):def handler(sock, addr):try:controller(SocketFile(sock, addr))except:print_exc(file=stderr)return handlerclass Server:def __init__(self, address, handler):sock = Socket(AF_INET, SOCK_STREAM)sock.setblocking(0)try:sock.setsockopt(SOL_SOCKET, SO_REUSEADDR,sock.getsockopt(SOL_SOCKET, SO_REUSEADDR)|1)except SocketError:passsock.bind(address)sock.listen(4194304)self.socket  = sockself.handler = handlerself.address = addressself.pid = sock.fileno()socket_map[self.pid] = selfpollster.register(self.pid, RE)def handle_read(self):handler = tasklet(self.handler)try:conn, addr = self.socket.accept()try:handler(conn, addr)except:print_exc(file=stderr)except SocketError, why:if why[0] == EWOULDBLOCK:passelse:print >> stderr, 'warning: server socket exception, ignore'except TypeError:passdef handle_error(self):print >> stderr, 'warning: server socket exception, ignore'def mainloop():while True:try:stackless.run()except KeyboardInterrupt:breakexcept:print_exc(file=stderr)continuedef poll():while True:try:r = pollster.poll(1)except SelectError, e:if e[0] != EINTR:raiser = []for fd, flags in r:try: obj = socket_map[fd]except KeyError: continueif flags & R:try: obj.handle_read()except StopIteration: passexcept: print_exc(file=stderr)if flags & W:try: obj.handle_write()except StopIteration: passexcept: print_exc(file=stderr)if flags & E:try: obj.handle_error()except: print_exc(file=stderr)schedule()def json(obj):if isinstance(obj, str): return repr(obj)elif isinstance(obj, unicode): return repr(obj)[1:]elif obj is None: return 'null'elif obj is True: return 'true'elif obj is False: return 'false'elif isinstance(obj, (int, long)): return str(obj)elif isinstance(obj, float): return _json_float(obj)elif isinstance(obj, (list, tuple)): return '[%s]' %', '.join(_json_array(obj))elif isinstance(obj, dict): return '{%s}' %', '.join(_json_object(obj))elif isinstance(obj, RemoteCall): return '__comet__.' + obj.functionraise ValueErrordef _json_array(l):for item in l: yield json(item)def _json_object(d):for key in d: yield '"%s":%s' %(key, json(d[key]))def _json_float(o):s = str(o)if (o < 0.0 and s[1].isdigit()) or s[0].isdigit(): return sif s == 'nan': return 'NaN'if s == 'inf': return 'Infinity'if s == '-inf': return '-Infinity'if o != o or o == 0.0: return 'NaN'if o < 0: return '-Infinity'return 'Infinity'R, W, E = POLLIN|POLLPRI, POLLOUT, POLLERR|POLLHUP|POLLNVALRE, WE, RWE = R|E, W|E, R|W|Esocket_map, pollster, Disconnect = {}, Poll(), type('Disconnect', (IOError, ), {})tasklet(poll)()T_REMOTECALL = Template('<script language="JavaScript">/r/n<!--/r/n''__comet__.${function}(${arguments});/r/n''//-->/r/n</script>/r/n' ).safe_substituteCOMET_BEGIN = ('<html>/r/n<head>/r/n''<meta http-equiv="Pragma" content="no-cache">/r/n''<meta http-equiv="Content-Type" content="text/html">/r/n''<body>/r/n''<script language="JavaScript">/r/n<!--/r/n''if(!window.__comet__) window.__comet__ = window.parent?''(window.parent.__comet__?parent.__comet__:parent):window;/r/n''if(document.all) __comet__.escape("FUCK IE");/r/n''//-->/r/n</script>/r/n<!--COMET BEGIN-->/r/n')COMET_END = '<!--COMET END-->/r/n</body>/r/n</html>'NOCACHEHEADERS = {'Pragma': 'no-cache', 'Cache-Control': 'no-cache, must-revalidate','Expires': 'Mon, 26 Jul 1997 05:00:00 GMT' }COMETHEADERS   = {'Pragma': 'no-cache', 'Cache-Control': 'no-cache, must-revalidate','Expires': 'Mon, 26 Jul 1997 05:00:00 GMT', 'Content-Type': 'text/html; charset=UTF-8' }R_UPLOAD = re.compile(r'([^///]+)$').searchR_UID    = re.compile(r'(?:[^;]+;)* *uid=([^;/r/n]+)').searchR_FIRST  = re.compile(r'^(GET|POST)[/s/t]+([^/r/n]+)[/s/t]+(HTTP/1/.[0-9])/r?/n$', re.I).matchR_HEADER = re.compile(r'^[/s/t]*([^/r/n:]+)[/s/t]*:[/s/t]*([^/r/n]+)[/s/t]*/r?/n$', re.I).match