blob: 3d4e511a76269b298552e5977c033067e9dd75ba [file] [log] [blame]
#!/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()