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()