# -*- Mode: Python; tab-width: 4 -*-

# Proxy/Tunnel Demonstration.

# This server will 'throttle' itself, in either direction, when its
# buffers become full.  Why would you need this?  Picture a fire hose
# coming into the proxy, and a drinking straw going out: Without the
# ability to throttle, the fire hose would dump into the proxy's
# buffers at full speed, resulting in an explosion in memory usage.

# Also demonstrates 'turning off' the input-delimiting feature, by
# setting the terminator to None.  Upon connection to the server, a
# line is read from the client indicating what host and port to
# connect to.  After this line, the terminator is set to None;
# no more input delimiting is done (the data is fed right through with
# looking at it)

import asynchat
import asyncore
import regex
import socket
import string

class proxy_server (asyncore.dispatcher):
	
	def __init__ (self, here=('',59999)):
		asyncore.dispatcher.__init__ (self)
		self.create_socket (socket.AF_INET, socket.SOCK_STREAM)

		# try to re-use a server port if possible
		try:
			self.socket.setsockopt (
				socket.SOL_SOCKET, socket.SO_REUSEADDR,
				self.socket.getsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1
				)
		except:
			pass

		self.bind (here)
		self.listen (5)

	def handle_accept (self):
		proxy_receiver (self, self.accept())

class proxy_sender (asynchat.async_chat):

	def __init__ (self, receiver, address):
		asynchat.async_chat.__init__ (self)
		self.receiver = receiver
		self.set_terminator ('')
		self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
		self.connect (address)

	def handle_connect (self):
		self.receiver.push ('Connected\r\n')

	def collect_incoming_data (self, data):
		self.receiver.push (data)

class proxy_receiver (asynchat.async_chat):
	def __init__ (self, server, (conn, addr)):
		self.buffer = ''
		self.set_terminator ('\r\n')
		asynchat.async_chat.__init__ (self, conn)
		self.push ('Enter <host> <port>:\r\n')

	def collect_incoming_data (self, data):
		if self.buffer is None:
			self.sender.push (data)
		else:
			self.buffer = self.buffer + data

	reg = regex.compile ('\(.*\) \([0-9]+\)')

	def found_terminator (self):
		# this will only happen for the first line.
		line = self.buffer
		self.buffer = None
		self.set_terminator (None)
		if self.reg.match (line) == len(line):
			host = self.reg.group(1)
			port = string.atoi (self.reg.group(2))
			self.sender = proxy_sender (
				self, (host, port)
				)
		else:
			self.close()
		
	def handle_close (self):
		self.sender.close()
		self.close()
		del self.sender

if __name__ == '__main__':
	ps = proxy_server()
	asyncore.loop()
