Commits

ro...@fe34127c-c842-0410-aa83-9fc675b9a81c  committed 54dd1ce

Created orbited repository

  • Participants
  • Branches 0.1

Comments (0)

Files changed (344)

File clients/php/php-orbited.php

+<?php
+if (!@include 'Net/Socket.php') die("Net_Socket not found - you need to install the Net_Socket package using:\n\tpear install Net_Socket\n");
+
+class Orbited_User_Key {
+    private $location;
+    private $username;
+    private $session_key;
+
+    function __construct($location, $username, $session_key) {
+        $this->location    = $location;
+        $this->username    = $username;
+        $this->session_key = $session_key;
+    }
+    
+    function __tostring() {
+        return $this->location . ', ' . $this->username . ', ' . $this->session_key;
+    }
+}
+
+class Orbited_Client {
+    const VERSION = 'Orbit 1.0';
+    private $addr;
+    private $port;
+    private $socket;
+    private $id;
+    private $connected;
+
+    function __construct($addr = 'localhost', $port = 9000) {
+        $this->addr      = $addr;
+        $this->port      = $port;
+        $this->socket    = null;
+        $this->id        = 0;
+        $this->connected = false;
+    }
+
+    function connect() {
+        $this->connected = true;
+        if ($this->socket) {
+            return;
+        }
+        $this->socket = new Net_Socket;
+        $this->socket->connect($this->addr, $this->port);
+        $this->socket->setBlocking(true);
+    }
+
+    function disconnect() {
+        $this->connected = false;
+        $this->socket->disconnect();
+        $this->socket = null;
+    }
+
+    function reconnect() {
+        $this->disconnect();
+        $this->connect();
+    }
+
+    function sendLine($line = '') {
+        $this->socket->writeLine($line);
+    }
+
+    function event($recipients, $body, $retry = true) {
+        if (!$this->connected) {
+            $this->connect();
+        }
+        try {
+            if (!is_array($recipients)) {
+                $recipients = array($recipients);
+            }
+            if (!$this->socket) {
+                throw new Exception('Connection Lost');
+            }
+            try {
+                $this->id++;
+                $this->sendLine(self::VERSION);
+                $this->sendLine('Event');
+                $this->sendLine('id: ' . $this->id);
+                foreach ($recipients as $recipient) {
+                    if (is_object($recipient)) {
+                        $recipient = $recipient->__tostring();
+                    }
+                    $this->sendLine("recipient: $recipient");
+                }
+                $this->sendLine('length: ' . strlen($body));
+                $this->sendLine();
+                $this->socket->write($body);
+                return $this->read_response();
+            } catch (Exception $e) {
+                $this->disconnect();
+                throw new Exception('Connection Lost');
+            }
+        } catch (Exception $e) {
+            if ($retry) {
+                $this->reconnect();
+                $this->event($recipients, $body, false);
+            } else {
+                throw new Exception('Send Failed');
+            }
+        }
+    }
+
+    // This method is copied from Net_Socket::readLine() since
+    // Net_Socket doesn't offer a method to read up to a specified string!?
+    function read_response() {
+        $line = '';
+        $timeout = time() + $this->socket->timeout;
+        while (!feof($this->socket->fp) && (!$this->socket->timeout || time() < $timeout)) {
+            $line .= @fgets($this->socket->fp, $this->socket->lineLength);
+            if (substr($line, -4) == "\r\n\r\n") {
+                return rtrim($line, "\r\n");
+            }
+        }
+        return $line;
+    }
+}
+
+$client = new Orbited_Client;
+$user_key = new Orbited_User_Key("user", 0, "/demo");
+$client->event( $user_key, "Hello from PHP!<br>", false);
+?>

File clients/python/LICENSE.txt

+This is the MIT license:
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2005, 2006 Michael Carter. pyrobit is a trademark of Kevin Dangoor.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File clients/python/pyorbited/LICENSE.txt

+This is the MIT license:
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2005, 2006 Michael Carter. pyrobit is a trademark of Kevin Dangoor.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File clients/python/pyorbited/__init__.py

Empty file added.

File clients/python/pyorbited/event.py

