1 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """A simple HTTP proxy server."""
15 from urlparse import urlparse
20 class _ProxyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
21 """Request handler for the proxy server."""
23 # Disables buffering, which causes problems with certain sites.
26 def __init__(self, request, client_addr, server):
27 BaseHTTPServer.BaseHTTPRequestHandler.__init__(
28 self, request, client_addr, server)
30 def _GetHandler(self):
31 """GET handler for requests that will be processed by the server."""
33 url = urllib.urlopen(self.path, proxies={'http:' : '127.0.0.1'})
38 self.wfile.write(data)
42 """Handles GET requests."""
43 if self._ShouldHandleRequest():
47 self._GenericResponseHandler()
50 """Handles CONNECT requests."""
51 self._GenericResponseHandler()
54 """Handles HEAD requests."""
58 """Handles POST requests."""
62 """Handles PUT requests."""
65 def _GenericResponseHandler(self):
66 """Sends a dummy reponse for HTTP requests not handled by the server."""
67 # Handle dropped connections.
69 self.send_response(200)
70 except (socket.error, socket.gaierror):
72 contents = 'Default response given for path: %s' % self.path
73 self.send_header('Content-Type', 'text/html')
74 self.send_header('Content-Length', len(contents))
76 if (self.command != 'HEAD'):
77 self.wfile.write(contents)
79 def _ShouldHandleRequest(self):
80 """Determines if a request should be processed by the server."""
81 if self.server.ShouldHandleAllRequests():
83 (scheme, netloc, path, params, query, flag) = urlparse(self.path, 'http')
84 paths = self.server.GetPaths()
85 if(any([netloc.find(url) >= 0 for url in paths]) or
86 any([self.path.find(url) >= 0 for url in paths])):
90 def _LogRequest(self):
91 """Logs requests handled by the server to a buffer."""
92 self.server.AddHandledRequest(self.requestline)
94 def log_request(self, *args, **kwargs):
95 """Overridden base class method that disables request logging."""
99 class ProxyServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
100 """Creates a threaded proxy server."""
102 def __init__(self, port=0, paths=[], handle_all=True):
103 """Initializes proxy server settings.
106 port: Server port number. If zero, the server will select a free port.
107 paths: A list containing urls the server will process. If |handle_all| is
108 False, the server will only process urls in this list. URLs should be
109 passed as follows: ['http://www.google.com', '...',].
110 handle_all: Flag that determines if the server will process all requests.
112 BaseHTTPServer.HTTPServer.__init__(
113 self, (_HOST, port), _ProxyRequestHandler, True)
114 self._stopped = False
115 self._serving = False
116 self._lock = threading.RLock()
117 self._paths = list(paths)
118 self._handle_all = handle_all
119 self._handled_requests = []
123 """Returns the port number the server is serving on."""
124 return self.server_port
126 def StartServer(self):
127 """Starts the proxy server in a new thread."""
129 raise RuntimeError('Cannot restart server.')
130 if not self._serving:
132 thread = WorkerThread(self)
136 """Shuts down the server."""
137 if not self._serving:
138 raise RuntimeError('Server is currently inactive.')
139 self._serving = False
142 urllib2.urlopen('http://%s:%s' % (self.server_name, self.server_port))
143 except urllib2.URLError:
147 def handle_request(self):
148 """Handles requests while the |_serving| flag is True."""
150 BaseHTTPServer.HTTPServer.handle_request(self)
152 def ShouldHandleAllRequests(self):
153 """Determines if server should handle all requests."""
154 return self._handle_all
156 def AddHandledRequest(self, request):
157 """Appends requests handled by the server to |_handled_requests|."""
160 self._handled_requests.append(request)
164 def GetHandledRequests(self):
165 """Returns requests handled by the server."""
168 return copy.deepcopy(self._handled_requests)
173 """Returns list of urls that will be handled by the server."""
177 class WorkerThread(threading.Thread):
178 """Creates a worker thread."""
180 def __init__(self, server):
181 threading.Thread.__init__(self)
182 self._server = server
185 """Overridden base class method."""
186 print 'Serving on port: %s' % self._server.server_port
187 self._server.daemon_threads = True
188 self._server.handle_request()