-add proxy
[oweals/gnunet.git] / src / gns / proxy / proxy.py
1 #!/usr/bin/python
2
3 __doc__ = """Tiny HTTP Proxy.
4
5 This module implements GET, HEAD, POST, PUT and DELETE methods
6 on BaseHTTPServer, and behaves as an HTTP proxy.  The CONNECT
7 method is also implemented experimentally, but has not been
8 tested yet.
9
10 Any help will be greatly appreciated.   SUZUKI Hisao
11 """
12
13 __version__ = "0.2.1"
14
15 import BaseHTTPServer, select, socket, SocketServer, urlparse, re
16
17 class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler):
18     __base = BaseHTTPServer.BaseHTTPRequestHandler
19     __base_handle = __base.handle
20
21     server_version = "TinyHTTPProxy/" + __version__
22     rbufsize = 0                        # self.rfile Be unbuffered
23     host_port = ()
24
25     def handle(self):
26         (ip, port) =  self.client_address
27         if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
28             self.raw_requestline = self.rfile.readline()
29             if self.parse_request(): self.send_error(403)
30         else:
31             self.__base_handle()
32
33     def _connect_to(self, netloc, soc):
34         i = netloc.find(':')
35         if i >= 0:
36             self.host_port = netloc[:i], int(netloc[i+1:])
37         else:
38             self.host_port = netloc, 80
39         print "\t" "connect to %s:%d" % self.host_port
40         try: soc.connect(self.host_port)
41         except socket.error, arg:
42             try: msg = arg[1]
43             except: msg = arg
44             self.send_error(404, msg)
45             return 0
46         return 1
47
48     def do_CONNECT(self):
49         soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
50         try:
51             if self._connect_to(self.path, soc):
52                 self.log_request(200)
53                 self.wfile.write(self.protocol_version +
54                                  " 200 Connection established\r\n")
55                 self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
56                 self.wfile.write("\r\n")
57                 self._read_write(soc, 300)
58         finally:
59             print "\t" "bye"
60             soc.close()
61             self.connection.close()
62
63     def do_GET(self):
64         (scm, netloc, path, params, query, fragment) = urlparse.urlparse(
65             self.path, 'http')
66         if scm != 'http' or fragment or not netloc:
67             self.send_error(400, "bad url %s" % self.path)
68             return
69         soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
70         try:
71             if self._connect_to(netloc, soc):
72                 self.log_request()
73                 soc.send("%s %s %s\r\n" % (
74                     self.command,
75                     urlparse.urlunparse(('', '', path, params, query, '')),
76                     self.request_version))
77                 self.headers['Connection'] = 'close'
78                 del self.headers['Proxy-Connection']
79                 for key_val in self.headers.items():
80                     soc.send("%s: %s\r\n" % key_val)
81                 soc.send("\r\n")
82                 self._read_write(soc)
83         finally:
84             print "\t" "bye"
85             soc.close()
86             self.connection.close()
87
88     def _read_write(self, soc, max_idling=20):
89         iw = [self.connection, soc]
90         ow = []
91         count = 0
92         msg = ''
93         while 1:
94             count += 1
95             (ins, _, exs) = select.select(iw, ow, iw, 3)
96             if exs:
97               break
98             if ins:
99                 for i in ins:
100                     if i is soc:
101                         out = self.connection
102                     else:
103                         out = soc
104                     data = i.recv(8192)
105                     if data:
106                         if (re.match("(\w+\.)*gnunet", self.host_port[0])):
107                             arr = self.host_port[0].split(' ')
108                             arr.pop(0)
109                             data = re.sub(r'(a href="http://(\w+\.)*)(\+)', r'\1'+self.host_port[0], data)
110                         print data
111                         out.send(data)
112                         count = 0
113             else:
114                 print "\t" "idle", count
115                 print msg
116             if count == max_idling: break
117
118     do_HEAD = do_GET
119     do_POST = do_GET
120     do_PUT  = do_GET
121     do_DELETE=do_GET
122
123 class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
124                            BaseHTTPServer.HTTPServer): pass
125
126 if __name__ == '__main__':
127     from sys import argv
128     if argv[1:] and argv[1] in ('-h', '--help'):
129         print argv[0], "[port [allowed_client_name ...]]"
130     else:
131         if argv[2:]:
132             allowed = []
133             for name in argv[2:]:
134                 client = socket.gethostbyname(name)
135                 allowed.append(client)
136                 print "Accept: %s (%s)" % (client, name)
137             ProxyHandler.allowed_clients = allowed
138             del argv[2:]
139         else:
140             print "Any clients will be served..."
141         BaseHTTPServer.test(ProxyHandler, ThreadingHTTPServer)
142