| Index: net/tools/testserver/testserver.py
|
| diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
|
| index 8bded89dbcec786f6ed08b0f75cb9cd86a0f7ec2..3679a0349cbc69119e097f85cb97aa59e277c0c7 100755
|
| --- a/net/tools/testserver/testserver.py
|
| +++ b/net/tools/testserver/testserver.py
|
| @@ -28,6 +28,7 @@ import re
|
| import select
|
| import socket
|
| import SocketServer
|
| +import struct
|
| import sys
|
| import threading
|
| import time
|
| @@ -473,6 +474,7 @@ class TestPageHandler(BasePageHandler):
|
| self.MultipartSlowHandler,
|
| self.GetSSLSessionCacheHandler,
|
| self.CloseSocketHandler,
|
| + self.RangeResetHandler,
|
| self.DefaultResponseHandler]
|
| post_handlers = [
|
| self.EchoTitleHandler,
|
| @@ -1622,6 +1624,110 @@ class TestPageHandler(BasePageHandler):
|
| self.wfile.close()
|
| return True
|
|
|
| + def RangeResetHandler(self):
|
| + """Send data broken up by connection resets every N (default 4K) bytes.
|
| + Support range requests. If the data requested doesn't straddle a reset
|
| + boundary, it will all be sent. Used for testing resuming downloads."""
|
| +
|
| + if not self._ShouldHandleRequest('/rangereset'):
|
| + return False
|
| +
|
| + _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
|
| +
|
| + # Defaults
|
| + size = 8000
|
| + # Note that the rst is sent just before sending the rst_boundary byte.
|
| + rst_boundary = 4000
|
| + respond_to_range = True
|
| + hold_for_signal = False
|
| +
|
| + # Parse the query
|
| + qdict = urlparse.parse_qs(query, True)
|
| + if 'size' in qdict:
|
| + size = int(qdict['size'])
|
| + if 'rst_boundary' in qdict:
|
| + rst_boundary = int(qdict['rst_boundary'])
|
| + if 'bounce_range' in qdict:
|
| + respond_to_range = False
|
| + if 'hold' in qdict:
|
| + hold_for_signal = True
|
| +
|
| + first_byte = 0
|
| + last_byte = size - 1
|
| +
|
| + # Does that define what we want to return, or do we need to apply
|
| + # a range?
|
| + range_response = False
|
| + range_header = self.headers.getheader('range')
|
| + if range_header and respond_to_range:
|
| + mo = re.match("bytes=(\d*)-(\d*)", range_header)
|
| + if mo.group(1):
|
| + first_byte = int(mo.group(1))
|
| + if mo.group(2):
|
| + last_byte = int(mo.group(2))
|
| + if last_byte > size - 1:
|
| + last_byte = size - 1
|
| + range_response = True
|
| + if last_byte < first_byte:
|
| + return False
|
| +
|
| + # Set socket send buf high enough that we don't need to worry
|
| + # about asynchronous closes when sending RSTs.
|
| + self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF,
|
| + 16284)
|
| +
|
| + if range_response:
|
| + self.send_response(206)
|
| + self.send_header('Content-Range',
|
| + 'bytes %d-%d/%d' % (first_byte, last_byte, size))
|
| + else:
|
| + self.send_response(200)
|
| + self.send_header('Content-Type', 'application/octet-stream')
|
| + self.send_header('Content-Length', last_byte - first_byte + 1)
|
| + self.end_headers()
|
| +
|
| + if hold_for_signal:
|
| + # TODO(rdsmith/phajdan.jr): http://crbug.com/169519: Without writing
|
| + # a single byte, the self.server.handle_request() below hangs
|
| + # without processing new incoming requests.
|
| + self.wfile.write('X')
|
| + first_byte = first_byte + 1
|
| + # handle requests until one of them clears this flag.
|
| + self.server.waitForDownload = True
|
| + while self.server.waitForDownload:
|
| + self.server.handle_request()
|
| +
|
| + possible_rst = ((first_byte / rst_boundary) + 1) * rst_boundary
|
| + if possible_rst >= last_byte:
|
| + # No RST has been requested in this range, so we don't need to
|
| + # do anything fancy; just write the data and let the python
|
| + # infrastructure close the connection.
|
| + self.wfile.write('X' * (last_byte - first_byte + 1))
|
| + self.wfile.flush()
|
| + return True
|
| +
|
| + # We're resetting the connection part way in; go to the RST
|
| + # boundary and then send an RST.
|
| + # WINDOWS WARNING: On windows, if the amount of data sent before the
|
| + # reset is > 4096, only 4096 bytes will make it across before the RST
|
| + # despite the flush. This is hypothesized to be due to an underlying
|
| + # asynchronous sending implementation, which the 0 second linger
|
| + # forcibly terminates. The amount of data pre-RST should be kept below
|
| + # 4096 for this reason.
|
| + self.wfile.write('X' * (possible_rst - first_byte))
|
| + self.wfile.flush()
|
| + l_onoff = 1 # Linger is active.
|
| + l_linger = 0 # Seconds to linger for.
|
| + self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
|
| + struct.pack('ii', l_onoff, l_linger))
|
| +
|
| + # Close all duplicates of the underlying socket to force the RST.
|
| + self.wfile.close()
|
| + self.rfile.close()
|
| + self.connection.close()
|
| +
|
| + return True
|
| +
|
| def DefaultResponseHandler(self):
|
| """This is the catch-all response handler for requests that aren't handled
|
| by one of the special handlers above.
|
|
|