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 1007 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1364 except ValueError: | 1363 except ValueError: |
1365 pass | 1364 pass |
1366 time.sleep(0.001 * chunkedSettings['waitBeforeHeaders']) | 1365 time.sleep(0.001 * chunkedSettings['waitBeforeHeaders']) |
1367 self.protocol_version = 'HTTP/1.1' # Needed for chunked encoding | 1366 self.protocol_version = 'HTTP/1.1' # Needed for chunked encoding |
1368 self.send_response(200) | 1367 self.send_response(200) |
1369 self.send_header('Content-Type', 'text/plain') | 1368 self.send_header('Content-Type', 'text/plain') |
1370 self.send_header('Connection', 'close') | 1369 self.send_header('Connection', 'close') |
1371 self.send_header('Transfer-Encoding', 'chunked') | 1370 self.send_header('Transfer-Encoding', 'chunked') |
1372 self.end_headers() | 1371 self.end_headers() |
1373 # Chunked encoding: sending all chunks, then final zero-length chunk and | 1372 # Chunked encoding: sending all chunks, then final zero-length chunk and |
1374 # then final CRLF. | 1373 #then final CRLF. |
svaldez
2015/11/11 20:42:41
Extra change?
asanka
2015/11/11 20:57:25
Indeed. Undone.
| |
1375 for i in range(0, chunkedSettings['chunksNumber']): | 1374 for i in range(0, chunkedSettings['chunksNumber']): |
1376 if i > 0: | 1375 if i > 0: |
1377 time.sleep(0.001 * chunkedSettings['waitBetweenChunks']) | 1376 time.sleep(0.001 * chunkedSettings['waitBetweenChunks']) |
1378 self.sendChunkHelp('*' * chunkedSettings['chunkSize']) | 1377 self.sendChunkHelp('*' * chunkedSettings['chunkSize']) |
1379 self.wfile.flush() # Keep in mind that we start flushing only after 1kb. | 1378 self.wfile.flush() # Keep in mind that we start flushing only after 1kb. |
1380 self.sendChunkHelp('') | 1379 self.sendChunkHelp('') |
1381 return True | 1380 return True |
1382 | 1381 |
1383 def NoContentHandler(self): | 1382 def NoContentHandler(self): |
1384 """Returns a 204 No Content response.""" | 1383 """Returns a 204 No Content response.""" |
(...skipping 168 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 if range_header and respond_to_range: | |
1633 mo = re.match("bytes=(\d*)-(\d*)", range_header) | |
1634 if mo.group(1): | |
1635 first_byte = int(mo.group(1)) | |
1636 if mo.group(2): | |
1637 last_byte = int(mo.group(2)) | |
1638 if last_byte > size - 1: | |
1639 last_byte = size - 1 | |
1640 range_response = True | |
1641 if last_byte < first_byte: | |
1642 return False | |
1643 | |
1644 if (fail_precondition and | |
1645 (self.headers.getheader('If-Modified-Since') or | |
1646 self.headers.getheader('If-Match'))): | |
1647 self.send_response(412) | |
1648 self.end_headers() | |
1649 return True | |
1650 | |
1651 if range_response: | |
1652 self.send_response(206) | |
1653 self.send_header('Content-Range', | |
1654 'bytes %d-%d/%d' % (first_byte, last_byte, size)) | |
1655 else: | |
1656 self.send_response(200) | |
1657 self.send_header('Content-Type', 'application/octet-stream') | |
1658 self.send_header('Content-Length', last_byte - first_byte + 1) | |
1659 if send_verifiers: | |
1660 # If fail_precondition is non-zero, then the ETag for each request will be | |
1661 # different. | |
1662 etag = "%s%d" % (token, fail_precondition) | |
1663 self.send_header('ETag', etag) | |
1664 self.send_header('Last-Modified', 'Tue, 19 Feb 2013 14:32 EST') | |
1665 self.end_headers() | |
1666 | |
1667 if hold_for_signal: | |
1668 # TODO(rdsmith/phajdan.jr): http://crbug.com/169519: Without writing | |
1669 # a single byte, the self.server.handle_request() below hangs | |
1670 # without processing new incoming requests. | |
1671 self.wfile.write(DataForRange(first_byte, first_byte + 1)) | |
1672 first_byte = first_byte + 1 | |
1673 # handle requests until one of them clears this flag. | |
1674 self.server.wait_for_download = True | |
1675 while self.server.wait_for_download: | |
1676 self.server.handle_request() | |
1677 | |
1678 possible_rst = ((first_byte / rst_boundary) + 1) * rst_boundary | |
1679 if possible_rst >= last_byte or rst_limit == 0: | |
1680 # No RST has been requested in this range, so we don't need to | |
1681 # do anything fancy; just write the data and let the python | |
1682 # infrastructure close the connection. | |
1683 self.wfile.write(DataForRange(first_byte, last_byte + 1)) | |
1684 self.wfile.flush() | |
1685 return True | |
1686 | |
1687 # We're resetting the connection part way in; go to the RST | |
1688 # boundary and then send an RST. | |
1689 # Because socket semantics do not guarantee that all the data will be | |
1690 # sent when using the linger semantics to hard close a socket, | |
1691 # we send the data and then wait for our peer to release us | |
1692 # before sending the reset. | |
1693 data = DataForRange(first_byte, possible_rst) | |
1694 self.wfile.write(data) | |
1695 self.wfile.flush() | |
1696 self.server.wait_for_download = True | |
1697 while self.server.wait_for_download: | |
1698 self.server.handle_request() | |
1699 l_onoff = 1 # Linger is active. | |
1700 l_linger = 0 # Seconds to linger for. | |
1701 self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, | |
1702 struct.pack('ii', l_onoff, l_linger)) | |
1703 | |
1704 # Close all duplicates of the underlying socket to force the RST. | |
1705 self.wfile.close() | |
1706 self.rfile.close() | |
1707 self.connection.close() | |
1708 | |
1709 return True | |
1710 | |
1711 def DefaultResponseHandler(self): | 1562 def DefaultResponseHandler(self): |
1712 """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 |
1713 by one of the special handlers above. | 1564 by one of the special handlers above. |
1714 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 |
1715 is not closed properly (and the browser keeps expecting data).""" | 1566 is not closed properly (and the browser keeps expecting data).""" |
1716 | 1567 |
1717 contents = "Default response given for path: " + self.path | 1568 contents = "Default response given for path: " + self.path |
1718 self.send_response(200) | 1569 self.send_response(200) |
1719 self.send_header('Content-Type', 'text/html') | 1570 self.send_header('Content-Type', 'text/html') |
1720 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... | |
2310 'an anonymous user.') | 2161 'an anonymous user.') |
2311 self.option_parser.add_option('--disable-channel-id', action='store_true') | 2162 self.option_parser.add_option('--disable-channel-id', action='store_true') |
2312 self.option_parser.add_option('--disable-extended-master-secret', | 2163 self.option_parser.add_option('--disable-extended-master-secret', |
2313 action='store_true') | 2164 action='store_true') |
2314 self.option_parser.add_option('--token-binding-params', action='append', | 2165 self.option_parser.add_option('--token-binding-params', action='append', |
2315 default=[], type='int') | 2166 default=[], type='int') |
2316 | 2167 |
2317 | 2168 |
2318 if __name__ == '__main__': | 2169 if __name__ == '__main__': |
2319 sys.exit(ServerRunner().main()) | 2170 sys.exit(ServerRunner().main()) |
OLD | NEW |