OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Constrained Network Server. Serves files with supplied network constraints. | 6 """Constrained Network Server. Serves files with supplied network constraints. |
7 | 7 |
8 The CNS exposes a web based API allowing network constraints to be imposed on | 8 The CNS exposes a web based API allowing network constraints to be imposed on |
9 file serving. | 9 file serving. |
10 | 10 |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 | 178 |
179 Args: | 179 Args: |
180 options: optparse based class returned by ParseArgs() | 180 options: optparse based class returned by ParseArgs() |
181 port_allocator: A port allocator instance. | 181 port_allocator: A port allocator instance. |
182 """ | 182 """ |
183 self._options = options | 183 self._options = options |
184 self._port_allocator = port_allocator | 184 self._port_allocator = port_allocator |
185 | 185 |
186 @cherrypy.expose | 186 @cherrypy.expose |
187 def ServeConstrained(self, f=None, bandwidth=None, latency=None, loss=None, | 187 def ServeConstrained(self, f=None, bandwidth=None, latency=None, loss=None, |
188 new_port=False): | 188 new_port=False, no_cache=False, **kwargs): |
189 """Serves the requested file with the requested constraints. | 189 """Serves the requested file with the requested constraints. |
190 | 190 |
191 Subsequent requests for the same constraints from the same IP will share the | 191 Subsequent requests for the same constraints from the same IP will share the |
192 previously created port unless new_port equals True. If no constraints | 192 previously created port unless new_port equals True. If no constraints |
193 are provided the file is served as is. | 193 are provided the file is served as is. |
194 | 194 |
195 Args: | 195 Args: |
196 f: path relative to http root of file to serve. | 196 f: path relative to http root of file to serve. |
197 bandwidth: maximum allowed bandwidth for the provided port (integer | 197 bandwidth: maximum allowed bandwidth for the provided port (integer |
198 in kbit/s). | 198 in kbit/s). |
199 latency: time to add to each packet (integer in ms). | 199 latency: time to add to each packet (integer in ms). |
200 loss: percentage of packets to drop (integer, 0-100). | 200 loss: percentage of packets to drop (integer, 0-100). |
201 new_port: whether to use a new port for this request or not. | 201 new_port: whether to use a new port for this request or not. |
| 202 no_cache: Set reponse's cache-control to no-cache. |
202 """ | 203 """ |
203 cherrypy.log('Got request for %s, bandwidth=%s, latency=%s, loss=%s, ' | 204 cherrypy.log('Got request for %s, bandwidth=%s, latency=%s, loss=%s, ' |
204 'new_port=%s' % (f, bandwidth, latency, loss, new_port)) | 205 'new_port=%s, no_cache=%s, kwargs=%s' % |
| 206 (f, bandwidth, latency, loss, new_port, no_cache, kwargs)) |
| 207 if no_cache: |
| 208 response = cherrypy.response |
| 209 response.headers['Pragma'] = 'no-cache' |
| 210 response.headers['Cache-Control'] = 'no-cache' |
| 211 |
205 # CherryPy is a bit wonky at detecting parameters, so just make them all | 212 # CherryPy is a bit wonky at detecting parameters, so just make them all |
206 # optional and validate them ourselves. | 213 # optional and validate them ourselves. |
207 if not f: | 214 if not f: |
208 raise cherrypy.HTTPError(400, 'Invalid request. File must be specified.') | 215 raise cherrypy.HTTPError(400, 'Invalid request. File must be specified.') |
209 | 216 |
210 # Sanitize and check the path to prevent www-root escapes. | 217 # Sanitize and check the path to prevent www-root escapes. |
211 sanitized_path = os.path.abspath(os.path.join(self._options.www_root, f)) | 218 sanitized_path = os.path.abspath(os.path.join(self._options.www_root, f)) |
212 if not sanitized_path.startswith(self._options.www_root): | 219 if not sanitized_path.startswith(self._options.www_root): |
213 raise cherrypy.HTTPError(403, 'Invalid file requested.') | 220 raise cherrypy.HTTPError(403, 'Invalid file requested.') |
214 | 221 |
(...skipping 24 matching lines...) Expand all Loading... |
239 interface=self._options.interface, bandwidth=bandwidth, latency=latency, | 246 interface=self._options.interface, bandwidth=bandwidth, latency=latency, |
240 loss=loss, new_port=new_port) | 247 loss=loss, new_port=new_port) |
241 end_time = time.time() | 248 end_time = time.time() |
242 | 249 |
243 if not constrained_port: | 250 if not constrained_port: |
244 raise cherrypy.HTTPError(503, 'Service unavailable. Out of ports.') | 251 raise cherrypy.HTTPError(503, 'Service unavailable. Out of ports.') |
245 | 252 |
246 cherrypy.log('Time to set up port %d = %ssec.' % | 253 cherrypy.log('Time to set up port %d = %ssec.' % |
247 (constrained_port, end_time - start_time)) | 254 (constrained_port, end_time - start_time)) |
248 | 255 |
249 # Build constrained URL. Only pass on the file parameter. | 256 # Build constrained URL using the constrained port and original URL |
250 constrained_url = '%s?f=%s' % ( | 257 # parameters except the network constraints (bandwidth, latency, and loss). |
| 258 constrained_url = '%s?f=%s&no_cache=%s&%s' % ( |
251 cherrypy.url().replace( | 259 cherrypy.url().replace( |
252 ':%d' % self._options.port, ':%d' % constrained_port), | 260 ':%d' % self._options.port, ':%d' % constrained_port), |
253 f) | 261 f, |
| 262 no_cache, |
| 263 '&'.join(['%s=%s' % (key, kwargs[key]) for key in kwargs])) |
254 | 264 |
255 # Redirect request to the constrained port. | 265 # Redirect request to the constrained port. |
256 cherrypy.lib.cptools.redirect(constrained_url, internal=False) | 266 cherrypy.lib.cptools.redirect(constrained_url, internal=False) |
257 | 267 |
258 def _ParseIntParameter(self, param, msg, check): | 268 def _ParseIntParameter(self, param, msg, check): |
259 """Returns integer value of param and verifies it satisfies the check. | 269 """Returns integer value of param and verifies it satisfies the check. |
260 | 270 |
261 Args: | 271 Args: |
262 param: Parameter name to check. | 272 param: Parameter name to check. |
263 msg: Message in error if raised. | 273 msg: Message in error if raised. |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
360 try: | 370 try: |
361 cherrypy.quickstart(ConstrainedNetworkServer(options, pa)) | 371 cherrypy.quickstart(ConstrainedNetworkServer(options, pa)) |
362 finally: | 372 finally: |
363 # Disable Ctrl-C handler to prevent interruption of cleanup. | 373 # Disable Ctrl-C handler to prevent interruption of cleanup. |
364 signal.signal(signal.SIGINT, lambda signal, frame: None) | 374 signal.signal(signal.SIGINT, lambda signal, frame: None) |
365 pa.Cleanup(options.interface, all_ports=True) | 375 pa.Cleanup(options.interface, all_ports=True) |
366 | 376 |
367 | 377 |
368 if __name__ == '__main__': | 378 if __name__ == '__main__': |
369 Main() | 379 Main() |
OLD | NEW |