| Index: tools/telemetry/telemetry/core/memory_cache_http_server.py
|
| diff --git a/tools/telemetry/telemetry/core/memory_cache_http_server.py b/tools/telemetry/telemetry/core/memory_cache_http_server.py
|
| deleted file mode 100644
|
| index a346edad110c76a1f224974443ff54b3cfaf4f89..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/telemetry/core/memory_cache_http_server.py
|
| +++ /dev/null
|
| @@ -1,276 +0,0 @@
|
| -# Copyright 2012 The Chromium Authors. All rights reserved.
|
| -# Use of this source code is governed by a BSD-style license that can be
|
| -# found in the LICENSE file.
|
| -
|
| -import BaseHTTPServer
|
| -from collections import namedtuple
|
| -import errno
|
| -import gzip
|
| -import mimetypes
|
| -import os
|
| -import SimpleHTTPServer
|
| -import socket
|
| -import SocketServer
|
| -import StringIO
|
| -import sys
|
| -import urlparse
|
| -
|
| -from telemetry.core import local_server
|
| -
|
| -ByteRange = namedtuple('ByteRange', ['from_byte', 'to_byte'])
|
| -ResourceAndRange = namedtuple('ResourceAndRange', ['resource', 'byte_range'])
|
| -
|
| -
|
| -class MemoryCacheHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
| -
|
| - protocol_version = 'HTTP/1.1' # override BaseHTTPServer setting
|
| - wbufsize = -1 # override StreamRequestHandler (a base class) setting
|
| -
|
| - def handle(self):
|
| - try:
|
| - BaseHTTPServer.BaseHTTPRequestHandler.handle(self)
|
| - except socket.error as e:
|
| - # Connection reset errors happen all the time due to the browser closing
|
| - # without terminating the connection properly. They can be safely
|
| - # ignored.
|
| - if e[0] != errno.ECONNRESET:
|
| - raise
|
| -
|
| - def do_GET(self):
|
| - """Serve a GET request."""
|
| - resource_range = self.SendHead()
|
| -
|
| - if not resource_range or not resource_range.resource:
|
| - return
|
| - response = resource_range.resource['response']
|
| -
|
| - if not resource_range.byte_range:
|
| - self.wfile.write(response)
|
| - return
|
| -
|
| - start_index = resource_range.byte_range.from_byte
|
| - end_index = resource_range.byte_range.to_byte
|
| - self.wfile.write(response[start_index:end_index + 1])
|
| -
|
| - def do_HEAD(self):
|
| - """Serve a HEAD request."""
|
| - self.SendHead()
|
| -
|
| - def log_error(self, fmt, *args):
|
| - pass
|
| -
|
| - def log_request(self, code='-', size='-'):
|
| - # Don't spam the console unless it is important.
|
| - pass
|
| -
|
| - def SendHead(self):
|
| - path = os.path.realpath(self.translate_path(self.path))
|
| - if path not in self.server.resource_map:
|
| - self.send_error(404, 'File not found')
|
| - return None
|
| -
|
| - resource = self.server.resource_map[path]
|
| - total_num_of_bytes = resource['content-length']
|
| - byte_range = self.GetByteRange(total_num_of_bytes)
|
| - if byte_range:
|
| - # request specified a range, so set response code to 206.
|
| - self.send_response(206)
|
| - self.send_header('Content-Range', 'bytes %d-%d/%d' %
|
| - (byte_range.from_byte, byte_range.to_byte,
|
| - total_num_of_bytes))
|
| - total_num_of_bytes = byte_range.to_byte - byte_range.from_byte + 1
|
| - else:
|
| - self.send_response(200)
|
| -
|
| - self.send_header('Content-Length', str(total_num_of_bytes))
|
| - self.send_header('Content-Type', resource['content-type'])
|
| - self.send_header('Last-Modified',
|
| - self.date_time_string(resource['last-modified']))
|
| - if resource['zipped']:
|
| - self.send_header('Content-Encoding', 'gzip')
|
| - self.end_headers()
|
| - return ResourceAndRange(resource, byte_range)
|
| -
|
| - def GetByteRange(self, total_num_of_bytes):
|
| - """Parse the header and get the range values specified.
|
| -
|
| - Args:
|
| - total_num_of_bytes: Total # of bytes in requested resource,
|
| - used to calculate upper range limit.
|
| - Returns:
|
| - A ByteRange namedtuple object with the requested byte-range values.
|
| - If no Range is explicitly requested or there is a failure parsing,
|
| - return None.
|
| - If range specified is in the format "N-", return N-END. Refer to
|
| - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for details.
|
| - If upper range limit is greater than total # of bytes, return upper index.
|
| - """
|
| -
|
| - range_header = self.headers.getheader('Range')
|
| - if range_header is None:
|
| - return None
|
| - if not range_header.startswith('bytes='):
|
| - return None
|
| -
|
| - # The range header is expected to be a string in this format:
|
| - # bytes=0-1
|
| - # Get the upper and lower limits of the specified byte-range.
|
| - # We've already confirmed that range_header starts with 'bytes='.
|
| - byte_range_values = range_header[len('bytes='):].split('-')
|
| - from_byte = 0
|
| - to_byte = 0
|
| -
|
| - if len(byte_range_values) == 2:
|
| - # If to_range is not defined return all bytes starting from from_byte.
|
| - to_byte = (int(byte_range_values[1]) if byte_range_values[1] else
|
| - total_num_of_bytes - 1)
|
| - # If from_range is not defined return last 'to_byte' bytes.
|
| - from_byte = (int(byte_range_values[0]) if byte_range_values[0] else
|
| - total_num_of_bytes - to_byte)
|
| - else:
|
| - return None
|
| -
|
| - # Do some validation.
|
| - if from_byte < 0:
|
| - return None
|
| -
|
| - # Make to_byte the end byte by default in edge cases.
|
| - if to_byte < from_byte or to_byte >= total_num_of_bytes:
|
| - to_byte = total_num_of_bytes - 1
|
| -
|
| - return ByteRange(from_byte, to_byte)
|
| -
|
| -
|
| -class _MemoryCacheHTTPServerImpl(SocketServer.ThreadingMixIn,
|
| - BaseHTTPServer.HTTPServer):
|
| - # Increase the request queue size. The default value, 5, is set in
|
| - # SocketServer.TCPServer (the parent of BaseHTTPServer.HTTPServer).
|
| - # Since we're intercepting many domains through this single server,
|
| - # it is quite possible to get more than 5 concurrent requests.
|
| - request_queue_size = 128
|
| -
|
| - # Don't prevent python from exiting when there is thread activity.
|
| - daemon_threads = True
|
| -
|
| - def __init__(self, host_port, handler, paths):
|
| - BaseHTTPServer.HTTPServer.__init__(self, host_port, handler)
|
| - self.resource_map = {}
|
| - for path in paths:
|
| - if os.path.isdir(path):
|
| - self.AddDirectoryToResourceMap(path)
|
| - else:
|
| - self.AddFileToResourceMap(path)
|
| -
|
| - def AddDirectoryToResourceMap(self, directory_path):
|
| - """Loads all files in directory_path into the in-memory resource map."""
|
| - for root, dirs, files in os.walk(directory_path):
|
| - # Skip hidden files and folders (like .svn and .git).
|
| - files = [f for f in files if f[0] != '.']
|
| - dirs[:] = [d for d in dirs if d[0] != '.']
|
| -
|
| - for f in files:
|
| - file_path = os.path.join(root, f)
|
| - if not os.path.exists(file_path): # Allow for '.#' files
|
| - continue
|
| - self.AddFileToResourceMap(file_path)
|
| -
|
| - def AddFileToResourceMap(self, file_path):
|
| - """Loads file_path into the in-memory resource map."""
|
| - file_path = os.path.realpath(file_path)
|
| - if file_path in self.resource_map:
|
| - return
|
| -
|
| - with open(file_path, 'rb') as fd:
|
| - response = fd.read()
|
| - fs = os.fstat(fd.fileno())
|
| - content_type = mimetypes.guess_type(file_path)[0]
|
| - zipped = False
|
| - if content_type in ['text/html', 'text/css', 'application/javascript']:
|
| - zipped = True
|
| - sio = StringIO.StringIO()
|
| - gzf = gzip.GzipFile(fileobj=sio, compresslevel=9, mode='wb')
|
| - gzf.write(response)
|
| - gzf.close()
|
| - response = sio.getvalue()
|
| - sio.close()
|
| - self.resource_map[file_path] = {
|
| - 'content-type': content_type,
|
| - 'content-length': len(response),
|
| - 'last-modified': fs.st_mtime,
|
| - 'response': response,
|
| - 'zipped': zipped
|
| - }
|
| -
|
| - index = 'index.html'
|
| - if os.path.basename(file_path) == index:
|
| - dir_path = os.path.dirname(file_path)
|
| - self.resource_map[dir_path] = self.resource_map[file_path]
|
| -
|
| -
|
| -class MemoryCacheHTTPServerBackend(local_server.LocalServerBackend):
|
| -
|
| - def __init__(self):
|
| - super(MemoryCacheHTTPServerBackend, self).__init__()
|
| - self._httpd = None
|
| -
|
| - def StartAndGetNamedPorts(self, args):
|
| - base_dir = args['base_dir']
|
| - os.chdir(base_dir)
|
| -
|
| - paths = args['paths']
|
| - for path in paths:
|
| - if not os.path.realpath(path).startswith(os.path.realpath(os.getcwd())):
|
| - print >> sys.stderr, '"%s" is not under the cwd.' % path
|
| - sys.exit(1)
|
| -
|
| - server_address = (args['host'], args['port'])
|
| - MemoryCacheHTTPRequestHandler.protocol_version = 'HTTP/1.1'
|
| - self._httpd = _MemoryCacheHTTPServerImpl(
|
| - server_address, MemoryCacheHTTPRequestHandler, paths)
|
| - return [local_server.NamedPort('http', self._httpd.server_address[1])]
|
| -
|
| - def ServeForever(self):
|
| - return self._httpd.serve_forever()
|
| -
|
| -
|
| -class MemoryCacheHTTPServer(local_server.LocalServer):
|
| -
|
| - def __init__(self, paths):
|
| - super(MemoryCacheHTTPServer, self).__init__(MemoryCacheHTTPServerBackend)
|
| - self._base_dir = None
|
| -
|
| - for path in paths:
|
| - assert os.path.exists(path), '%s does not exist.' % path
|
| -
|
| - paths = list(paths)
|
| - self._paths = paths
|
| -
|
| - self._paths_as_set = set(map(os.path.realpath, paths))
|
| -
|
| - common_prefix = os.path.commonprefix(paths)
|
| - if os.path.isdir(common_prefix):
|
| - self._base_dir = common_prefix
|
| - else:
|
| - self._base_dir = os.path.dirname(common_prefix)
|
| -
|
| - def GetBackendStartupArgs(self):
|
| - return {'base_dir': self._base_dir,
|
| - 'paths': self._paths,
|
| - 'host': self.host_ip,
|
| - 'port': 0}
|
| -
|
| - @property
|
| - def paths(self):
|
| - return self._paths_as_set
|
| -
|
| - @property
|
| - def url(self):
|
| - return self.forwarder.url
|
| -
|
| - def UrlOf(self, path):
|
| - relative_path = os.path.relpath(path, self._base_dir)
|
| - # Preserve trailing slash or backslash.
|
| - # It doesn't matter in a file path, but it does matter in a URL.
|
| - if path.endswith(os.sep) or (os.altsep and path.endswith(os.altsep)):
|
| - relative_path += '/'
|
| - return urlparse.urljoin(self.url, relative_path.replace(os.sep, '/'))
|
|
|