]> granicus.if.org Git - python/commitdiff
??? adding this file somehow failed -- jvr
authorJust van Rossum <just@lettererror.com>
Sat, 30 Jan 1999 22:40:26 +0000 (22:40 +0000)
committerJust van Rossum <just@lettererror.com>
Sat, 30 Jan 1999 22:40:26 +0000 (22:40 +0000)
Mac/Tools/IDE/Wsocket.py [new file with mode: 0644]

diff --git a/Mac/Tools/IDE/Wsocket.py b/Mac/Tools/IDE/Wsocket.py
new file mode 100644 (file)
index 0000000..e0077b2
--- /dev/null
@@ -0,0 +1,395 @@
+"""Async sockets, build on top of Sam Rushing's excellent async library"""
+
+import asyncore
+import socket
+from socket import AF_INET, SOCK_STREAM
+import string
+import cStringIO
+import mimetools
+import httplib
+
+
+__version__ = "0.9"
+__author__ = "jvr"
+
+BUFSIZE = 512
+
+VERBOSE = 1
+
+class Server(asyncore.dispatcher):
+       
+       """Generic asynchronous server class"""
+       
+       def __init__(self, port, handler_class, backlog=1, host=""):
+               """arguments:
+               - port: the port to listen to
+               - handler_class: class to handle requests
+               - backlog: backlog queue size (optional) (don't fully understand, see socket docs)
+               - host: host name (optional: can be empty to use default host name)
+               """
+               if VERBOSE:
+                       print "Starting", self.__class__.__name__
+               self.handler_class = handler_class
+               asyncore.dispatcher.__init__(self)
+               self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+               self.bind((host, port))
+               self.listen(backlog)
+       
+       def handle_accept(self):
+               conn, addr = self.accept()
+               if VERBOSE:
+                       print 'Incoming Connection from %s:%d' % addr
+               self.handler_class(conn)
+
+
+class ProxyServer(Server):
+       
+       """Generic proxy server class"""
+       
+       def __init__(self, port, handler_class, proxyaddr=None, closepartners=0):
+               """arguments:
+               - port: the port to listen to
+               - handler_class: proxy class to handle requests
+               - proxyaddr: a tuple containing the address and 
+                 port of a remote host to connect to (optional)
+               - closepartners: boolean, specifies whether we should close
+                 all proxy connections or not (optional). http seems to *not*
+                 want this, but telnet does...
+               """
+               Server.__init__(self, port, handler_class, 1, "")
+               self.proxyaddr = proxyaddr
+               self.closepartners = closepartners
+       
+       def handle_accept(self):
+               conn, addr = self.accept()
+               if VERBOSE:
+                       print 'Incoming Connection from %s:%d' % addr
+               self.handler_class(conn, self.proxyaddr, closepartner=self.closepartners)
+
+
+class Connection(asyncore.dispatcher):
+       
+       """Generic connection class"""
+       
+       def __init__(self, sock_or_address=None, readfunc=None, terminator=None):
+               """arguments: 
+               - sock_or_address: either a socket, or a tuple containing the name 
+               and port number of a remote host
+               - readfunc: callback function (optional). Will be called whenever
+                 there is some data available, or when an appropraite terminator
+                 is found.
+               - terminator: string which specifies when a read is complete (optional)"""
+               self._out_buffer = ""
+               self._in_buffer = ""
+               self.readfunc = readfunc
+               self.terminator = terminator
+               asyncore.dispatcher.__init__(self)
+               if hasattr(sock_or_address, "fileno"):
+                       self.set_socket(sock_or_address)
+               else:
+                       sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                       sock.setblocking(0)
+                       self.set_socket(sock)
+                       if sock_or_address:
+                               self.connect(sock_or_address)
+       
+       # public methods
+       def send(self, data):
+               self._out_buffer = self._out_buffer + data
+       
+       def recv(self, bytes=-1):
+               if bytes == -1:
+                       bytes = len(self._in_buffer)
+               data = self._in_buffer[:bytes]
+               self._in_buffer = self._in_buffer[bytes:]
+               return data
+       
+       def set_terminator(self, terminator):
+               self.terminator = terminator
+       
+       # override this if you want to control the incoming data stream 
+       def handle_incoming_data(self, data):
+               if self.readfunc:
+                       if self.terminator:
+                               self._in_buffer = self._in_buffer + data
+                               pos = string.find(self._in_buffer, self.terminator)
+                               if pos < 0:
+                                       return
+                               data = self._in_buffer[:pos]
+                               self._in_buffer = self._in_buffer[pos + len(self.terminator):]
+                               self.readfunc(data)
+                       else:
+                               self.readfunc(self._in_buffer + data)
+                               self._in_buffer = ""
+               else:
+                       self._in_buffer = self._in_buffer + data
+       
+       # internal muck
+       def handle_read(self):
+               data = asyncore.dispatcher.recv(self, BUFSIZE)
+               if data:
+                       if VERBOSE > 2:
+                               print "incoming ->", "%x" % id(self), `data`
+                       self.handle_incoming_data(data)
+       
+       def handle_write(self):
+               if self._out_buffer:
+                       sent = self.socket.send(self._out_buffer[:BUFSIZE])
+                       if VERBOSE > 2:
+                               print "outgoing ->", "%x" % id(self), `self._out_buffer[:sent]`
+                       self._out_buffer = self._out_buffer[sent:]
+       
+       def close(self):
+               if self.readfunc and self._in_buffer:
+                       self.readfunc(self._in_buffer)
+                       self._in_buffer = ""
+               #elif VERBOSE > 1 and self._in_buffer:
+               #       print "--- there is unread data:", `self._in_buffer`
+               asyncore.dispatcher.close(self)
+       
+       def handle_close(self):
+               self.close()
+       
+       def handle_connect(self):
+               pass
+
+
+class ConnectionUI:
+       
+       """Glue to let a connection tell things to the UI in a standardized way.
+       
+       The protocoll defines four functions, which the connection will call:
+       
+               def settotal(int total): gets called when the connection knows the data size
+               def setcurrent(int current): gets called when some new data has arrived
+               def done(): gets called when the transaction is complete
+               def error(type, value, tb): gets called wheneven an error occured
+       """
+       
+       def __init__(self, settotal_func, setcurrent_func, done_func, error_func):
+               self.settotal = settotal_func
+               self.setcurrent = setcurrent_func
+               self.done = done_func
+               self.error = error_func
+
+
+class HTTPError(socket.error): pass
+
+
+class HTTPClient(Connection, httplib.HTTP):
+       
+       """Asynchronous HTTP connection"""
+       
+       def __init__(self, (host, port), datahandler, ui=None):
+               Connection.__init__(self, (host, port))
+               self.datahandler = datahandler
+               self.ui = ui
+               self.buf = ""
+               self.doneheaders = 0
+               self.done = 0
+               self.headers = None
+               self.length = None
+               self.pos = 0
+       
+       def getreply(self):
+               raise TypeError, "getreply() is not supported in async HTTP connection"
+       
+       def handle_incoming_data(self, data):
+               assert not self.done
+               if not self.doneheaders:
+                       self.buf = self.buf + data
+                       pos = string.find(self.buf, "\r\n\r\n")
+                       if pos >= 0:
+                               self.handle_reply(self.buf[:pos+4])
+                               length = self.headers.getheader("Content-Length")
+                               if length is not None:
+                                       self.length = int(length)
+                                       if self.ui is not None:
+                                               self.ui.settotal(self.length)
+                               else:
+                                       self.length = -1
+                               self.doneheaders = 1
+                               self.handle_data(self.buf[pos+4:])
+                               self.buf = ""
+               else:
+                       self.handle_data(data)
+       
+       def handle_reply(self, data):
+               f = cStringIO.StringIO(data)
+               ver, code, msg = string.split(f.readline(), None, 2)
+               code = int(code)
+               msg = string.strip(msg)
+               if code <> 200:
+                       # Hm, this is what *I* need, but probably not correct...
+                       raise HTTPError, (code, msg)
+               self.headers = mimetools.Message(f)
+       
+       def handle_data(self, data):
+               self.pos = self.pos + len(data)
+               if self.ui is not None:
+                       self.ui.setcurrent(self.pos)
+               self.datahandler(data)
+               if self.pos >= self.length:
+                       self.datahandler("")
+                       self.done = 1
+                       if self.ui is not None:
+                               self.ui.done()
+       
+       def handle_error(self, type, value, tb):
+               if self.ui is not None:
+                       self.ui.error(type, value, tb)
+               else:
+                       Connection.handle_error(self, type, value, tb)
+       
+       def log(self, message):
+               if VERBOSE:
+                       print 'LOG:', message
+
+
+class PyMessage:
+       
+       def __init__(self):
+               self._buf = ""
+               self._len = None
+               self._checksum = None
+       
+       def feed(self, data):
+               self._buf = self._buf + data
+               if self._len is None:
+                       if len(self._buf) >= 8:
+                               import struct
+                               self._len, self._checksum = struct.unpack("ll", self._buf[:8])
+                               self._buf = self._buf[8:]
+               if self._len is not None:
+                       if len(self._buf) >= self._len:
+                               import zlib
+                               data = self._buf[:self._len]
+                               leftover = self._buf[self._len:]
+                               self._buf = None
+                               assert self._checksum == zlib.adler32(data), "corrupt data"
+                               self.data = data
+                               return 1, leftover
+                       else:
+                               return 0, None
+               else:
+                       return 0, None
+
+
+class PyConnection(Connection):
+       
+       def __init__(self, sock_or_address):
+               Connection.__init__(self, sock_or_address)
+               self.currentmessage = PyMessage()
+       
+       def handle_incoming_data(self, data):
+               while data:
+                       done, data = self.currentmessage.feed(data)
+                       if done:
+                               import cPickle
+                               self.handle_object(cPickle.loads(self.currentmessage.data))
+                               self.currentmessage = PyMessage()
+       
+       def handle_object(self, object):
+               print 'unhandled object:', `object`
+       
+       def send(self, object):
+               import cPickle, zlib, struct
+               data = cPickle.dumps(object, 1)
+               length = len(data)
+               checksum = zlib.adler32(data)
+               data = struct.pack("ll", length, checksum) + data
+               Connection.send(self, data)
+
+
+class Echo(Connection):
+       
+       """Simple echoing connection: it sends everything back it receives.""" 
+       
+       def handle_incoming_data(self, data):
+               self.send(data)
+
+
+class Proxy(Connection):
+       
+       """Generic proxy connection"""
+       
+       def __init__(self, sock_or_address=None, proxyaddr=None, closepartner=0):
+               """arguments:
+               - sock_or_address is either a socket or a tuple containing the 
+               name and port number of a remote host
+               - proxyaddr: a tuple containing a name and a port number of a 
+                 remote host (optional).
+               - closepartner: boolean, specifies whether we should close
+                 the proxy connection (optional)"""
+               
+               Connection.__init__(self, sock_or_address)
+               self.other = None
+               self.proxyaddr = proxyaddr
+               self.closepartner = closepartner
+       
+       def close(self):
+               if self.other:
+                       other = self.other
+                       self.other = None
+                       other.other = None
+                       if self.closepartner:
+                               other.close()
+               Connection.close(self)
+       
+       def handle_incoming_data(self, data):
+               if not self.other:
+                       # pass data for possible automatic remote host detection
+                       # (see HTTPProxy)
+                       data = self.connectproxy(data)
+               self.other.send(data)
+       
+       def connectproxy(self, data):
+               other = self.__class__(self.proxyaddr, closepartner=self.closepartner)
+               self.other = other
+               other.other = self
+               return data
+
+
+class HTTPProxy(Proxy):
+       
+       """Simple, useless, http proxy. It figures out itself where to connect to."""
+       
+       def connectproxy(self, data):
+               if VERBOSE:
+                       print "--- proxy request", `data`
+               addr, data = de_proxify(data)
+               other = Proxy(addr)
+               self.other = other
+               other.other = self
+               return data
+
+
+# helper for HTTPProxy
+def de_proxify(data):
+       import re
+       req_pattern = "GET http://([a-zA-Z0-9-_.]+)(:([0-9]+))?"
+       m = re.match(req_pattern, data)
+       host, dummy, port = m.groups()
+       if not port:
+               port = 80
+       else:
+               port = int(port)
+       # change "GET http://xx.xx.xx/yy" into "GET /yy"
+       data = re.sub(req_pattern, "GET ", data)
+       return (host, port), data
+
+
+# if we're running "under W", let's register the socket poller to the event loop
+try:
+       import W
+except:
+       pass
+else:
+       W.getapplication().addidlefunc(asyncore.poll)
+
+
+## testing muck
+#testserver = Server(10000, Connection)
+#echoserver = Server(10007, Echo)
+#httpproxyserver = Server(8088, HTTPProxy, 5)
+#asyncore.close_all()