+import socket
+import event
+LQUEUE_SIZE = 500
+BUFFER_SIZE = 4096
+
+END = '\r\n\r\n'
+LINE = '\r\n'
+
+def cb(req):
+    print req.status
+    print req.headers
+    event.abort()
+    
+def cb2(req):
+    print req.status
+    print req.headers
+
+def client_socket(addr, port):
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.setblocking(0)
+    sock.connect_ex((addr, port))
+    return sock    
+
+def jsonify(self, data):
+    return data
+    
+class Client(object):
+    def __init__(self, servers, jscript=None):        
+        if jscript:
+            self.jscript = jscript
+        self.servers = servers
+        self.connections = dict()
+        self.connections[0] = Connection(0, "localhost", 9000)
+        self.connections[1] = Connection(1, "localhost", 9000)
+        self.id = 0
+#    def connect_all(self):
+#        for connection in self.connections.values():
+#            connection.connect()
+        
+    def event(self, recipients, data, callback=None, jscript=True, timeout=10):
+        self.id += 1
+        if jscript:
+            data = jsonify(data)
+        r = Request(self, self.id, recipients, data, callback)
+        if not callback:
+            # this timeout isn't good for real use. just so the interpreter
+            # doesn't hang while testing.
+            tevent = event.timeout(timeout, event.abort)
+            event.dispatch()
+            tevent.delete()
+            return r
+            
+    def get_conn(self, id):
+        return self.connections[id]
+            
+    def hash(self, key):
+        if 'carn' in key:
+            return 0
+        return 1
+
+
+class Request(object):
+    def __init__(self, client, id, recipients, data, callback=None):
+        self.id = id
+        self.callback = callback
+        self.replies = dict()
+        self.requests = dict()
+        self.data = data
+        for recipient in recipients:
+            num = client.hash(recipient)
+            if num not in self.requests:
+                self.requests[num] = []
+            self.requests[num].append(recipient)
+            
+        for server_id in self.requests.keys():
+            client.get_conn(server_id).event(self)
+        
+
+
+    def render(self, connection):
+        out  = "Orbit 1.0\r\nEvent\r\nid: %s\r\n" % self.id
+        for recipient in self.requests[connection.id]:
+            out += "recipient: %s\r\n" % recipient
+        out += "length: %s\r\n\r\n" % len(self.data)
+        out += self.data
+#        print "request: %s" % out
+        return out
+        
+    def reply(self, connection, status, headers):
+        self.replies[connection.id] = status, headers
+        if len(self.replies.keys()) == len(self.requests.keys()):
+            self.complete()
+            
+    def complete(self):
+        final_headers = {
+            'msg': 'success',
+            'id': self.id,
+            'recipients': []
+        }
+        self.status = 'success'
+        for status, headers in self.replies.values():
+            if status.lower() == 'failure':
+                self.status = 'failure'
+                final_headers['msg'] = headers['msg']
+                final_headers['recipients'].extend(headers['recipient'])
+                
+        self.headers = final_headers
+        if self.status == 'success':
+            del self.headers['recipients']
+        # We are in synchronous mode...
+        if not self.callback:
+            return event.abort()
+        # We are in asynchronous mode...
+        self.callback(self)
+        
+class Connection(object):
+    def __init__(self, id, addr, port):
+        self.id = id
+        self.sock = client_socket(addr, port)
+        self.write_buffer = ""
+        self.read_buffer = ""
+        self.events = []
+        self.pending_events = {}
+        self.state = None
+        self.revent = None
+        self.wevent = None
+        
+    def close(self):
+        pass
+        
+    def event(self, event):
+        self.events.append(event)
+        self.start_write()
+        self.start_read()
+            
+    def start_write(self):
+        if not self.wevent:
+            self.next_event()
+            self.wevent = event.write(self.sock, self.write_ready)
+            
+    def start_read(self):
+        if not self.revent:
+            self.revent = event.read(self.sock, self.read_ready)
+            self.revent.add()
+        
+    def next_event(self):
+        e = self.events.pop(0)
+        self.pending_events[e.id] = e
+        self.write_buffer = e.render(self)      
+        
+    def write_ready(self):
+        bsent = self.sock.send(self.write_buffer)
+#        print "=====wrote=====\n %s" % self.write_buffer[:bsent]
+        self.write_buffer = self.write_buffer[bsent:]
+        if not self.write_buffer:
+            if not self.events:
+#                print "Wrote ALL Events"
+                self.wevent = None
+                return None
+            self.next_event()
+        return True
+        
+    def read_ready(self):
+        data = self.sock.recv(BUFFER_SIZE)
+#        print "READ: %s" % data
+        if not data:
+            self.close()
+            return None
+        self.read_buffer += data
+        if END in self.read_buffer:
+            self.process()
+#            self.state = None
+#            self.start_write()
+        return True   
+
+    def process(self):
+#        print "processing!"
+        index = self.read_buffer.find(END)
+        reply = self.read_buffer[:index]
+        self.read_buffer = self.read_buffer[index+len(END):]
+        lines = reply.split(LINE)
+        status = lines[0]
+        headers = {}
+        for line in lines[1:]:
+            key, val = line.split(': ')
+            if key == 'recipient':
+                if key in headers:
+                    headers[key].append(val)# = [ headers[key], val ]
+                else:
+                    headers[key] = [val]
+            else:
+                headers[key] = val
+#        print "FINISHED READ"
+#        print "headers: %s" % headers
+#        print "pending: %s" % self.pending_events
+        event = self.pending_events.pop(int(headers['id']))
+        event.reply(self, status, headers)
+        

File clients/python/pyorbited/io.py

+import socket
+LQUEUE_SIZE = 500
+BUFFER_SIZE = 4096
+
+def server_socket(port):
+    """Return a listening socket bound to the given interface and port.
+    """
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(("localhost", port))
+    sock.listen(LQUEUE_SIZE)
+    return sock
+
+
+def client_socket(addr, port):
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.setblocking(0)
+    sock.connect_ex((addr, port))
+    return sock    

File clients/python/pyorbited/simple.py

+import socket, io
+from simplejson.encoder import JSONEncoder
+encode = JSONEncoder().encode
+END = '\r\n'
+
+def debug(data):
+    pass
+
+class Client(object):
+    version = 'Orbit 1.0'
+    def __init__(self, addr="localhost", port=9000):
+        self.addr = addr
+        self.port = port
+        self.socket = None
+        self.id = 0
+        self.connected = False
+    
+    def connect(self):
+        self.connected = True
+        if self.socket:
+            debug("Already Connected")
+            return
+        self.socket = io.client_socket(self.addr, self.port)
+        self.socket.setblocking(1)
+    
+    def disconnect(self):
+        self.connected = False
+        self.socket.close()
+        self.socket = None
+    
+    def reconnect(self):
+        self.disconnect()
+        self.connect()
+    
+    def sendline(self, line=""):
+        self.socket.send('%s%s' % (line, END))
+    
+    def event(self, recipients, body, json=True, retry=True):
+        if not self.connected:
+            self.connect()
+        try:
+            if json:
+                body = encode(body)
+            if not self.socket:
+                raise "ConnectionLost"
+            try:
+                self.id += 1
+                self.sendline(self.version)
+                self.sendline('Event')
+                self.sendline('id: %s' % self.id)
+                for recipient in recipients:
+                    self.sendline('recipient: %s' % (str(recipient)))
+                self.sendline('length: %s' % len(body))
+                self.sendline()
+                self.socket.send(body)
+                return self.read_response()
+            except socket.error:
+                self.disconnect()
+                raise "ConnectionLost"
+        except "ConnectionLost":
+            if retry:
+                self.reconnect()
+                self.event(recipients, body, json, False)
+            else:
+                raise
+    
+    def read_response(self):
+        return self.socket.recv(io.BUFFER_SIZE)
+    

