Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1388)

Side by Side Diff: net/tools/testserver/testserver.py

Issue 11571025: Initial CL for Downloads resumption. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Sync'd to r176084. Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « content/public/test/mock_download_manager.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """This is a simple HTTP/FTP/SYNC/TCP/UDP/ server used for testing Chrome. 6 """This is a simple HTTP/FTP/SYNC/TCP/UDP/ server used for testing Chrome.
7 7
8 It supports several test URLs, as specified by the handlers in TestPageHandler. 8 It supports several test URLs, as specified by the handlers in TestPageHandler.
9 By default, it listens on an ephemeral port and sends the port number back to 9 By default, it listens on an ephemeral port and sends the port number back to
10 the originating process over a pipe. The originating process can specify an 10 the originating process over a pipe. The originating process can specify an
(...skipping 10 matching lines...) Expand all
21 import hashlib 21 import hashlib
22 import json 22 import json
23 import logging 23 import logging
24 import minica 24 import minica
25 import os 25 import os
26 import random 26 import random
27 import re 27 import re
28 import select 28 import select
29 import socket 29 import socket
30 import SocketServer 30 import SocketServer
31 import struct
31 import sys 32 import sys
32 import threading 33 import threading
33 import time 34 import time
34 import urllib 35 import urllib
35 import urlparse 36 import urlparse
36 import zlib 37 import zlib
37 38
38 import echo_message 39 import echo_message
39 import pyftpdlib.ftpserver 40 import pyftpdlib.ftpserver
40 import testserver_base 41 import testserver_base
(...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 self.SlowServerHandler, 467 self.SlowServerHandler,
467 self.ChunkedServerHandler, 468 self.ChunkedServerHandler,
468 self.ContentTypeHandler, 469 self.ContentTypeHandler,
469 self.NoContentHandler, 470 self.NoContentHandler,
470 self.ServerRedirectHandler, 471 self.ServerRedirectHandler,
471 self.ClientRedirectHandler, 472 self.ClientRedirectHandler,
472 self.MultipartHandler, 473 self.MultipartHandler,
473 self.MultipartSlowHandler, 474 self.MultipartSlowHandler,
474 self.GetSSLSessionCacheHandler, 475 self.GetSSLSessionCacheHandler,
475 self.CloseSocketHandler, 476 self.CloseSocketHandler,
477 self.RangeResetHandler,
476 self.DefaultResponseHandler] 478 self.DefaultResponseHandler]
477 post_handlers = [ 479 post_handlers = [
478 self.EchoTitleHandler, 480 self.EchoTitleHandler,
479 self.EchoHandler, 481 self.EchoHandler,
480 self.DeviceManagementHandler, 482 self.DeviceManagementHandler,
481 self.PostOnlyFileHandler] + get_handlers 483 self.PostOnlyFileHandler] + get_handlers
482 put_handlers = [ 484 put_handlers = [
483 self.EchoTitleHandler, 485 self.EchoTitleHandler,
484 self.EchoHandler] + get_handlers 486 self.EchoHandler] + get_handlers
485 head_handlers = [ 487 head_handlers = [
(...skipping 1129 matching lines...) Expand 10 before | Expand all | Expand 10 after
1615 1617
1616 def CloseSocketHandler(self): 1618 def CloseSocketHandler(self):
1617 """Closes the socket without sending anything.""" 1619 """Closes the socket without sending anything."""
1618 1620
1619 if not self._ShouldHandleRequest('/close-socket'): 1621 if not self._ShouldHandleRequest('/close-socket'):
1620 return False 1622 return False
1621 1623
1622 self.wfile.close() 1624 self.wfile.close()
1623 return True 1625 return True
1624 1626
1627 def RangeResetHandler(self):
1628 """Send data broken up by connection resets every N (default 4K) bytes.
1629 Support range requests. If the data requested doesn't straddle a reset
1630 boundary, it will all be sent. Used for testing resuming downloads."""
1631
1632 if not self._ShouldHandleRequest('/rangereset'):
1633 return False
1634
1635 _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
1636
1637 # Defaults
1638 size = 8000
1639 # Note that the rst is sent just before sending the rst_boundary byte.
1640 rst_boundary = 4000
1641 respond_to_range = True
1642 hold_for_signal = False
1643
1644 # Parse the query
1645 qdict = urlparse.parse_qs(query, True)
1646 if 'size' in qdict:
1647 size = int(qdict['size'])
1648 if 'rst_boundary' in qdict:
1649 rst_boundary = int(qdict['rst_boundary'])
1650 if 'bounce_range' in qdict:
1651 respond_to_range = False
1652 if 'hold' in qdict:
1653 hold_for_signal = True
1654
1655 first_byte = 0
1656 last_byte = size - 1
1657
1658 # Does that define what we want to return, or do we need to apply
1659 # a range?
1660 range_response = False
1661 range_header = self.headers.getheader('range')
1662 if range_header and respond_to_range:
1663 mo = re.match("bytes=(\d*)-(\d*)", range_header)
1664 if mo.group(1):
1665 first_byte = int(mo.group(1))
1666 if mo.group(2):
1667 last_byte = int(mo.group(2))
1668 if last_byte > size - 1:
1669 last_byte = size - 1
1670 range_response = True
1671 if last_byte < first_byte:
1672 return False
1673
1674 # Set socket send buf high enough that we don't need to worry
1675 # about asynchronous closes when sending RSTs.
1676 self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF,
1677 16284)
1678
1679 if range_response:
1680 self.send_response(206)
1681 self.send_header('Content-Range',
1682 'bytes %d-%d/%d' % (first_byte, last_byte, size))
1683 else:
1684 self.send_response(200)
1685 self.send_header('Content-Type', 'application/octet-stream')
1686 self.send_header('Content-Length', last_byte - first_byte + 1)
1687 self.end_headers()
1688
1689 if hold_for_signal:
1690 # Hack alert/explanation: Without writing a single byte, the
Paweł Hajdan Jr. 2013/01/10 23:50:22 Please file a bug for this and put a TODO here.
Randy Smith (Not in Mondays) 2013/01/11 19:28:34 Done.
1691 # self.server.handle_request() below hangs without processing
1692 # new incoming requests. Presumably this is due to a bug in
1693 # BaseHTTPRequestHandler.
1694 self.wfile.write('X')
1695 first_byte = first_byte + 1
1696 # handle requests until one of them clears this flag.
1697 self.server.waitForDownload = True
1698 while self.server.waitForDownload:
1699 self.server.handle_request()
1700
1701 possible_rst = ((first_byte / rst_boundary) + 1) * rst_boundary
1702 if possible_rst >= last_byte:
1703 # No RST has been requested in this range, so we don't need to
1704 # do anything fancy; just write the data and let the python
1705 # infrastructure close the connection.
1706 self.wfile.write('X' * (last_byte - first_byte + 1))
1707 self.wfile.flush()
1708 return True
1709
1710 # We're resetting the connection part way in; go to the RST
1711 # boundary and then send an RST.
1712 # WINDOWS WARNING: On windows, if the amount of data sent before the
1713 # reset is > 4096, only 4096 bytes will make it across before the RST
1714 # despite the flush. This is hypothesized to be due to an underlying
1715 # asynchronous sending implementation, which the 0 second linger
1716 # forcibly terminates. The amount of data pre-RST should be kept below
1717 # 4096 for this reason.
1718 self.wfile.write('X' * (possible_rst - first_byte))
1719 self.wfile.flush()
1720 l_onoff = 1 # Linger is active.
1721 l_linger = 0 # Seconds to linger for.
1722 self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
1723 struct.pack('ii', l_onoff, l_linger))
1724
1725 # Close all duplicates of the underlying socket to force the RST.
1726 self.wfile.close()
1727 self.rfile.close()
1728 self.connection.close()
1729
1730 return True
1731
1625 def DefaultResponseHandler(self): 1732 def DefaultResponseHandler(self):
1626 """This is the catch-all response handler for requests that aren't handled 1733 """This is the catch-all response handler for requests that aren't handled
1627 by one of the special handlers above. 1734 by one of the special handlers above.
1628 Note that we specify the content-length as without it the https connection 1735 Note that we specify the content-length as without it the https connection
1629 is not closed properly (and the browser keeps expecting data).""" 1736 is not closed properly (and the browser keeps expecting data)."""
1630 1737
1631 contents = "Default response given for path: " + self.path 1738 contents = "Default response given for path: " + self.path
1632 self.send_response(200) 1739 self.send_response(200)
1633 self.send_header('Content-Type', 'text/html') 1740 self.send_header('Content-Type', 'text/html')
1634 self.send_header('Content-Length', len(contents)) 1741 self.send_header('Content-Length', len(contents))
(...skipping 808 matching lines...) Expand 10 before | Expand all | Expand 10 after
2443 'load multipe keys into the server. If the ' 2550 'load multipe keys into the server. If the '
2444 'server has multiple keys, it will rotate ' 2551 'server has multiple keys, it will rotate '
2445 'through them in at each request a ' 2552 'through them in at each request a '
2446 'round-robin fashion. The server will ' 2553 'round-robin fashion. The server will '
2447 'generate a random key if none is specified ' 2554 'generate a random key if none is specified '
2448 'on the command line.') 2555 'on the command line.')
2449 2556
2450 2557
2451 if __name__ == '__main__': 2558 if __name__ == '__main__':
2452 sys.exit(ServerRunner().main()) 2559 sys.exit(ServerRunner().main())
OLDNEW
« no previous file with comments | « content/public/test/mock_download_manager.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698