# This is an attempt to do a really simple server in python.
import sys, string, socket, select
import errno

class Selector:
	'''Selector implements a select() based server model.

No initialization arguments.

methods:
- addreader(reader), addwriter(writer), delreader(reader), delwriter(writer):
  Add or delete selectable objects for reading or writing. The same object
  may be added in both categories. Deleting nonexistent objects will raise
  an error.
- isreader(o), iswriter(o): is object o currently a reader (or a writer)
- timeout(n): set the select timeout value to n. The default timeout is None.
- abort(): cease operations. Most useful when called from insinde loop().
- once(): process the select loop once.
- loop(): process the select loop until abort() is called or there is
  nothing left reading or writing.

When an object becomes ready for reading or writing its 'inhandle' or
'outhandle' method is called. If a socket error is raised by this, the
object is deleted from the respective list of objects to be selected on.
'''
	def __init__(self):
		self._readers = []
		self._writers = []
		self._timeout = None
		self._abort = None
	def addreader(self, reader):
		self._readers.append(reader)
	def addwriter(self, writer):
		self._writers.append(writer)
	def delreader(self, reader):
		self._readers.remove(reader)
	def delwriter(self, writer):
		self._writers.remove(writer)
	def isreader(self, o):
		return o in self._readers
	def iswriter(self, o):
		return o in self._writers
	def isreaders(self):
		return len(self._readers) > 0
	def iswriters(self):
		return len(self._writers) > 0
	def settimeout(self, timeout):
		self._timeout = timeout
	def abort(self):
		self._abort = 1
	def once(self):
		(rdrs, wrtrs, t) = select.select(self._readers, self._writers, [],
						 self._timeout)
		# If the reader or writer operations encounter an IO
		# error, we kill the reader (or writer). This keeps things
		# neat (hopefully), and going. If everyone dies, well...
		for rdr in rdrs:
			try:	rdr.inhandle()
			except socket.error:
				try:	self.delreader(rdr)
				except:	pass
		for wrtr in wrtrs:
			try:	wrtr.outhandle()
			except socket.error:
				try:	self.delwriter(wrtr)
				except:	pass
	def loop(self):
		while not self._abort and (len(self._readers) > 0 or len(self._writers) > 0):
			self.once()

# This is a superclass, not intended to be used directly.
class Socket:
	'''The superclass for sockets for use with Selector.

Socket is not directly usable; it exists to be a class parent.
Initialization arguments:
	Socket(socket, selector-obj, [option = ..., option =...])
'socket' is the underlying socket, and selector-obj is the selector
object we are to be associated with.
Common Socket methods:
- fileno(): supplies the socket file number for select.
- setopts(option = ..., [...]): standard way to set object options by name.
- abort(): abruptly and gracelessly terminates the socket connection on error.
- shutdown(): gracefully terminates socket connection on normal shutdown.
'''
	def fileno(self):
		return self._socket.fileno()
	def getpeername(self):
		return self._socket.getpeername()
	def getsockname(self):
		return self._socket.getsockname()
	def setopts(self, **kwargs):
		for i in kwargs.keys():
			if hasattr(self, i):
				setattr(self, i, kwargs[i])
			else:
				raise TypeError, 'option %s does not exist' % (i,)

# This is the class for listening (selector-accepting) sockets.
class ListenSocket(Socket):
	'''ListenSocket implements a listening socket.

Init parameters: listenbacklog = some value
Important methods:
- setchildargs(CreateFN, keyword = a, keyword2 = b, ...):
  Sets the creation function for this object. The creation function
  is called when a new connection is accepted, and is called as
  'CreateFN(socket, selector-obj, addr = addr, listener = ME)'. It
  is expected to return a selectable-for-read object that will be
  added to the selector. Traditionally the creation function is a
  class, eg DataSocket.
  If optional keyword arguments are supplied, they will be hoovered
  up and sent to newobj.setopts().

A ListenSocket with no creation function will simply accept and then
discard new connection sockets.
'''
	childinit = None
	childopts = {}
	listenbacklog = 10
	def __init__(self, socket, selector, **args):
		self._socket = socket
		self.selector = selector
		apply(self.setopts, (), args)
		self._socket.setblocking(0)
		self._socket.listen(self.listenbacklog)
	def abort(self):
		if self.selector:
			self.selector.delreader(self)
			self.selector = None
			self._socket.close()
			self._socket = None
	def shutdown(self):
		self.abort()
	def setchildargs(self, spawner, **kwopts):
		self.childinit = spawner
		self.childopts = kwopts
	def inhandle(self):
		(sock, addr) = self._socket.accept()
		sock.setblocking(0)
		# We apply some black magic with a keyword dictionary to
		# make this thing go.
		if self.childinit:
			res = self.childinit(sock, self.selector, addr = addr, listener = self)
			if res:
				if self.childopts:
					apply(res.setopts, (), self.childopts)
				self.selector.addreader(res)
		else:
			# Bye bye.
			socket.close()

