| #!/usr/bin/python |
| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """A simple web server for cell modem tests |
| |
| Used for: |
| Connectivity tests |
| Bandwidth tests |
| """ |
| |
| import BaseHTTPServer |
| import socket |
| import struct |
| import urlparse |
| |
| import numpy.random |
| |
| |
| _MAX_SINGLE_TRANSFER = 1 << 20 |
| |
| |
| def WriteBytes(out, n): |
| """Write n random bytes to out.""" |
| remaining = n |
| |
| while remaining: |
| if remaining > _MAX_SINGLE_TRANSFER: |
| # We use random bytes to defeat any compression that may be |
| # present. This code using the random module was on the edge of |
| # too slow: |
| # struct.pack('Q' * (n/8), |
| # *[random.getrandbits(64) for x in xrange(n/8)]) |
| # so we use numpy.random.bytes instead. |
| out.write(numpy.random.bytes(_MAX_SINGLE_TRANSFER)) |
| remaining -= _MAX_SINGLE_TRANSFER |
| else: |
| out.write(numpy.random.bytes(remaining)) |
| remaining = 0 |
| |
| |
| class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
| def address_string(self): |
| """Build address string without attempting a reverse DNS lookup.""" |
| return str(self.client_address[0]) |
| |
| def _Dispatch(self, command): |
| """Parses query and dispatches a request to the relevant method.""" |
| parsed = urlparse.urlparse(self.path) |
| |
| # the last value specified for each key |
| self.query = dict( |
| [(k, v[-1]) for k,v in urlparse.parse_qs(parsed.query).items()]) |
| |
| dispatch_table = { |
| 'GET': { |
| '/connectivity/index.html': self.Connectivity, |
| '/download': self.GetBytes, |
| }, |
| 'POST': { |
| '/upload': self.SinkBytes, |
| }, |
| } |
| if parsed.path not in dispatch_table[command]: |
| self.send_error(404) |
| return |
| dispatch_table[command][parsed.path]() |
| |
| def do_GET(self): |
| return self._Dispatch('GET') |
| |
| def do_POST(self): |
| return self._Dispatch('POST') |
| |
| def GetBytes(self): |
| try: |
| n = int(self.query['size']) |
| except KeyError: |
| self.send_error(400) |
| return |
| self.send_response(200) |
| self.send_header('Content-Length', n) |
| self.end_headers() |
| WriteBytes(self.wfile, n) |
| |
| def SinkBytes(self): |
| remaining = int(self.headers['Content-Length']) |
| |
| while remaining: |
| this_read = min(remaining, _MAX_SINGLE_TRANSFER) |
| got = self.rfile.read(this_read) |
| if not got: |
| break |
| remaining -= len(got) |
| |
| self.send_response(200) |
| self.end_headers() |
| |
| def Connectivity(self): |
| self.send_response(200) |
| self.send_header('Content-Type', 'text/plain') |
| self.end_headers() |
| self.wfile.write('Chromium') |
| |
| |
| class BigBufferHTTPServer(BaseHTTPServer.HTTPServer): |
| """HTTP server with large buffer sizes. |
| |
| Empirically, we see poor performance with cell emulators and default |
| buffer sizes. |
| """ |
| |
| def get_request(self): |
| """Overrides get_request to set buffer sizes. |
| Returns: |
| (socket, address), where socket has been modified for large buffers |
| """ |
| # We can't use super() here because BaseHTTPServer.HTTPServer is an |
| # old-style class. |
| sock, addr = BaseHTTPServer.HTTPServer.get_request(self) |
| sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1 << 24) |
| sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1 << 24) |
| return sock, addr |
| |
| |
| BigBufferHTTPServer(('', 80), HttpHandler).serve_forever() |