move example to different port
[multitaskhttpd.git] / ProxyServer.py
index 5f1a731f7eec9b471eed63953fd5e0927e8f3974..4816cdc37c1d27a2cb44063ca29969ebf9300d48 100644 (file)
@@ -44,6 +44,7 @@ class ProxyConnection:
     auto_open = 1
     debuglevel = 0
     strict = 0
+    serving = False
 
     def __init__(self):
         self.sock = None
@@ -120,147 +121,197 @@ class ProxyServerRequestHandler(object):
 
     """
 
+    debuglevel = 0
     server_version = "SimpleHTTP/" + __version__
 
     def on_query(self, client, reqtype, *args):
         """Serve a request."""
+
         self.client = client
         self.hr = args[0]
-        print "on_query", reqtype, repr(self.hr.headers), str(self.hr.headers)
-        if not hasattr(self.client, "proxy"):
-            print "new proxy"
-            self.client.proxy = ProxyConnection()
-            self.client.proxy.connect()
+        if self.debuglevel > 0:
+            print "on_query", reqtype, repr(self.hr.headers), \
+                              str(self.hr.headers)
+        session = self.client.session 
+        p = self.proxies.get(session, None)
+        if not p:
+            proxy = ProxyConnection()
+            proxy.connect()
+            self.proxies[session] = proxy
 
-        multitask.add(self.proxy_relay(reqtype))
+        yield self.proxy_relay(reqtype)
 
-        return True
+        #while p.serving:
+        #    (yield multitask.sleep(0.01))
+
+        raise StopIteration
 
     def onPOST(self, client, *args):
         """Serve a POST request."""
-        return self.on_query(client, "POST", *args)
+        yield self.on_query(client, "POST", *args)
 
     def onGET(self, client, *args):
         """Serve a GET request."""
-        return self.on_query(client, "GET", *args)
+        yield self.on_query(client, "GET", *args)
 
     def proxy_relay(self, reqtype):
 
-        p = self.client.proxy 
-
-        # send command
-        req = "%s %s %s\n" % (reqtype, self.hr.path, self.hr.request_version)
-        print "req", req
-        yield p.ss.write(req)
-
-        # send headers
-        hdrs = str(self.hr.headers)
-        print "hdrs", hdrs
-        yield p.ss.write(hdrs)
-        yield p.ss.write('\r\n')
-
-        conntype = self.hr.headers.get('Connection', "")
-        keepalive = conntype.lower() == 'keep-alive'
-
-        # now content
-        if self.hr.headers.has_key('content-length'):
-            max_chunk_size = 10*1024*1024
-            size_remaining = int(self.hr.headers["content-length"])
-            L = []
-            print "size_remaining", size_remaining
-            while size_remaining:
-                chunk_size = min(size_remaining, max_chunk_size)
-                data = self.hr.rfile.read(chunk_size)
-                print "proxy rfile read", repr(data)
-                yield multitask.send(p.sock, data)
-                size_remaining -= len(data)
-
-        # now read response and write back
-        # HTTP/1.0 200 OK status line etc.
-        line = (yield p.ss.readline())
-        yield self.client.writeMessage(line)
-
-        res = ''
-        try:
-            while 1:
-                line = (yield p.ss.readline())
-                print "reading from proxy", repr(line)
-                res += line
-                if line in ['\n', '\r\n']:
-                    break
-        except StopIteration:
-            if httpd._debug: print "proxy read stopiter"
-            # TODO: close connection
-        except:
-            if httpd._debug:
-                print 'proxy read error', \
-                      (traceback and traceback.print_exc() or None)
-            # TODO: close connection
-
-        f = StringIO(res)
-
-        # Examine the headers and look for a Connection directive
-        respheaders = mimetools.Message(f, 0)
-        print "response headers", str(respheaders)
-        remote = self.client.remote
-        rcooks = httpd.process_cookies(respheaders, remote, "Set-Cookie", False)
-        rcooks['session'] = self.hr.response_cookies['session'].value # nooo
-        rcooks['session']['expires'] = \
-                self.hr.response_cookies['session']['expires']
-        self.hr.response_cookies = rcooks
-        print "rcooks", str(rcooks)
-
-        # send all but Set-Cookie headers
-        del respheaders['Set-Cookie'] # being replaced
-        yield self.client.writeMessage(str(respheaders))
-
-        # now replacement cookies
-        for k, v in rcooks.items():
-            val = v.output()
-            yield self.client.writeMessage(val+"\r\n")
-
-        # check connection for "closed" header
-        if keepalive:
-            conntype = respheaders.get('Connection', "")
-            if conntype.lower() == 'close':
-                self.hr.close_connection = 1
-            elif (conntype.lower() == 'keep-alive' and
-                  self.hr.protocol_version >= "HTTP/1.1"):
-                self.hr.close_connection = 0
-
-        # write rest of data
-        print "writing to client body"
-        yield self.client.writeMessage("\r\n")
-
-        if respheaders.has_key('content-length'):
-            max_chunk_size = 10*1024*1024
-            size_remaining = int(respheaders["content-length"])
-            while size_remaining:
-                chunk_size = min(size_remaining, max_chunk_size)
-                data = (yield p.ss.read(chunk_size))
-                print "reading from proxy expecting", size_remaining, repr(data)
-                yield self.client.writeMessage(data)
-                size_remaining -= len(data)
-        else:
-            while True:
-                #data = p.read()
-                try:
-                    data = (yield p.ss.read(1024))
-                except httpd.ConnectionClosed:
-                    break
-                print "reading from proxy", repr(data)
-                if data == '':
-                    break
-                yield self.client.writeMessage(data)
+        session = self.client.session 
+        p = self.proxies[session]
 
+        p.serving = True
 
-        if self.hr.close_connection:
-            print 'proxy wants client to close_connection'
+        try:
+            # send command
+            req = "%s %s %s\n" % (reqtype, self.hr.path, "HTTP/1.1")
+            if self.debuglevel > 0:
+                print "req", req
+            yield p.ss.write(req)
+
+            conntype = self.hr.headers.get('Connection', "")
+            keepalive = conntype.lower() == 'keep-alive'
+
+            self.hr.headers['Connection'] = 'keep-alive'
+            self.hr.close_connection = 0
+
+            # send headers
+            hdrs = str(self.hr.headers)
+            if self.debuglevel > 0:
+                print "hdrs", hdrs
+            yield p.ss.write(hdrs)
+            yield p.ss.write('\r\n')
+
+            # now content
+            if self.hr.headers.has_key('content-length'):
+                max_chunk_size = 10*1024*1024
+                size_remaining = int(self.hr.headers["content-length"])
+                L = []
+                if self.debuglevel > 0:
+                    print "size_remaining", size_remaining
+                while size_remaining:
+                    chunk_size = min(size_remaining, max_chunk_size)
+                    data = self.hr.rfile.read(chunk_size)
+                    if self.debuglevel > 0:
+                        print "proxy rfile read", repr(data)
+                    yield multitask.send(p.sock, data)
+                    size_remaining -= len(data)
+
+            # now read response and write back
+            # HTTP/1.0 200 OK status line etc.
+            responseline = (yield p.ss.readline())
+            yield self.client.writeMessage(responseline)
+
+            res = ''
             try:
-                yield self.client.connectionClosed()
-            except httpd.ConnectionClosed:
-                print 'close_connection done'
-                pass
+                while 1:
+                    line = (yield p.ss.readline())
+                    if self.debuglevel > 0:
+                        print "reading from proxy", repr(line)
+                    res += line
+                    if line in ['\n', '\r\n']:
+                        break
+            except StopIteration:
+                if self.debuglevel > 0:
+                    print "proxy read stopiter"
+                # TODO: close connection
+            except:
+                if self.debuglevel > 0:
+                    print 'proxy read error', \
+                          (traceback and traceback.print_exc() or None)
+                # TODO: close connection
+
+            f = StringIO(res)
+
+            # Examine the headers and look for a Connection directive
+            respheaders = mimetools.Message(f, 0)
+            if self.debuglevel > 0:
+                print "response headers", str(respheaders)
+            remote = self.client.remote
+            rcooks = httpd.process_cookies(respheaders, remote, "Set-Cookie", False)
+            rcooks['session'] = self.hr.response_cookies['session'].value # nooo
+            rcooks['session']['expires'] = \
+                    self.hr.response_cookies['session']['expires']
+            self.hr.response_cookies = rcooks
+            if self.debuglevel > 0:
+                print "rcooks", str(rcooks)
+
+            # override connection: keep-alive hack
+            #responseline = responseline.split(" ")
+            #print "responseline:", responseline
+            #if responseline[1] != "200":
+            #    respheaders['Connection'] = 'close'
+
+            # send all but Set-Cookie headers
+            del respheaders['Set-Cookie'] # being replaced
+            yield self.client.writeMessage(str(respheaders))
+
+            # now replacement cookies
+            for k, v in rcooks.items():
+                val = v.output()
+                yield self.client.writeMessage(val+"\r\n")
+
+            # check connection for "closed" header
+            if keepalive:
+                conntype = respheaders.get('Connection', "")
+                if conntype.lower() == 'close':
+                    self.hr.close_connection = 1
+                elif (conntype.lower() == 'keep-alive' and
+                      self.hr.protocol_version >= "HTTP/1.1"):
+                    self.hr.close_connection = 0
+
+            # write rest of data
+            if self.debuglevel > 0:
+                print "writing to client body"
+            yield self.client.writeMessage("\r\n")
+
+            if respheaders.has_key('content-length'):
+                max_chunk_size = 10*1024*1024
+                size_remaining = int(respheaders["content-length"])
+                while size_remaining:
+                    chunk_size = min(size_remaining, max_chunk_size)
+                    data = (yield p.ss.read(chunk_size))
+                    if self.debuglevel > 0:
+                        print "reading from proxy expecting", \
+                                    size_remaining, repr(data)
+                    yield self.client.writeMessage(data)
+                    size_remaining -= len(data)
+            else:
+                while True:
+                    #data = p.read()
+                    try:
+                        data = (yield p.ss.read(1024))
+                    except httpd.ConnectionClosed:
+                        break
+                    if self.debuglevel > 0:
+                        print "reading from proxy", repr(data)
+                    if data == '':
+                        break
+                    yield self.client.writeMessage(data)
+
+            if not keepalive: #self.hr.close_connection:
+                if self.debuglevel > 0:
+                    print 'proxy wants client to close_connection'
+                try:
+                    yield self.client.connectionClosed()
+                    p.serving = False
+                    raise httpd.ConnectionClosed
+                except httpd.ConnectionClosed:
+                    if self.debuglevel > 0:
+                        print 'close_connection done'
+                    pass
+
+            p.serving = False
+        except httpd.ConnectionClosed:
+            # whoops, remote end has died: remove client and
+            # remove proxy session, we cannot do anything else,
+            # there's nothing there to talk to.
+            self.client.removeConnection()
+            self.proxies.pop(session)
 
+        except:
+            if self.debuglevel > 0:
+                print traceback.print_exc()
+            
+        p.serving = False
         raise StopIteration