class DataSocket(Socket):
	'''DataSocket accepts block data with a maximum buffer size.

DataSocket is a Socket subclass.
Initailization arguments: socket, selector-obj, optional options as keyword
arguments.
Available options:
- maxbufsize: constrain the amount of data we buffer without being able
  to process. We will never buffer more than this amount, and if we have
  this amount buffered and cannot process it it is an error and we abort
  ourselves. Default: unlimited.
- blocksize: if less than maxbufsize, we read data from the network in
  chunks no larger than this.
- listener, addr: the listening socket that created us and the address of
  the remote endpoint of our socket, respectively. (Set at creation.)
- handlerinit: the function called to create a handler object for data
  read from the socket. See setchildargs().

Methods:
- setchildargs(CreateFN[, option = val, ...]): CreateFN is called
  to create a handler object, with the optional keyword arguments. A
  handler object has two methods: processin(str) and errormax().
  The former is called to process a block of data and must return the
  number of bytes of the block it has consumed (and not touch the block);
  it may return 0 if it can do nothing right now. The latter is called
  when the buffer overflows and the socket is about to be aborted.
  Conventionally the creation function is a class.
  The creation function is called with one normal argument, the DataSocket
  object it is associated with.
- send(str): send the string out the socket network connection.

If there is no creation function, data read is quietly discarded.
The processin() method is called every time we have read a block of
data from the network. It is called with all unconsumed data to date
(both newly read and any old data not yet consumed). If it has received
incomplete data, it should return 0 and wait for more to show up.
'''
	listener = handlerinit = addr = None
	handleropts = {}
	maxbufsize = sys.maxint
	blocksize = 16 * 1024
	def __init__(self, socket, selector, **kwargs):
		self._socket = socket
		self.selector = selector
		self._inbuf = self._outbuf = ''
		self._live = 1
		self._handler = None
		apply(self.setopts, (), kwargs)
	def setchildargs(self, spawner, **kwopts):
		self.handlerinit = spawner
		self.handleropts = kwopts
	def _inithandler(self):
		if self._handler:
			return self._handler
		if not self.handlerinit:
			return None
		self._handler = apply(self.handlerinit, (self,), self.handleropts)
		return self._handler
	def abort(self):
		if not self._socket:
			return
		self._socket.close()
		if self._outbuf and self.selector:
			self.selector.delwriter(self)
		if self._live:
			self.selector.delreader(self)
		self._live = 0
		self._socket = None
		self.selector = None
	def shutdown(self):
		'''Gracefully shut down reading and clean up.'''
		if not self._live or not self._socket:
			return
		self._live = 0
		self.selector.delreader(self)
		if not self._outbuf:
			self._socket.close()
			self._socket = None
		else:
			self._socket.shutdown(0)
			# Our write operation will clean itself up.
	def outhandle(self):
		res = self._socket.send(self._outbuf)
		self._outbuf = self._outbuf[res:]
		if len(self._outbuf) == 0:
			self.selector.delwriter(self)
			# Are we shutting down?
			if not self._live:
				self._socket.close()
				self._socket = None
	def send(self, str):
		kick = 1
		if not self._outbuf:
			self.selector.addwriter(self)
		else:
			# cannot kick with a pending buffer, we might have
			# exhausted our non-blocking write buffer.
			kick = 0
		self._outbuf = self._outbuf + str
		# We can get processed in the select loop, but kick it here:
		if kick:
			self.outhandle()
	def processin(self):
		if not self._inithandler():
			# With no action handler, just eat all the data.
			self._inbuf = ''
			return
		while len(self._inbuf) > 0 and self._live:
			res = self._handler.processin(self._inbuf)
			if res == 0:
				if len(self._inbuf) == self.maxbufsize:
					self._handler.errormax()
					self.abort()
				return
			else:
				self._inbuf = self._inbuf[res:]
	def inhandle(self):
		self._inithandler()
		t = None
		work = 0
		# Why loop? Because we need to reblock the input if necessary.
		# While we may permit a large input size, it doesn't mean we
		# want to create a buffer that big on each pass!
		while len(self._inbuf) < self.maxbufsize:
			chunk = self.maxbufsize - len(self._inbuf)
			if chunk > self.blocksize:
				chunk = self.blocksize
			# socket must be nonblocking!
			try:	t = self._socket.recv(chunk)
			except socket.error, (err, msg):
				if err == errno.EAGAIN:
					break
				else:
					raise socket.error, (err, msg)
			if not t:
				break
			self._inbuf = self._inbuf + t
			work = 1
		# we always process here even on null reads so that we can
		# attempt to flush any pending data through processing at EOF
		self.processin()
		if not work:
			# We have hit EOF. Shutdown or close the socket.
			self.shutdown()

class LineDataSocket(DataSocket):
	'''LineDataSocket accepts and processes input a newline-terminated line at a time.

LineDataSocket is a subclass of DataSocket.
Unlike DataSocket, LineDataSocket calls its handler's processin method
once for each completed newline-terminated line it receives, passing
the processin method the line sans terminating newline. It ignores
the return value; lines passed to the handler are discarded and will not
be reproccessed.

It defines one new method:
- pending(): returns true if there is more data read from the network and
  pending.
'''
	def pending(self):
		return not len(self._inbuf) == 0
	def processin(self):
		if not self._inithandler():
			# ungh. Might as well eat pending partial lines too.
			self._inbuf = ''
			return
		while len(self._inbuf) > 0 and self._live:
			pos = string.find(self._inbuf, '\n')
			if pos == -1:
				if len(self._inbuf) >= self.maxbufsize:
					self._handler.errormax()
					self.abort()
				return
			line = self._inbuf[:pos]
			# unlike the normal case, we skip the \n.
			self._inbuf = self._inbuf[pos+1:]
			self._handler.processin(line)

# This is a base class for socket handlers:
class BaseSocketHandler:
	'''BaseSocketHandler is the base class of Socket handler classes.

BaseSocketHandler defines the necessary behavior for a 'null' handler
class for DataSocket and descendants. A BaseSocketHandler object simply
consumes all data handed to it silently.
'''
	def __init__(self, sobj):
		self.sock = sobj
	def errormax(self):
		pass
	def processin(self, str):
		return len(str)
