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