| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 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/TCP/UDP/BASIC_AUTH_PROXY/WEBSOCKET server used for | 6 """This is a simple HTTP/FTP/TCP/UDP/BASIC_AUTH_PROXY/WEBSOCKET server used for |
| 7 testing Chrome. | 7 testing Chrome. |
| 8 | 8 |
| 9 It supports several test URLs, as specified by the handlers in TestPageHandler. | 9 It supports several test URLs, as specified by the handlers in TestPageHandler. |
| 10 By default, it listens on an ephemeral port and sends the port number back to | 10 By default, it listens on an ephemeral port and sends the port number back to |
| (...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 336 self.NoContentHandler, | 336 self.NoContentHandler, |
| 337 self.ServerRedirectHandler, | 337 self.ServerRedirectHandler, |
| 338 self.CrossSiteRedirectHandler, | 338 self.CrossSiteRedirectHandler, |
| 339 self.ClientRedirectHandler, | 339 self.ClientRedirectHandler, |
| 340 self.GetSSLSessionCacheHandler, | 340 self.GetSSLSessionCacheHandler, |
| 341 self.SSLManySmallRecords, | 341 self.SSLManySmallRecords, |
| 342 self.GetChannelID, | 342 self.GetChannelID, |
| 343 self.GetClientCert, | 343 self.GetClientCert, |
| 344 self.ClientCipherListHandler, | 344 self.ClientCipherListHandler, |
| 345 self.CloseSocketHandler, | 345 self.CloseSocketHandler, |
| 346 self.RangeResetHandler, | |
| 347 self.DefaultResponseHandler] | 346 self.DefaultResponseHandler] |
| 348 post_handlers = [ | 347 post_handlers = [ |
| 349 self.EchoTitleHandler, | 348 self.EchoTitleHandler, |
| 350 self.EchoHandler, | 349 self.EchoHandler, |
| 351 self.PostOnlyFileHandler, | 350 self.PostOnlyFileHandler, |
| 352 self.EchoMultipartPostHandler] + get_handlers | 351 self.EchoMultipartPostHandler] + get_handlers |
| 353 put_handlers = [ | 352 put_handlers = [ |
| 354 self.EchoTitleHandler, | 353 self.EchoTitleHandler, |
| 355 self.EchoHandler] + get_handlers | 354 self.EchoHandler] + get_handlers |
| 356 head_handlers = [ | 355 head_handlers = [ |
| (...skipping 1196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1553 | 1552 |
| 1554 def CloseSocketHandler(self): | 1553 def CloseSocketHandler(self): |
| 1555 """Closes the socket without sending anything.""" | 1554 """Closes the socket without sending anything.""" |
| 1556 | 1555 |
| 1557 if not self._ShouldHandleRequest('/close-socket'): | 1556 if not self._ShouldHandleRequest('/close-socket'): |
| 1558 return False | 1557 return False |
| 1559 | 1558 |
| 1560 self.wfile.close() | 1559 self.wfile.close() |
| 1561 return True | 1560 return True |
| 1562 | 1561 |
| 1563 def RangeResetHandler(self): | |
| 1564 """Send data broken up by connection resets every N (default 4K) bytes. | |
| 1565 Support range requests. If the data requested doesn't straddle a reset | |
| 1566 boundary, it will all be sent. Used for testing resuming downloads.""" | |
| 1567 | |
| 1568 def DataForRange(start, end): | |
| 1569 """Data to be provided for a particular range of bytes.""" | |
| 1570 # Offset and scale to avoid too obvious (and hence potentially | |
| 1571 # collidable) data. | |
| 1572 return ''.join([chr(y % 256) | |
| 1573 for y in range(start * 2 + 15, end * 2 + 15, 2)]) | |
| 1574 | |
| 1575 if not self._ShouldHandleRequest('/rangereset'): | |
| 1576 return False | |
| 1577 | |
| 1578 # HTTP/1.1 is required for ETag and range support. | |
| 1579 self.protocol_version = 'HTTP/1.1' | |
| 1580 _, _, url_path, _, query, _ = urlparse.urlparse(self.path) | |
| 1581 | |
| 1582 # Defaults | |
| 1583 size = 8000 | |
| 1584 # Note that the rst is sent just before sending the rst_boundary byte. | |
| 1585 rst_boundary = 4000 | |
| 1586 respond_to_range = True | |
| 1587 hold_for_signal = False | |
| 1588 rst_limit = -1 | |
| 1589 token = 'DEFAULT' | |
| 1590 fail_precondition = 0 | |
| 1591 send_verifiers = True | |
| 1592 | |
| 1593 # Parse the query | |
| 1594 qdict = urlparse.parse_qs(query, True) | |
| 1595 if 'size' in qdict: | |
| 1596 size = int(qdict['size'][0]) | |
| 1597 if 'rst_boundary' in qdict: | |
| 1598 rst_boundary = int(qdict['rst_boundary'][0]) | |
| 1599 if 'token' in qdict: | |
| 1600 # Identifying token for stateful tests. | |
| 1601 token = qdict['token'][0] | |
| 1602 if 'rst_limit' in qdict: | |
| 1603 # Max number of rsts for a given token. | |
| 1604 rst_limit = int(qdict['rst_limit'][0]) | |
| 1605 if 'bounce_range' in qdict: | |
| 1606 respond_to_range = False | |
| 1607 if 'hold' in qdict: | |
| 1608 # Note that hold_for_signal will not work with null range requests; | |
| 1609 # see TODO below. | |
| 1610 hold_for_signal = True | |
| 1611 if 'no_verifiers' in qdict: | |
| 1612 send_verifiers = False | |
| 1613 if 'fail_precondition' in qdict: | |
| 1614 fail_precondition = int(qdict['fail_precondition'][0]) | |
| 1615 | |
| 1616 # Record already set information, or set it. | |
| 1617 rst_limit = TestPageHandler.rst_limits.setdefault(token, rst_limit) | |
| 1618 if rst_limit != 0: | |
| 1619 TestPageHandler.rst_limits[token] -= 1 | |
| 1620 fail_precondition = TestPageHandler.fail_precondition.setdefault( | |
| 1621 token, fail_precondition) | |
| 1622 if fail_precondition != 0: | |
| 1623 TestPageHandler.fail_precondition[token] -= 1 | |
| 1624 | |
| 1625 first_byte = 0 | |
| 1626 last_byte = size - 1 | |
| 1627 | |
| 1628 # Does that define what we want to return, or do we need to apply | |
| 1629 # a range? | |
| 1630 range_response = False | |
| 1631 range_header = self.headers.getheader('range') | |
| 1632 | |
| 1633 if fail_precondition and self.headers.getheader('If-Range'): | |
| 1634 # Failing a precondition for an If-Range just means that we are going to | |
| 1635 # return the entire entity ignoring the Range header. | |
| 1636 respond_to_range = False | |
| 1637 | |
| 1638 if range_header and respond_to_range: | |
| 1639 mo = re.match("bytes=(\d*)-(\d*)", range_header) | |
| 1640 if mo.group(1): | |
| 1641 first_byte = int(mo.group(1)) | |
| 1642 if mo.group(2): | |
| 1643 last_byte = int(mo.group(2)) | |
| 1644 if last_byte > size - 1: | |
| 1645 last_byte = size - 1 | |
| 1646 range_response = True | |
| 1647 if last_byte < first_byte: | |
| 1648 return False | |
| 1649 | |
| 1650 if range_response: | |
| 1651 self.send_response(206) | |
| 1652 self.send_header('Content-Range', | |
| 1653 'bytes %d-%d/%d' % (first_byte, last_byte, size)) | |
| 1654 else: | |
| 1655 self.send_response(200) | |
| 1656 self.send_header('Content-Type', 'application/octet-stream') | |
| 1657 self.send_header('Content-Length', last_byte - first_byte + 1) | |
| 1658 if send_verifiers: | |
| 1659 # If fail_precondition is non-zero, then the ETag for each request will be | |
| 1660 # different. | |
| 1661 etag = "%s%d" % (token, fail_precondition) | |
| 1662 self.send_header('ETag', etag) | |
| 1663 self.send_header('Last-Modified', 'Tue, 19 Feb 2013 14:32 EST') | |
| 1664 self.end_headers() | |
| 1665 | |
| 1666 if hold_for_signal: | |
| 1667 # TODO(rdsmith/phajdan.jr): http://crbug.com/169519: Without writing | |
| 1668 # a single byte, the self.server.handle_request() below hangs | |
| 1669 # without processing new incoming requests. | |
| 1670 self.wfile.write(DataForRange(first_byte, first_byte + 1)) | |
| 1671 first_byte = first_byte + 1 | |
| 1672 # handle requests until one of them clears this flag. | |
| 1673 self.server.wait_for_download = True | |
| 1674 while self.server.wait_for_download: | |
| 1675 self.server.handle_request() | |
| 1676 | |
| 1677 possible_rst = ((first_byte / rst_boundary) + 1) * rst_boundary | |
| 1678 if possible_rst >= last_byte or rst_limit == 0: | |
| 1679 # No RST has been requested in this range, so we don't need to | |
| 1680 # do anything fancy; just write the data and let the python | |
| 1681 # infrastructure close the connection. | |
| 1682 self.wfile.write(DataForRange(first_byte, last_byte + 1)) | |
| 1683 self.wfile.flush() | |
| 1684 return True | |
| 1685 | |
| 1686 # We're resetting the connection part way in; go to the RST | |
| 1687 # boundary and then send an RST. | |
| 1688 # Because socket semantics do not guarantee that all the data will be | |
| 1689 # sent when using the linger semantics to hard close a socket, | |
| 1690 # we send the data and then wait for our peer to release us | |
| 1691 # before sending the reset. | |
| 1692 data = DataForRange(first_byte, possible_rst) | |
| 1693 self.wfile.write(data) | |
| 1694 self.wfile.flush() | |
| 1695 self.server.wait_for_download = True | |
| 1696 while self.server.wait_for_download: | |
| 1697 self.server.handle_request() | |
| 1698 l_onoff = 1 # Linger is active. | |
| 1699 l_linger = 0 # Seconds to linger for. | |
| 1700 self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, | |
| 1701 struct.pack('ii', l_onoff, l_linger)) | |
| 1702 | |
| 1703 # Close all duplicates of the underlying socket to force the RST. | |
| 1704 self.wfile.close() | |
| 1705 self.rfile.close() | |
| 1706 self.connection.close() | |
| 1707 | |
| 1708 return True | |
| 1709 | |
| 1710 def DefaultResponseHandler(self): | 1562 def DefaultResponseHandler(self): |
| 1711 """This is the catch-all response handler for requests that aren't handled | 1563 """This is the catch-all response handler for requests that aren't handled |
| 1712 by one of the special handlers above. | 1564 by one of the special handlers above. |
| 1713 Note that we specify the content-length as without it the https connection | 1565 Note that we specify the content-length as without it the https connection |
| 1714 is not closed properly (and the browser keeps expecting data).""" | 1566 is not closed properly (and the browser keeps expecting data).""" |
| 1715 | 1567 |
| 1716 contents = "Default response given for path: " + self.path | 1568 contents = "Default response given for path: " + self.path |
| 1717 self.send_response(200) | 1569 self.send_response(200) |
| 1718 self.send_header('Content-Type', 'text/html') | 1570 self.send_header('Content-Type', 'text/html') |
| 1719 self.send_header('Content-Length', len(contents)) | 1571 self.send_header('Content-Length', len(contents)) |
| (...skipping 589 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2309 'an anonymous user.') | 2161 'an anonymous user.') |
| 2310 self.option_parser.add_option('--disable-channel-id', action='store_true') | 2162 self.option_parser.add_option('--disable-channel-id', action='store_true') |
| 2311 self.option_parser.add_option('--disable-extended-master-secret', | 2163 self.option_parser.add_option('--disable-extended-master-secret', |
| 2312 action='store_true') | 2164 action='store_true') |
| 2313 self.option_parser.add_option('--token-binding-params', action='append', | 2165 self.option_parser.add_option('--token-binding-params', action='append', |
| 2314 default=[], type='int') | 2166 default=[], type='int') |
| 2315 | 2167 |
| 2316 | 2168 |
| 2317 if __name__ == '__main__': | 2169 if __name__ == '__main__': |
| 2318 sys.exit(ServerRunner().main()) | 2170 sys.exit(ServerRunner().main()) |
| OLD | NEW |