File clients/python/pyorbited/twistedorbit.py

+# Author: Michael Carter
+# Email: CarterMichael@gmail.com
+#
+# Please note that this is an incomplete implementation of the client api. As
+# of version 0.1 it works only with a single server
+from twisted.internet import reactor, defer
+from twisted.python import log
+from twisted.internet.protocol import Protocol, ClientFactory
+from twisted import internet
+import simplejson
+import sys
+
+make_json = simplejson.encoder.JSONEncoder().encode
+
+def test(c, recipients, event):
+    yoho = []
+    def test(response):
+        yoho.append(response)
+        reactor.stop()
+    c.client.event(recipients, event).addCallback(test)
+    reactor.start()
+    return yoho[0]
+
+class OrbitRequest(object):
+    def __init__(self, id, deferred, recipients, data):
+        self.id = id
+        self.deferred = deferred
+        self.recipients = recipients
+        self.data = data
+        
+    def render(self):
+        out = "Orbit 1.0\r\nEvent\r\nid: %s\r\nlength: %s\r\n" % (self.id, len(self.data))
+        for r in self.recipients:
+            out += "recipient: %s\r\n" % r
+        out += "\r\n"
+        out += self.data
+        return out
+        
+    def success(self, status, headers):
+        response = OrbitResponse(status, headers)
+        self.deferred.callback(response)
+        
+class OrbitResponse(object):
+    def __init__(self, status, headers):
+        self.status = status
+        self.headers = headers
+        
+class OrbitProtocol(Protocol):
+    def __init__(self, factory):
+        self.factory = factory
+        self.buffer = ""
+        self.write_buffer = ""
+        self.requests = dict()
+        self.pending = []
+        self.id = 0
+        
+
+    def event(self, recipients, data, jscript=True):
+        self.id+=1
+        if jscript:
+            data = make_json(data)
+        d = defer.Deferred()
+        self.requests[self.id] = OrbitRequest(self.id, d, recipients, data)
+        self.transport.write(self.requests[self.id].render())
+        return d
+        
+    def dataReceived(self, data):
+        self.buffer += data
+        self.process()
+        
+    def process(self):
+        while '\r\n\r\n' in self.buffer:
+            req, self.buffer = self.buffer.split('\r\n\r\n', 1)                
+            lines = req.split('\r\n')
+            action = lines[0]
+            headers = {}
+            recipients = []
+            for line in lines[1:]:
+                key, val = line.split(': ')
+                if key == 'recipient':
+                    recipients.append(val)
+                else:
+                    headers[key] = val
+            if recipients:
+                headers['recipients'] = recipients
+            self.requests[int(headers['id'])].success(action, headers)
+            self.requests.pop(int(headers['id']))
+            
+class OrbitFactory(ClientFactory):
+    protocol = OrbitProtocol
+    def __init__(self):
+        self.client = None
+        
+        
+    def buildProtocol(self, addr):
+        self.client = self.protocol(self)
+        return self.client
+    
+class OrbitClient(object):
+        def __init__(self, addr="localhost", port=9000):
+            self.factory = OrbitFactory()
+            reactor.connectTCP(addr, port, self.factory)
+            
+        def event(self, recipients, event, jscript=True):
+            return self.factory.client.event(recipients, event, jscript)

File clients/python/setup.py

