tests/tinyproxy.py
changeset 2337 3f24bc5dee81
equal deleted inserted replaced
2336:f77edcffb837 2337:3f24bc5dee81
       
     1 #!/usr/bin/env 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
       
    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 
       
    24     def handle(self):
       
    25         (ip, port) =  self.client_address
       
    26         if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
       
    27             self.raw_requestline = self.rfile.readline()
       
    28             if self.parse_request(): self.send_error(403)
       
    29         else:
       
    30             self.__base_handle()
       
    31 
       
    32     def _connect_to(self, netloc, soc):
       
    33         i = netloc.find(':')
       
    34         if i >= 0:
       
    35             host_port = netloc[:i], int(netloc[i+1:])
       
    36         else:
       
    37             host_port = netloc, 80
       
    38         print "\t" "connect to %s:%d" % host_port
       
    39         try: soc.connect(host_port)
       
    40         except socket.error, arg:
       
    41             try: msg = arg[1]
       
    42             except: msg = arg
       
    43             self.send_error(404, msg)
       
    44             return 0
       
    45         return 1
       
    46 
       
    47     def do_CONNECT(self):
       
    48         soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
    49         try:
       
    50             if self._connect_to(self.path, soc):
       
    51                 self.log_request(200)
       
    52                 self.wfile.write(self.protocol_version +
       
    53                                  " 200 Connection established\r\n")
       
    54                 self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
       
    55                 self.wfile.write("\r\n")
       
    56                 self._read_write(soc, 300)
       
    57         finally:
       
    58             print "\t" "bye"
       
    59             soc.close()
       
    60             self.connection.close()
       
    61 
       
    62     def do_GET(self):
       
    63         (scm, netloc, path, params, query, fragment) = urlparse.urlparse(
       
    64             self.path, 'http')
       
    65         if scm != 'http' or fragment or not netloc:
       
    66             self.send_error(400, "bad url %s" % self.path)
       
    67             return
       
    68         soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
    69         try:
       
    70             if self._connect_to(netloc, soc):
       
    71                 self.log_request()
       
    72                 soc.send("%s %s %s\r\n" % (
       
    73                     self.command,
       
    74                     urlparse.urlunparse(('', '', path, params, query, '')),
       
    75                     self.request_version))
       
    76                 self.headers['Connection'] = 'close'
       
    77                 del self.headers['Proxy-Connection']
       
    78                 for key_val in self.headers.items():
       
    79                     soc.send("%s: %s\r\n" % key_val)
       
    80                 soc.send("\r\n")
       
    81                 self._read_write(soc)
       
    82         finally:
       
    83             print "\t" "bye"
       
    84             soc.close()
       
    85             self.connection.close()
       
    86 
       
    87     def _read_write(self, soc, max_idling=20):
       
    88         iw = [self.connection, soc]
       
    89         ow = []
       
    90         count = 0
       
    91         while 1:
       
    92             count += 1
       
    93             (ins, _, exs) = select.select(iw, ow, iw, 3)
       
    94             if exs: break
       
    95             if ins:
       
    96                 for i in ins:
       
    97                     if i is soc:
       
    98                         out = self.connection
       
    99                     else:
       
   100                         out = soc
       
   101                     data = i.recv(8192)
       
   102                     if data:
       
   103                         out.send(data)
       
   104                         count = 0
       
   105             else:
       
   106                 print "\t" "idle", count
       
   107             if count == max_idling: break
       
   108 
       
   109     do_HEAD = do_GET
       
   110     do_POST = do_GET
       
   111     do_PUT  = do_GET
       
   112     do_DELETE=do_GET
       
   113 
       
   114 class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
       
   115                            BaseHTTPServer.HTTPServer): pass
       
   116 
       
   117 if __name__ == '__main__':
       
   118     from sys import argv
       
   119     if argv[1:] and argv[1] in ('-h', '--help'):
       
   120         print argv[0], "[port [allowed_client_name ...]]"
       
   121     else:
       
   122         if argv[2:]:
       
   123             allowed = []
       
   124             for name in argv[2:]:
       
   125                 client = socket.gethostbyname(name)
       
   126                 allowed.append(client)
       
   127                 print "Accept: %s (%s)" % (client, name)
       
   128             ProxyHandler.allowed_clients = allowed
       
   129             del argv[2:]
       
   130         else:
       
   131             print "Any clients will be served..."
       
   132         BaseHTTPServer.test(ProxyHandler, ThreadingHTTPServer)