Chromium Code Reviews| Index: media/tools/constrained_network_server/cns.py |
| diff --git a/media/tools/constrained_network_server/cns.py b/media/tools/constrained_network_server/cns.py |
| index 4fe31ee0722c406ed1914e58930de453cb0d7955..732b032b9234a64d6b24ee39e39367a49e3b52e7 100755 |
| --- a/media/tools/constrained_network_server/cns.py |
| +++ b/media/tools/constrained_network_server/cns.py |
| @@ -21,6 +21,9 @@ import signal |
| import sys |
| import threading |
| import time |
| +import urllib |
| +import urllib2 |
| + |
| import traffic_control |
| try: |
| @@ -201,19 +204,89 @@ class ConstrainedNetworkServer(object): |
| if not f: |
| raise cherrypy.HTTPError(400, 'Invalid request. File must be specified.') |
| + # Check existence early to prevent wasted constraint setup. |
| + self._CheckRequestedFileExist(f) |
| + |
| + # If there are no constraints, just serve the file. |
| + if bandwidth is None and latency is None and loss is None: |
| + return self._ServeFile(f) |
| + |
| + constrained_port = self._GetConstrainedPort( |
| + f, bandwidth=bandwidth, latency=latency, loss=loss, new_port=new_port, |
| + **kwargs) |
| + |
| + # Build constrained URL using the constrained port and original URL |
| + # parameters except the network constraints (bandwidth, latency, and loss). |
| + constrained_url = self._GetServerURL(f, constrained_port, |
| + no_cache=no_cache, **kwargs) |
| + |
| + # Redirect request to the constrained port. |
| + cherrypy.log('Redirect to %s' % constrained_url) |
| + cherrypy.lib.cptools.redirect(constrained_url, internal=False) |
| + |
| + def _CheckRequestedFileExist(self, f): |
| + """Checks if the requested file exists, raises HTTPError otherwise.""" |
| + if self._options.local_server_port: |
| + self._CheckFileExistOnLocalServer(f) |
| + else: |
| + self._CheckFileExistOnServer(f) |
| + |
| + def _CheckFileExistOnServer(self, f): |
| + """Checks if requested file f exists to be served by this server.""" |
| # Sanitize and check the path to prevent www-root escapes. |
| sanitized_path = os.path.abspath(os.path.join(self._options.www_root, f)) |
| if not sanitized_path.startswith(self._options.www_root): |
| raise cherrypy.HTTPError(403, 'Invalid file requested.') |
| - |
| - # Check existence early to prevent wasted constraint setup. |
| if not os.path.exists(sanitized_path): |
| raise cherrypy.HTTPError(404, 'File not found.') |
| - # If there are no constraints, just serve the file. |
| - if bandwidth is None and latency is None and loss is None: |
| + def _CheckFileExistOnLocalServer(self, f): |
| + """Checks if requested file exists on local server hosting files.""" |
| + test_url = self._GetServerURL(f, self._options.local_server_port) |
| + try: |
| + cherrypy.log('Check file exist using URL: %s' % test_url) |
| + return urllib2.urlopen(test_url) is not None |
| + except Exception: |
| + raise cherrypy.HTTPError(404, 'File not found on local server.') |
| + |
| + def _ServeFile(self, f): |
| + """Serves the file as an http response.""" |
| + if self._options.local_server_port: |
| + redirect_url = self._GetServerURL(f, self._options.local_server_port) |
| + cherrypy.log('Redirect to %s' % redirect_url) |
| + cherrypy.lib.cptools.redirect(redirect_url, internal=False) |
| + else: |
| + sanitized_path = os.path.abspath(os.path.join(self._options.www_root, f)) |
| return cherrypy.lib.static.serve_file(sanitized_path) |
| + def _GetServerURL(self, f, port, **kwargs): |
| + """Returns a URL for local server to serve the file on given port. |
| + |
| + Args: |
| + f: file name to serve on local server. Relative to www_root. |
| + port: Local server port (it can be a configured constrained port). |
| + kwargs: extra parameteres passed in the URL. |
| + """ |
| + base_url = '%s?f=%s' % (cherrypy.url(), f) |
| + if self._options.local_server_port: |
| + base_url = '%s/%s' % (cherrypy.url().replace('ServeConstrained', |
|
DaleCurtis
2012/08/09 17:28:02
Cleaner as:
base_url = '%s/%s' % (
cherrypy.u
shadi
2012/08/09 18:26:49
The www_root used with local-server-port is relati
DaleCurtis
2012/08/09 18:46:31
You need to update the --options documentation for
|
| + self._options.www_root), |
| + f) |
| + base_url = base_url.replace(':%d' % self._options.port, ':%d' % port) |
| + extra_args = urllib.urlencode(kwargs) |
| + if extra_args: |
| + delimeter = '&' |
|
DaleCurtis
2012/08/09 17:28:02
You can probably just roll the &, ? into the if ab
shadi
2012/08/09 18:26:49
Done.
|
| + if self._options.local_server_port: |
| + delimeter = '?' |
| + base_url += '%s%s' % (delimeter, extra_args) |
| + return base_url |
| + |
| + def _GetConstrainedPort(self, f=None, bandwidth=None, latency=None, loss=None, |
| + new_port=False, **kwargs): |
| + """Creates or gets a port with specified network constraints. |
| + |
| + See ServeConstrained() for more details. |
| + """ |
| # Validate inputs. isdigit() guarantees a natural number. |
| bandwidth = self._ParseIntParameter( |
| bandwidth, 'Invalid bandwidth constraint.', lambda x: x > 0) |
| @@ -222,35 +295,24 @@ class ConstrainedNetworkServer(object): |
| loss = self._ParseIntParameter( |
| loss, 'Invalid loss constraint.', lambda x: x <= 100 and x >= 0) |
| - # Allocate a port using the given constraints. If a port with the requested |
| - # key is already allocated, it will be reused. |
| - # |
| - # TODO(dalecurtis): The key cherrypy.request.remote.ip might not be unique |
| - # if build slaves are sharing the same VM. |
| + redirect_port = self._options.port |
| + if self._options.local_server_port: |
| + redirect_port = self._options.local_server_port |
| + |
| start_time = time.time() |
| + # Allocate a port using the given constraints. If a port with the requested |
| + # key and kwargs already exist then reuse that port. |
| constrained_port = self._port_allocator.Get( |
| - cherrypy.request.remote.ip, server_port=self._options.port, |
| + cherrypy.request.remote.ip, server_port=redirect_port, |
| interface=self._options.interface, bandwidth=bandwidth, latency=latency, |
| loss=loss, new_port=new_port, file=f, **kwargs) |
| - end_time = time.time() |
| + |
| + cherrypy.log('Time to set up port %d = %.3fsec.' % |
| + (constrained_port, time.time() - start_time)) |
| if not constrained_port: |
| raise cherrypy.HTTPError(503, 'Service unavailable. Out of ports.') |
| - |
| - cherrypy.log('Time to set up port %d = %ssec.' % |
| - (constrained_port, end_time - start_time)) |
| - |
| - # Build constrained URL using the constrained port and original URL |
| - # parameters except the network constraints (bandwidth, latency, and loss). |
| - constrained_url = '%s?f=%s&no_cache=%s&%s' % ( |
| - cherrypy.url().replace( |
| - ':%d' % self._options.port, ':%d' % constrained_port), |
| - f, |
| - no_cache, |
| - '&'.join(['%s=%s' % (key, kwargs[key]) for key in kwargs])) |
| - |
| - # Redirect request to the constrained port. |
| - cherrypy.lib.cptools.redirect(constrained_url, internal=False) |
| + return constrained_port |
| def _ParseIntParameter(self, param, msg, check): |
| """Returns integer value of param and verifies it satisfies the check. |
| @@ -301,9 +363,12 @@ def ParseArgs(): |
| default=cherrypy._cpserver.Server.thread_pool, |
| help=('Number of threads in the thread pool. Default: ' |
| '%default')) |
| - parser.add_option('--www-root', default=os.getcwd(), |
| + parser.add_option('--www-root', default='', |
| help=('Directory root to serve files from. Defaults to the ' |
| - 'current directory: %default')) |
| + 'current directory (if --local-server-port is not ' |
| + 'used): %s' % os.getcwd())) |
| + parser.add_option('--local-server-port', type='int', |
| + help=('Optional local server port to host files.')) |
| parser.add_option('-v', '--verbose', action='store_true', default=False, |
| help='Turn on verbose output.') |
| @@ -320,7 +385,10 @@ def ParseArgs(): |
| parser.error('Invalid expiry time specified.') |
| # Convert the path to an absolute to remove any . or .. |
| - options.www_root = os.path.abspath(options.www_root) |
| + if not options.local_server_port: |
| + if not options.www_root: |
| + options.www_root = os.getcwd() |
| + options.www_root = os.path.abspath(options.www_root) |
| _SetLogger(options.verbose) |