Wsocket.py :  » Language-Interface » ChinesePython » chinesepython2.1.3-0.4 » Mac » Tools » IDE » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Language Interface » ChinesePython 
ChinesePython » chinesepython2.1.3 0.4 » Mac » Tools » IDE » Wsocket.py
"""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()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.