OLD | NEW |
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 |
11 explicit port if necessary. | 11 explicit port if necessary. |
12 It can use https if you specify the flag --https=CERT where CERT is the path | 12 It can use https if you specify the flag --https=CERT where CERT is the path |
13 to a pem file containing the certificate and private key that should be used. | 13 to a pem file containing the certificate and private key that should be used. |
14 """ | 14 """ |
15 | 15 |
16 import asyncore | 16 import asyncore |
17 import base64 | 17 import base64 |
18 import BaseHTTPServer | 18 import BaseHTTPServer |
19 import cgi | 19 import cgi |
20 import errno | 20 import errno |
21 import httplib | 21 import httplib |
| 22 import minica |
22 import optparse | 23 import optparse |
23 import os | 24 import os |
24 import random | 25 import random |
25 import re | 26 import re |
26 import select | 27 import select |
| 28 import socket |
27 import SocketServer | 29 import SocketServer |
28 import socket | 30 import struct |
29 import sys | 31 import sys |
30 import struct | 32 import threading |
31 import time | 33 import time |
32 import urllib | 34 import urllib |
33 import urlparse | 35 import urlparse |
34 import warnings | 36 import warnings |
35 import zlib | 37 import zlib |
36 | 38 |
37 # Ignore deprecation warnings, they make our output more cluttered. | 39 # Ignore deprecation warnings, they make our output more cluttered. |
38 warnings.filterwarnings("ignore", category=DeprecationWarning) | 40 warnings.filterwarnings("ignore", category=DeprecationWarning) |
39 | 41 |
40 import echo_message | 42 import echo_message |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 | 100 |
99 def serve_forever(self): | 101 def serve_forever(self): |
100 self.stop = False | 102 self.stop = False |
101 self.nonce_time = None | 103 self.nonce_time = None |
102 while not self.stop: | 104 while not self.stop: |
103 self.handle_request() | 105 self.handle_request() |
104 self.socket.close() | 106 self.socket.close() |
105 | 107 |
106 | 108 |
107 class HTTPServer(ClientRestrictingServerMixIn, StoppableHTTPServer): | 109 class HTTPServer(ClientRestrictingServerMixIn, StoppableHTTPServer): |
108 """This is a specialization of StoppableHTTPerver that adds client | 110 """This is a specialization of StoppableHTTPServer that adds client |
109 verification.""" | 111 verification.""" |
110 | 112 |
111 pass | 113 pass |
112 | 114 |
| 115 class OCSPServer(ClientRestrictingServerMixIn, BaseHTTPServer.HTTPServer): |
| 116 """This is a specialization of HTTPServer that serves an |
| 117 OCSP response""" |
| 118 |
| 119 def serve_forever_on_thread(self): |
| 120 self.thread = threading.Thread(target = self.serve_forever, |
| 121 name = "OCSPServerThread") |
| 122 self.thread.start() |
| 123 |
| 124 def stop_serving(self): |
| 125 self.shutdown() |
| 126 self.thread.join() |
113 | 127 |
114 class HTTPSServer(tlslite.api.TLSSocketServerMixIn, | 128 class HTTPSServer(tlslite.api.TLSSocketServerMixIn, |
115 ClientRestrictingServerMixIn, | 129 ClientRestrictingServerMixIn, |
116 StoppableHTTPServer): | 130 StoppableHTTPServer): |
117 """This is a specialization of StoppableHTTPerver that add https support and | 131 """This is a specialization of StoppableHTTPServer that add https support and |
118 client verification.""" | 132 client verification.""" |
119 | 133 |
120 def __init__(self, server_address, request_hander_class, cert_path, | 134 def __init__(self, server_address, request_hander_class, pem_cert_and_key, |
121 ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers, | 135 ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers, |
122 record_resume_info): | 136 record_resume_info): |
123 s = open(cert_path).read() | 137 self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key) |
124 self.cert_chain = tlslite.api.X509CertChain().parseChain(s) | 138 self.private_key = tlslite.api.parsePEMKey(pem_cert_and_key, private=True) |
125 s = open(cert_path).read() | |
126 self.private_key = tlslite.api.parsePEMKey(s, private=True) | |
127 self.ssl_client_auth = ssl_client_auth | 139 self.ssl_client_auth = ssl_client_auth |
128 self.ssl_client_cas = [] | 140 self.ssl_client_cas = [] |
129 for ca_file in ssl_client_cas: | 141 for ca_file in ssl_client_cas: |
130 s = open(ca_file).read() | 142 s = open(ca_file).read() |
131 x509 = tlslite.api.X509() | 143 x509 = tlslite.api.X509() |
132 x509.parse(s) | 144 x509.parse(s) |
133 self.ssl_client_cas.append(x509.subject) | 145 self.ssl_client_cas.append(x509.subject) |
134 self.ssl_handshake_settings = tlslite.api.HandshakeSettings() | 146 self.ssl_handshake_settings = tlslite.api.HandshakeSettings() |
135 if ssl_bulk_ciphers is not None: | 147 if ssl_bulk_ciphers is not None: |
136 self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers | 148 self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers |
(...skipping 1732 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1869 # Create the default path to our data dir, relative to the exe dir. | 1881 # Create the default path to our data dir, relative to the exe dir. |
1870 my_data_dir = os.path.dirname(sys.argv[0]) | 1882 my_data_dir = os.path.dirname(sys.argv[0]) |
1871 my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..", | 1883 my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..", |
1872 "test", "data") | 1884 "test", "data") |
1873 | 1885 |
1874 #TODO(ibrar): Must use Find* funtion defined in google\tools | 1886 #TODO(ibrar): Must use Find* funtion defined in google\tools |
1875 #i.e my_data_dir = FindUpward(my_data_dir, "test", "data") | 1887 #i.e my_data_dir = FindUpward(my_data_dir, "test", "data") |
1876 | 1888 |
1877 return my_data_dir | 1889 return my_data_dir |
1878 | 1890 |
| 1891 class OCSPHandler(BasePageHandler): |
| 1892 def __init__(self, request, client_address, socket_server): |
| 1893 handlers = [self.OCSPResponse] |
| 1894 self.ocsp_response = socket_server.ocsp_response |
| 1895 BasePageHandler.__init__(self, request, client_address, socket_server, |
| 1896 handlers, handlers, handlers, |
| 1897 handlers, handlers) |
| 1898 |
| 1899 def OCSPResponse(self): |
| 1900 self.send_response(200) |
| 1901 self.send_header('Content-Type', 'application/ocsp-response') |
| 1902 self.send_header('Content-Length', str(len(self.ocsp_response))) |
| 1903 self.end_headers() |
| 1904 |
| 1905 self.wfile.write(self.ocsp_response) |
1879 | 1906 |
1880 class TCPEchoHandler(SocketServer.BaseRequestHandler): | 1907 class TCPEchoHandler(SocketServer.BaseRequestHandler): |
1881 """The RequestHandler class for TCP echo server. | 1908 """The RequestHandler class for TCP echo server. |
1882 | 1909 |
1883 It is instantiated once per connection to the server, and overrides the | 1910 It is instantiated once per connection to the server, and overrides the |
1884 handle() method to implement communication to the client. | 1911 handle() method to implement communication to the client. |
1885 """ | 1912 """ |
1886 | 1913 |
1887 def handle(self): | 1914 def handle(self): |
1888 """Handles the request from the client and constructs a response.""" | 1915 """Handles the request from the client and constructs a response.""" |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1949 sys.stdout = FileMultiplexer(sys.stdout, logfile) | 1976 sys.stdout = FileMultiplexer(sys.stdout, logfile) |
1950 else: | 1977 else: |
1951 sys.stdout = logfile | 1978 sys.stdout = logfile |
1952 | 1979 |
1953 port = options.port | 1980 port = options.port |
1954 host = options.host | 1981 host = options.host |
1955 | 1982 |
1956 server_data = {} | 1983 server_data = {} |
1957 server_data['host'] = host | 1984 server_data['host'] = host |
1958 | 1985 |
| 1986 ocsp_server = None |
| 1987 |
1959 if options.server_type == SERVER_HTTP: | 1988 if options.server_type == SERVER_HTTP: |
1960 if options.cert: | 1989 if options.cert: |
1961 # let's make sure the cert file exists. | 1990 pem_cert_and_key = None |
1962 if not os.path.isfile(options.cert): | 1991 if options.cert.startswith('__'): |
1963 print 'specified server cert file not found: ' + options.cert + \ | 1992 ocsp_server = OCSPServer((host, 0), OCSPHandler) |
1964 ' exiting...' | 1993 print 'OCSP server on port ' + str(ocsp_server.server_port) |
1965 return | 1994 |
| 1995 ocsp_der = None |
| 1996 ocsp_revoked = False |
| 1997 ocsp_invalid = False |
| 1998 |
| 1999 if options.cert == '__ocsp_ok__': |
| 2000 pass |
| 2001 elif options.cert == '__ocsp_revoked__': |
| 2002 ocsp_revoked = True |
| 2003 elif options.cert == '__ocsp_invalid__': |
| 2004 ocsp_invalid = True |
| 2005 else: |
| 2006 print 'unknown special certificate type: ' + options.cert |
| 2007 return |
| 2008 |
| 2009 (pem_cert_and_key, ocsp_der) = \ |
| 2010 minica.GenerateCertKeyAndOCSP( |
| 2011 subject = "127.0.0.1", |
| 2012 ocsp_url = "http://127.0.0.1:%d/ocsp" % ocsp_server.server_port, |
| 2013 ocsp_revoked = ocsp_revoked) |
| 2014 |
| 2015 if ocsp_invalid: |
| 2016 ocsp_der = '3' |
| 2017 |
| 2018 ocsp_server.ocsp_response = ocsp_der |
| 2019 else: |
| 2020 # options.cert is a filename. Let's make sure the cert file exists. |
| 2021 if not os.path.isfile(options.cert): |
| 2022 print 'specified server cert file not found: ' + options.cert + \ |
| 2023 ' exiting...' |
| 2024 return |
| 2025 pem_cert_and_key = file(options.cert, 'r').read() |
| 2026 |
1966 for ca_cert in options.ssl_client_ca: | 2027 for ca_cert in options.ssl_client_ca: |
1967 if not os.path.isfile(ca_cert): | 2028 if not os.path.isfile(ca_cert): |
1968 print 'specified trusted client CA file not found: ' + ca_cert + \ | 2029 print 'specified trusted client CA file not found: ' + ca_cert + \ |
1969 ' exiting...' | 2030 ' exiting...' |
1970 return | 2031 return |
1971 server = HTTPSServer((host, port), TestPageHandler, options.cert, | 2032 server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key, |
1972 options.ssl_client_auth, options.ssl_client_ca, | 2033 options.ssl_client_auth, options.ssl_client_ca, |
1973 options.ssl_bulk_cipher, options.record_resume) | 2034 options.ssl_bulk_cipher, options.record_resume) |
1974 print 'HTTPS server started on %s:%d...' % (host, server.server_port) | 2035 print 'HTTPS server started on %s:%d...' % (host, server.server_port) |
1975 else: | 2036 else: |
1976 server = HTTPServer((host, port), TestPageHandler) | 2037 server = HTTPServer((host, port), TestPageHandler) |
1977 print 'HTTP server started on %s:%d...' % (host, server.server_port) | 2038 print 'HTTP server started on %s:%d...' % (host, server.server_port) |
1978 | 2039 |
1979 server.data_dir = MakeDataDir() | 2040 server.data_dir = MakeDataDir() |
1980 server.file_root_url = options.file_root_url | 2041 server.file_root_url = options.file_root_url |
1981 server_data['port'] = server.server_port | 2042 server_data['port'] = server.server_port |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2041 else: | 2102 else: |
2042 fd = options.startup_pipe | 2103 fd = options.startup_pipe |
2043 startup_pipe = os.fdopen(fd, "w") | 2104 startup_pipe = os.fdopen(fd, "w") |
2044 # First write the data length as an unsigned 4-byte value. This | 2105 # First write the data length as an unsigned 4-byte value. This |
2045 # is _not_ using network byte ordering since the other end of the | 2106 # is _not_ using network byte ordering since the other end of the |
2046 # pipe is on the same machine. | 2107 # pipe is on the same machine. |
2047 startup_pipe.write(struct.pack('=L', server_data_len)) | 2108 startup_pipe.write(struct.pack('=L', server_data_len)) |
2048 startup_pipe.write(server_data_json) | 2109 startup_pipe.write(server_data_json) |
2049 startup_pipe.close() | 2110 startup_pipe.close() |
2050 | 2111 |
| 2112 if ocsp_server is not None: |
| 2113 ocsp_server.serve_forever_on_thread() |
| 2114 |
2051 try: | 2115 try: |
2052 server.serve_forever() | 2116 server.serve_forever() |
2053 except KeyboardInterrupt: | 2117 except KeyboardInterrupt: |
2054 print 'shutting down server' | 2118 print 'shutting down server' |
| 2119 if ocsp_server is not None: |
| 2120 ocsp_server.stop_serving() |
2055 server.stop = True | 2121 server.stop = True |
2056 | 2122 |
2057 if __name__ == '__main__': | 2123 if __name__ == '__main__': |
2058 option_parser = optparse.OptionParser() | 2124 option_parser = optparse.OptionParser() |
2059 option_parser.add_option("-f", '--ftp', action='store_const', | 2125 option_parser.add_option("-f", '--ftp', action='store_const', |
2060 const=SERVER_FTP, default=SERVER_HTTP, | 2126 const=SERVER_FTP, default=SERVER_HTTP, |
2061 dest='server_type', | 2127 dest='server_type', |
2062 help='start up an FTP server.') | 2128 help='start up an FTP server.') |
2063 option_parser.add_option('', '--sync', action='store_const', | 2129 option_parser.add_option('', '--sync', action='store_const', |
2064 const=SERVER_SYNC, default=SERVER_HTTP, | 2130 const=SERVER_SYNC, default=SERVER_HTTP, |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2131 dest='host', | 2197 dest='host', |
2132 help='Hostname or IP upon which the server will ' | 2198 help='Hostname or IP upon which the server will ' |
2133 'listen. Client connections will also only be ' | 2199 'listen. Client connections will also only be ' |
2134 'allowed from this address.') | 2200 'allowed from this address.') |
2135 option_parser.add_option('', '--auth-token', dest='auth_token', | 2201 option_parser.add_option('', '--auth-token', dest='auth_token', |
2136 help='Specify the auth token which should be used' | 2202 help='Specify the auth token which should be used' |
2137 'in the authorization header for GData.') | 2203 'in the authorization header for GData.') |
2138 options, args = option_parser.parse_args() | 2204 options, args = option_parser.parse_args() |
2139 | 2205 |
2140 sys.exit(main(options, args)) | 2206 sys.exit(main(options, args)) |
OLD | NEW |