summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-12 15:57:10 +0000
committerrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-12 15:57:10 +0000
commit8d68a3e0b0c300cbb8a61aae64cf1b28c10ab93e (patch)
tree52890906250ace5c098d0fe926aca72bc7ffb9c2 /net
parentf24083dd121de728df41d5c3ed579d9e6cbde3d1 (diff)
downloadchromium_src-8d68a3e0b0c300cbb8a61aae64cf1b28c10ab93e.zip
chromium_src-8d68a3e0b0c300cbb8a61aae64cf1b28c10ab93e.tar.gz
chromium_src-8d68a3e0b0c300cbb8a61aae64cf1b28c10ab93e.tar.bz2
Initial CL for Downloads resumption.
(Original work done by ahendrickson@chromium.org) BUG=7648 R=ahendrickson@chromium.org R=asanka@chromium.org Review URL: https://chromiumcodereview.appspot.com/11571025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176530 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rwxr-xr-xnet/tools/testserver/testserver.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index 8bded89..3679a03 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.