+#from ez_setup import use_setuptools
+#use_setuptools()
+from setuptools import setup
+setup(
+    name='pyorbited',
+    version='0.1.1',
+    author='Michael Carter',
+    author_email='CarterMichael@gmail.com',
+    url='http://orbited.org',
+    download_url='http://orbited.org/download',
+    license="MIT License",
+    description='A python client for the orbited (Orbit Event Daemon), a COMET server. Includes three implementations: pyevent, twisted, basic sockets.'
+    long_description='',
+    packages=["pyorbited"],    
+    install_requires = [
+#        "event >= 0.3"
+    ],
+    
+#    entry_points = """    
+#    [console_scripts]
+#    orbited = orbited.start:main
+#    """,
+
+    classifiers = [
+        'Development Status :: 3 - Alpha',
+        'Environment :: Console',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: MIT License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Libraries :: Python Modules'],        
+    )
+    
+    
+    

File clients/ruby/ruby-orbited.rb

+require 'socket'
+include Socket::Constants
+require 'json'
+
+BUFFER_SIZE = 4096
+LINE_END = "\r\n"
+
+module SimpleOrbit
+end
+
+class SimpleOrbit::Client
+  @@version = 'Orbit 1.0'
+  def initialize(addr, port)
+    @addr = addr
+    @port = port
+    @socket = nil
+    @id = 0
+    @connected = false
+  end
+  def connect()
+    @connected = true
+    if @socket
+      # already connected
+      return
+    end
+    @socket = TCPSocket.new(@addr, @port)
+  end
+  def disconnect()
+    if @connected
+      @connected = false
+      @socket.close()
+      @socket = nil
+    end
+  end
+  def reconnect()
+    disconnect()
+    connect()
+  end
+  def sendline(line='')
+    @socket.write(line.to_s + LINE_END)
+    puts line.to_s
+  end
+  def event(recipients, body, json=true, try_again=true)
+    if not @connected
+      connect()
+    end
+    begin
+      if json
+        body = JSON.generate(body)
+      end
+      if not @socket
+        raise ## **Connection-lost**
+      end
+      begin
+        @id = @id + 1
+        sendline(@@version.to_s)
+        sendline('Event')
+        sendline('id: ' + @id.to_s)
+        for recipient in recipients
+          sendline('recipient: ' + recipient.to_s)
+        end
+        sendline('length: ' + body.length.to_s)
+        sendline()
+        @socket.write(body)
+        puts body.to_s
+        return read_response()
+      rescue
+        disconnect()
+        raise
+      end
+    rescue
+      if try_again
+          reconnect()
+          event(recipients, body, json, false)
+      else
+          raise
+      end
+    end
+  end
+  def read_response()
+    # this is incorrect. Should read until it gets a \r\n\r\n
+    return @socket.read(15)
+  end
+end

File clients/ruby/test.rb

+require 'ruby-orbited'
+
+client = SimpleOrbit::Client.new('127.0.0.1', 9000)
+client.connect()
+
+puts client.event(['user, 0, /demo'], 'Hello World!<br>', false).to_s

File daemon/LICENSE.txt

+This is the MIT license:
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2005, 2006 Michael Carter. Orbited is a trademark of Kevin Dangoor.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File daemon/orbited/LICENSE.txt

+This is the MIT license:
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2005, 2006 Michael Carter. Orbited is a trademark of Kevin Dangoor.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File daemon/orbited/__init__.py

Empty file added.

File daemon/orbited/admin.py

+
+SEP = '|'
+        
+class AdminDaemon(object):
+    def __init__(self, port, log):
+        log("startup", "Created Admin@%s" % port)
+        pass
+
+class AdminConnection(object):
+
+    def read_ready(self):
+        try:
+            data = self.sock.recv(io.BUFFER_SIZE)
+        except:
+            data = None
+        if not data:
+            self.close()
+            return None
+        return self.read(data)
+
+    def read(self, data):
+        complete = self.request.read(data)
+        if not complete:
+            return True
+        
+    
+class AdminRequest(object):
+    """
+LOG|type=all|file=log.txt
+SETTINGS|max_connections=50|timeout=300
+START_COUNTING|
+STOP_COUNTING|
+COUNT_REPORT|    
+    """
+    
+    def __init__(self):
+        self.buffer = ""
+        
+    def read(self, data):
+        self.buffer += data
+        return self.process()
+        
+    def process(self):
+        if '\r\n' not in self.buffer:
+            return False        
+        index = self.buffer.find('\r\n')
+        self.line = self.buffer[:index]
+        self.buffer = self.buffer[index+2:]
+        index = self.line.find('SEP')   
+        self.cmd = self.line[:index].lower()
+        #TODO: Lower case the keys
+        self.details = dict([ i.split('=') for i in self.line[index+1:].split('|') ])
+        return True
+
+
+class AdminApp(object):
+    
+    def __init__(self, main_app, port):
+        self.main_app = main_app
+        self.log = main_app.log
+        self.daemon = AdminDaemon(port, self.log)
+        self.request = None
+        
+    def incoming_request(self, request):
+        getattr(self, 'cmd_%s' % request.cmd)(request)
+        
+        
+    def cmd_start_counting(self, request):
+        self.app.logger
+        pass
+        
+        
+    def __call__(self, action, details):
+        pass

File daemon/orbited/app.py

+from orbit import OrbitDaemon
+from http import HTTPDaemon
+import event
+import sys
+from debug import *
+import log
+from config import map as config
+from admin import AdminApp
+import transport
+import traceback
+import StringIO
+#config = config.map
+"""
+app:    
+    stores all requests
+    executes requests
+    replies to requests
+   
+   
+daemon:
+    accepts socket connections. creates connection objects
+    
+connection:
+    reads data and creates new requests as needed
+request:
+    processes read data and stores info about a request
+    
+"""
+
+class App(object):
+
+    def __init__(self):
+        self.requests = dict()
+        self.connections = dict()
+        log.create(self)
+        self.log = log.log
+        self.admin = None
+        if config['[global]']['admin.enabled'] == '1':
+            self.admin = AdminApp(self, int(config['[admin]']['admin.port']))
+        self.daemon = OrbitDaemon(self, self.log, int(config['[global]']['orbit.port']))
+#        if int(config['proxy']):
+        self.http = HTTPDaemon(self, self.log, int(config['[global]']['http.port']), config['[proxy]'])
+        transport.load_transports()
+        
+    def accept_orbit_request(self, request):
+        self.log("ACCESS", "ORBIT\t%s/%s\t%s" % (request.connection.addr[0], request.id, request.length))
+        self.requests[request.key()] = request
+        self.dispatch_request(request)
+
+        
+    def dispatch_request(self, request):
+        for recipient in request.recipients:
+            if recipient in self.connections:
+                self.connections[recipient].respond(request)
+            else:
+                self.log("ERROR", "ORBIT\t%s/%s\tRecipient %s not Found" % (request.connection.addr[0], request.id, recipient))
+                request.error(recipient)
+                    
+        self.requests.pop(request.key())
+        
+    def accept_http_connection(self, connection):
+        try:
+            if connection.key() not in self.connections:
+                self.connections[connection.key()] = transport.create(connection, self)        
+                
+            elif connection.request.transport_name != self.connections[connection.key()].name:
+                print "TRANSPORT EXISTS. Switching: %s -> %s" % (self.connections[connection.key()].name, connection.request.transport_name)
+                self.connections[connection.key()].close()
+                self.connections[connection.key()] = transport.create(connection, self)
+            self.connections[connection.key()].accept_http_connection(connection)
+#            self.log('close_http_connection', self.connections[connection.key()]) 
+#            self.connections[connection.key()].close()
+        except"InvalidTransport":                
+            self.log("ERROR", "HTTP\t%s\tTransport \"%s\" Not Found" % (connection.addr[0], connection.request.transport_name))
+            connection.close()
+            return
+
+    def expire_http_connection(self, connection):
+        if connection.key() in self.connections:
+            self.connections[connection.key()].expire_http_connection(connection)
+        
+    def start(self):
+    
+        def collect_toplevel_exceptions():
+            return True
+            
+        event.timeout(1, collect_toplevel_exceptions)
+        while True:
+            try:
+                event.dispatch()
+            except KeyboardInterrupt, k:
+                event.abort()
+                print "Received Ctr+c shutdown"
+                break
+            except Exception, e:
+                exception, instance, tb = traceback.sys.exc_info()
+
+                # Start: There is certainly a better way of doing this
+                x = StringIO.StringIO()
+                traceback.print_tb(tb, file=x)
+                relevant_line = x.getvalue().split('\n')[-3]
+                # End: Find a better way
+                
+                self.log("ERROR", "%s:%s\t%s" % (exception, instance, relevant_line))
+            
+if __name__ == "__main__":
+    a = App()
+    a.start()
+    

File daemon/orbited/config.py

+map = {
+    '[global]': {
+        'orbit.port': '9000',
+        'http.port': '8000',
+        'admin.enabled': '0',
+        'proxy.enabled': '0',
+        'proxy.keepalive': 0,
+        'proxy.keepalive_default_timeout': 300,
+        'log.enabled': '1'        
+    },
+    '[admin]': {
+        'admin.port': '9001'
+    },
+    '[proxy]': [        
+    ],
+    '[log]': {
+        'log.access': ('screen',),
+        'log.error': ('screen',),
+        'log.event': ('screen',)
+    }
+}
+
+def update(**kwargs):
+    map.update(kwargs)
+    return True
+
+def load(filename):
+    try:
+        f = open(filename)
+        lines = [ i.strip() for i in f.readlines() ]
+        
+    except IOError:    
+        print filename, "could not be found. Using default configuration"
+        return False
+        #lines = default.split('\n')
+                
+    section = None
+    for line in lines:
+        line = line.strip(' ')        
+        if '#' in line:
+            line, comment = line.split('#', 1)
+        if not line:
+            continue
+        if line.startswith('[') and line.endswith(']'):
+            section = line
+            if section not in map:
+                map[section]  = {}
+            continue            
+            
+        if section == "[proxy]":
+            source, target = line.split('->')
+            source = source.strip(' ')
+            target = target.strip(' ')
+            map[section].append((source, target))
+            continue
+        if '=' not in line:
+            continue
+#        line = line.replace(' =', '=')
+#        line = line.replace('= ', '=')        
+
+        key, value = line.split('=')
+        key = key.strip(' ')
+        value = value.strip(' ')
+        if section == "[log]":
+            if ',' in value:
+                values = value.split(',')
+                value = values[0].strip(' '), values[1].strip(' ')
+            else:
+                value = value,
+        if section not in map:
+            map[section] = dict()
+        map[section][key] = value
+    print "CONFIG"
+    print map
+    return True
+    

File daemon/orbited/debug.py

+import traceback
+import sys
+
+
+def debug(*args):
+    return
+    if len(args) == 1:
+        print args[0]
+    else:
+        print args
+#    print data
+    
+    
+    
+def tbinfo():
+    return traceback.print_stack()
+    try:
+        raise Exception("TBINFO")
+    except:
+        a,b,tb = sys.exc_info()
+        traceback.print_tb(tb)
+        return
+    

File daemon/orbited/http.py

+import io
+import event
+import random
+import transport
+import static
+from debug import *
+def debug(arg):
+    return
+    print arg
+    
+from proxy2 import Proxy
+from config import map as config
+class ProxyChecker(object):
+    def __init__(self, rules):
+        self.rules = rules
+        
+    def __call__(self, url):
+        if '|' in url:
+            return False
+        if url.startswith('/_/'):
+            
+            data = getattr(static, url[3:], None)
+            if data:
+                raise "StaticContent", data
+        if config['[global]']['proxy.enabled'] != '1':
+            raise "ProxyDisabled", url
+        for match, target in self.rules:
+            if url.startswith(match):
+                port = 80
+                addr = None
+                if '//' in target:
+                    addr = target.split('//', 1)[1]
+                if ':' in addr:
+                    addr, port = addr.split(':', 1)                
+                return (addr, int(port))
+        raise "NoDestination", url
+        
+
+class HTTPDaemon(object):
+    def __init__(self, app, log, port, rules):
+        log("startup", "Created http@%s" % port)
+        self.log = log
+        sock = io.server_socket(port)
+        self.app = app
+        self.listen = event.event(self.accept_connection, handle=sock, evtype=event.EV_READ | event.EV_PERSIST)
+        self.listen.add()
+        self.proxy_checker = ProxyChecker(rules)
+        self.keepalive_enabled = (config['[global]']['proxy.keepalive'] == '1')
+        
+        
+    def accept_connection(self, ev, sock, event_type, *arg):
+        sock, addr = sock.accept()
+#        self.log("ACCESS", "http", *addr)
+
+        HTTPConnection(self.app, self.log, self.proxy_checker, sock, addr, self.keepalive_enabled)
+        
+        
+class HTTPConnection(object):
+
+    def debug(self, data):
+        return
+        print "%s %s" % (self, data)
+    def __str__(self):
+        return "<HTTPConnection %s>" % self.id
+    id = 0
+    def __init__(self, app, log, proxy_checker, sock, addr, keepalive_enabled):
+        self.keepalive_enabled = keepalive_enabled
+        HTTPConnection.id += 1
+        self.id = HTTPConnection.id 
+        self.keepalive_timer = None
+        self.proxy_id = 0
+        self.debug("NEW Connection")
+        self.sock = sock
+        self.addr = addr
+        self.app = app
+        self.log = log
+        self.proxy_checker = proxy_checker
+        self.events = []
+        self.proxy = None
+        self.finish_close = False
+        
+        self.setup()
+        
+    def setup(self):
+        self.revent = event.read(self.sock, self.read_ready)
+        self.wevent = None
+        self.buffer = ""
+        self.write_buffer = ""
+        self.request = HTTPRequest(self.log, self.proxy_checker)
+        self.current_response = None
+        
+    def proxy_complete(self, proxy, complete=False):
+        if complete:
+            self.close()
+        else:
+            self.set_keepalive_timer()
+            self.setup()
+        
+        
+    def proxy_expired(self, proxy):
+        self.debug("proxy expired")
+        if self.proxy == proxy:
+            self.proxy = None
+            
+#    def proxy_complete(self, proxy):
+#        self.debug("Proxy Completed!")
+#        self.set_keepalive_timer()
+#        self.setup()
+        
+
+    def close(self):
+#        tbinfo()
+        if hasattr(self, 'proxy') and self.proxy:
+            self.proxy.close_server()
+        self.debug("CONNECTION CLOSED")
+        self.sock.close()
+        if self.revent:
+            self.revent.delete()
+            self.revent = None
+        if self.wevent:
+            self.wevent.delete()
+            self.wevent = None
+        if hasattr(self.request, 'user_id'):
+            # This is an orbited connection that closed...
+            self.app.expire_http_connection(self)
+            
+            
+        
+    def write_ready(self):
+        if not self.write_buffer:
+            if self.current_response:
+                self.current_response.success(self)
+                self.current_response = None
+            if not self.events:
+                
+                self.wevent = None
+                if self.finish_close:
+                    self.close()
+                return None
+            self.current_response = self.events.pop(0)
+            self.write_buffer = self.current_response.data        
+        try:
+            bsent = self.sock.send(self.write_buffer)
+            debug("http sent: %s" % self.write_buffer[:bsent])
+            self.write_buffer = self.write_buffer[bsent:]
+            return True
+        except io.socket.error:
+            self.current_response.failure(self)
+            self.close()
+            return None
+
+    def respond(self, response):
+        self.events.append(response)
+        if not self.wevent:
+            self.wevent = event.write(self.sock, self.write_ready)
+
+            
+    def read_ready(self):
+        try:
+            data = self.sock.recv(io.BUFFER_SIZE)
+            if not data:
+                self.close()
+                return None
+            return self.read(data)
+        except io.socket.error:
+            self.close()
+            return None
+
+    def read(self, data):
+        try:
+            proxy_info =  self.request.read(data)
+            # print 'a'
+        except "NoDestination", url:
+            self.complete = True
+            self.log("ERROR", "PROXY\tNo proxy rule matches: " + url)
+            return self.close()
+        except "ProxyDisabled", url:
+            self.complete = True
+            self.log("ERROR", "PROXY\tProxy disabled. No match: " + url)
+            return self.close()
+        except "StaticContent", data:
+            self.write_buffer = data
+            self.wevent = event.write(self.sock, self.write_ready)
+            self.finish_close = True
+            return
+        if proxy_info:
+            addr, port = proxy_info
+            self.log("ACCESS", "PROXY\t%s -> http://%s:%s" % (self.request.headers['url'],addr, port))
+            a = self.request.action + "\r\n" + "\r\n".join([ ": ".join(i) for i in self.request.headers.items()]) + "\r\n\r\n"
+            
+            if self.proxy:
+                # print " use old proxy"
+                if (self.proxy.addr, self.proxy.port) == (addr, port):
+                    # print "go!"
+                    id_parts = self.proxy.id.split('.')
+                    id_parts[2] = str(int(id_parts[2]) + 1)
+                    self.proxy.id = '.'.join(id_parts)
+                    self.proxy.next_request(self.sock, a)
+                    self.revent.delete()
+                    self.revent = None
+                else:
+                    # print "old proxy is to the wrong server"
+                    self.proxy.close_server()
+                    self.proxy = None
+            if not self.proxy:
+                # print "No proxy.. create one"
+                self.keepalive_timeout = 0
+                self.debug("headers: %s" % (self.request.headers,))
+                self.debug("Checking Keep-alive")
+                self.debug("Connection: %s" % self.request.headers.get("connection", "Close"))
+                if self.request.headers.get("connection", "Close").lower() == "keep-alive" and self.keepalive_enabled:
+                    # print "server says keepalive"
+                    self.debug("Keep-alive: %s" % self.request.headers.get("keep-alive", -1))
+                    self.keepalive_timeout = int(self.request.headers.get("keep-alive", config['[global]']['proxy.keepalive_default_timeout']))
+                self.proxy_id += 1
+                id = str(self.id) + '.' + str(self.proxy_id) + '.' + '1'
+                self.proxy = Proxy(self, addr, port, self.sock, buffer=a, id=id, keepalive=self.keepalive_enabled )
+                self.set_keepalive_timer()
+                self.revent.delete()
+                self.revent = None
+            return None
+        elif self.request.complete:
+            self.log("ACCESS", "HTTP\t(%s, %s, %s)" % self.key())        
+            self.app.accept_http_connection(self)
+            return None
+        return True
+
+    def set_keepalive_timer(self):
+        return
+        if self.keepalive_timer:
+            self.keepalive_timer.delete()
+        self.keepalive_timer = event.timeout(self.keepalive_timeout, self.keepalive_timeout)
+    
+    def keepalive_timeout(self):
+        if self.proxy:
+            self.proxy.close_client()
+            self.proxy.close_server()
+        self.close()
+        
+    def key(self):
+        return self.request.key()
+
+        
+
+class HTTPRequest(object):
+
+
+    def __init__(self, log, proxy_checker):
+        self.buffer = ""
+        self.state = "action"
+        self.headers = {}
+        self.complete = False
+        self.log = log
+        self.proxy_checker = proxy_checker
+        
+    def read(self, data):
+        self.buffer += data
+        return self.process()
+        
+    def process(self):
+        return getattr(self, 'state_%s' % self.state)()
+        
+    def state_action(self):
+        if '\r\n' not in self.buffer:
+            return
+        i = self.buffer.find('\r\n')
+        self.action = self.buffer[:i]
+        debug("action: %s" % self.action)
+        self.buffer = self.buffer[i+2:]
+        self.state = "headers"
+        self.headers['url'] = self.action.split(' ')[1]
+        return self.state_headers()
+        
+    def state_headers(self):
+        while True:
+            index = self.buffer.find('\r\n')
+            if index == -1:
+                break;
+            if index == 0:
+                self.buffer = self.buffer[2:]
+                self.state="completed"
+                return self.state_completed()
+            debug("line: %s" % self.buffer[:index])
+#            debug("split: %s" % self.buffer[:index].split(': '))
+            key, value = self.buffer[:index].split(': ')
+            self.headers[key.lower()] = value
+            self.buffer = self.buffer[index+2:]
+
+    def state_completed(self):
+        target_addr = self.proxy_checker(self.headers['url'])
+        if target_addr:
+            debug("target_addr: " + str(target_addr))
+            return target_addr 
+        else:
+            self.location, identifiers = self.headers['url'].split('|')
+            self.transport_name = 'iframe'
+            if ',' not in identifiers:
+                self.user_id = identifiers
+                self.session_id = '0'
+            else:
+                self.user_id, self.session_id = identifiers.split(',', 1)
+            if ',' in self.session_id:
+                self.session_id, self.transport_name = self.session_id.split(',', 1)
+            if ',' in self.transport_name:
+                self.transport_name, self.extra = self.transport_name.split(',', 1)
+            self.complete = True
+            debug("completed")
+            
+    def key(self):
+        return self.user_id, self.session_id, self.location
+        
+#    def state_body():
+#        if len(self.buffer) < int(self.headers['content-length']):
+#            return
+#        self.body = self.buffer[:self.headers['content-length']
+        # TODO: Exception if there's more data. when would this happen?
+#        return True
+        
+#            if extra:
+#                self.state = "body"
+#            self.buffer = extra
+#        elif state == "body":
+#            self.buffer += data
+#            if len(self.buffer) >= request['content-length']:
+
+    

File daemon/orbited/io.py

+import socket
+LQUEUE_SIZE = 500
+BUFFER_SIZE = 4096
+
+HTTP_DELIMITER = "\r\n"
+HTTP_DELIM_LENGTH = len(HTTP_DELIMITER)
+def server_socket(port):
+    """Return a listening socket bound to the given interface and port.
+    """
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(("", port))
+    sock.listen(LQUEUE_SIZE)
+    return sock
+
+
+def client_socket(addr, port):
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.setblocking(0)
+    sock.connect_ex((addr, port))
+    return sock    

File daemon/orbited/log.py

+from config import map as config
+from datetime import datetime
+
+global log
+log = None
+class Logger(object):
+    
+    def __init__(self, app):
+        self.app = app
+        self.enabled = True
+        if config["[global]"]["log.enabled"] != '1':
+            self.enabled = False
+            return
+        self.process = self._process
+        self.events = []
+        self.access, self.event, self.error = None, None, None
+        self.screen = []
+        self.load(config["[log]"])
+        
+    def __call__(self, *args):
+        self._process(*args)
+        
+    def load(self, options):        
+        for type in 'access', 'error', 'event':
+            for item in options['log.%s' % type]:
+                if item == 'screen':
+                    self.screen.append(type)
+                else:
+                    setattr(self, type, open(item, 'a'))
+        
+        
+    def _process(self, context, msg, severity=5):
+        if not self.enabled:
+            return
+        if context == "startup":
+            print msg#[0]
+        file = getattr(self, context.lower(), None)
+        time = str(datetime.now())[:-3]
+        out = "\t".join([time, context, msg])
+        if file:
+        
+            file.write('%s\n' % out)
+            file.flush()            
+        if context.lower() in self.screen:
+            print out
+    
+def create(app):
+    global log
+    log = Logger(app)
+    

File daemon/orbited/orbit.py

+import io
+import event
+from debug import *
+
+class OrbitDaemon(object):
+
+    def __init__(self, app, log, port):
+        log("startup", "Created Orbit@%s" % port)
+        self.log = log
+        self.index = 0
+        self.port = port
+        self.app = app
+        self.sock = io.server_socket(port)
+        self.listen = event.event(self.accept_connection, 
+            handle=self.sock, evtype=event.EV_READ | event.EV_PERSIST)
+        self.listen.add()
+        
+    def accept_connection(self, event_, sock, event_type, *arg):
+        self.index+=1
+        connection = OrbitConnection(sock.accept(), self.index, self.app, self.log)
+        
+        
+class OrbitConnection(object):
+    def __init__(self, (sock, addr), id, app, log):
+        self.log = log
+        debug("Accepting Orbit Connection [id: %s ] from %s on port %s" % ((id,) + addr))
+        self.log("ACCESS", "ORBIT\t[id: %s]\t%s\t%s" % ((id,) + addr))
+        self.id = id
+        self.app = app
+        self.addr = addr
+        self.sock = sock
+        self.revent = event.read(sock, self.read_data)
+        self.wevent = None
+        self.response_queue = []
+        self.write_buffer = ""
+        self.request = Request(self, self.log)
+        
+    def close(self):
+        debug("Closing Orbit Connection [id: %s ] from %s on port %s" % ((self.id,) + self.addr))
+        self.wevent = None
+        self.revent = None
+        self.sock.close()
+        
+        
+        
+    def read_data(self):
+        try:
+            data = self.sock.recv(io.BUFFER_SIZE)
+        except:
+            data = None
+        if not data:
+            self.close()
+            return None
+        try:
+            self.request.read_data(data)
+        except RequestComplete, ex:
+            debug("Request finished. Start a new one")
+            self.app.accept_orbit_request(self.request)
+            self.request = Request(self, self.log, ex.leftover_buffer)   
+            while True:
+                try:
+                    self.request.process()
+                    break
+                except RequestComplete, ex:
+                    self.app.accept_orbit_request(self.request)
+                    self.request = Request(self, self.log, ex.leftover_buffer)
+        return True
+        
+    def write_data(self):
+        try:
+            bsent = self.sock.send(self.write_buffer)
+            debug("wrote:\n======\n%s" % self.write_buffer[:bsent])
+            self.write_buffer = self.write_buffer[bsent:]
+            return self.write_next()
+        except io.socket.error:
+            return None
+            
+    def write_next(self):
+        if not self.write_buffer:
+            if not self.response_queue:
+                self.wevent = None
+                return None
+            else:
+                self.write_buffer = self.response_queue.pop(0)
+                if not self.wevent:
+                    self.wevent = event.write(self.sock, self.write_data)
+                return True
+                    
+    def respond(self, response):
+        self.response_queue.append(response)
+        self.write_next()
+        
+from debug import *
+import random
+
+
+class RequestComplete(Exception):
+
+    def __init__(self, leftover_buffer):
+        self.leftover_buffer = leftover_buffer
+        Exception.__init__(self)
+
+
+    
+class Request(object):
+
+    def __init__(self, connection, log, buffer=""):
+        self.log = log
+        self.connection = connection
+        self.version = None
+        self.type = None
+        self.id = None
+        self.recipients = []
+        self.replies = {}
+        self.state = "version"
+        self.buffer = buffer
+        
+    def key(self):
+        if not self.id:
+            raise "NoKey"        
+        return self.connection.id, self.id
+        
+        
+    def read_data(self, data):
+#        print "read:\n=====\n %s" % data
+        self.buffer += data
+        self.process()
+            
+    def process(self):
+        if self.state == 'body':
+            if len(self.buffer) < self.length:
+                return
+            self.body = self.buffer[:self.length]
+            debug("body: %s" % self.body)
+            self.buffer = self.buffer[self.length:]
+            raise RequestComplete(self.buffer)
+            
+        if '\r\n' in self.buffer:
+            i = self.buffer.find('\r\n')
+            line = self.buffer[:i]
+            self.buffer = self.buffer[i+2:]
+            getattr(self, 'state_%s' % self.state)(line)
+            self.process()
+            
+        
+    def state_version(self, line):
+        debug("version: %s" % line)
+        self.version = line
+        self.state = 'type'
+        
+    def state_type(self, line):
+        debug("type: %s" % line)
+        self.type = line
+        self.state = "headers"
+        
+    def state_headers(self, line):
+        debug("header: %s" % line)
+        if line == '':
+            self.state = 'body'
+            return
+        name, content = line.split(': ')
+        name = name.lower()
+        if name == 'id':
+            self.id = content
+        elif name == 'recipient':
+            self.recipients.append(tuple(content.split(', ')))
+        elif name == 'length':
+            self.length = int(content)
+
+    def error(self, recipient_key):
+        recipient = "(%s, %s, %s)" % recipient_key
+        self.replies[recipient] = 0
+        if len(self.replies.keys()) == len(self.recipients):
+            self.reply()
+            
+    def success(self, recipient_key):
+        recipient = "(%s, %s, %s)" % recipient_key
+        self.replies[recipient] = 1
+        if len(self.replies.keys()) == len(self.recipients):
+            self.reply()
+
+    def reply(self):
+        if len(self.recipients) == sum(self.replies.values()):
+            self.reply_success()
+        else:
+            self.reply_failure()
+    
+    def reply_success(self):
+        response = "Success\r\nid: %s\r\n\r\n" % self.id
+        self.connection.respond(response)
+        
+    def reply_failure(self):
+        response = "Failure\r\nid: %s\r\nmsg: Failed to reach one or more recipients\r\n" % self.id
+        for recipient, success in self.replies.items():
+            if not success:
+                response += "recipient: %s\r\n" % (recipient,)
+        response += "\r\n"
+        self.connection.respond(response)
+