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, []) | |
1897 | |
1898 def OCSPResponse(self): | |
1899 self.send_response(200) | |
1900 self.send_header('Content-Type', 'application/ocsp-response') | |
1901 self.send_header('Content-Length', str(len(self.ocsp_response))) | |
1902 self.end_headers() | |
1903 | |
1904 self.wfile.write(self.ocsp_response) | |
1879 | 1905 |
1880 class TCPEchoHandler(SocketServer.BaseRequestHandler): | 1906 class TCPEchoHandler(SocketServer.BaseRequestHandler): |
1881 """The RequestHandler class for TCP echo server. | 1907 """The RequestHandler class for TCP echo server. |
1882 | 1908 |
1883 It is instantiated once per connection to the server, and overrides the | 1909 It is instantiated once per connection to the server, and overrides the |
1884 handle() method to implement communication to the client. | 1910 handle() method to implement communication to the client. |
1885 """ | 1911 """ |
1886 | 1912 |
1887 def handle(self): | 1913 def handle(self): |
1888 """Handles the request from the client and constructs a response.""" | 1914 """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) | 1975 sys.stdout = FileMultiplexer(sys.stdout, logfile) |
1950 else: | 1976 else: |
1951 sys.stdout = logfile | 1977 sys.stdout = logfile |
1952 | 1978 |
1953 port = options.port | 1979 port = options.port |
1954 host = options.host | 1980 host = options.host |
1955 | 1981 |
1956 server_data = {} | 1982 server_data = {} |
1957 server_data['host'] = host | 1983 server_data['host'] = host |
1958 | 1984 |
1985 ocsp_server = None | |
1986 | |
1959 if options.server_type == SERVER_HTTP: | 1987 if options.server_type == SERVER_HTTP: |
1960 if options.cert: | 1988 if options.https: |
1961 # let's make sure the cert file exists. | 1989 pem_cert_and_key = None |
1962 if not os.path.isfile(options.cert): | 1990 if options.cert_and_key_file: |
1963 print 'specified server cert file not found: ' + options.cert + \ | 1991 if not os.path.isfile(options.cert_and_key_file): |
1964 ' exiting...' | 1992 print ('specified server cert file not found: ' + |
1965 return | 1993 options.cert_and_key_file + ' exiting...') |
1994 return | |
1995 pem_cert_and_key = file(options.cert_and_key_file, 'r').read() | |
1996 else: | |
1997 # generate a new certificate and run an OCSP server for it. | |
1998 ocsp_server = OCSPServer((host, 0), OCSPHandler) | |
1999 print 'OCSP server on port ' + str(ocsp_server.server_port) | |
Ryan Sleevi
2012/03/13 23:06:39
nit:
print 'OCSP server started on %s:%d' % (host
agl
2012/03/13 23:44:03
Done.
| |
2000 | |
2001 ocsp_der = None | |
2002 ocsp_revoked = False | |
2003 ocsp_invalid = False | |
2004 | |
2005 if options.ocsp == 'ok': | |
2006 pass | |
2007 elif options.ocsp == 'revoked': | |
2008 ocsp_revoked = True | |
2009 elif options.ocsp == 'invalid': | |
2010 ocsp_invalid = True | |
2011 else: | |
2012 print 'unknown OCSP status: ' + options.ocsp_status | |
2013 return | |
2014 | |
2015 (pem_cert_and_key, ocsp_der) = \ | |
2016 minica.GenerateCertKeyAndOCSP( | |
2017 subject = "127.0.0.1", | |
2018 ocsp_url = "http://127.0.0.1:%d/ocsp" % ocsp_server.server_port, | |
Ryan Sleevi
2012/03/13 23:06:39
bug? 127.0.0.1 -> host
We've avoided hardcoding i
agl
2012/03/13 23:44:03
Done.
| |
2019 ocsp_revoked = ocsp_revoked) | |
2020 | |
2021 if ocsp_invalid: | |
2022 ocsp_der = '3' | |
2023 | |
2024 ocsp_server.ocsp_response = ocsp_der | |
2025 | |
1966 for ca_cert in options.ssl_client_ca: | 2026 for ca_cert in options.ssl_client_ca: |
1967 if not os.path.isfile(ca_cert): | 2027 if not os.path.isfile(ca_cert): |
1968 print 'specified trusted client CA file not found: ' + ca_cert + \ | 2028 print 'specified trusted client CA file not found: ' + ca_cert + \ |
1969 ' exiting...' | 2029 ' exiting...' |
1970 return | 2030 return |
1971 server = HTTPSServer((host, port), TestPageHandler, options.cert, | 2031 server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key, |
1972 options.ssl_client_auth, options.ssl_client_ca, | 2032 options.ssl_client_auth, options.ssl_client_ca, |
1973 options.ssl_bulk_cipher, options.record_resume) | 2033 options.ssl_bulk_cipher, options.record_resume) |
1974 print 'HTTPS server started on %s:%d...' % (host, server.server_port) | 2034 print 'HTTPS server started on %s:%d...' % (host, server.server_port) |
1975 else: | 2035 else: |
1976 server = HTTPServer((host, port), TestPageHandler) | 2036 server = HTTPServer((host, port), TestPageHandler) |
1977 print 'HTTP server started on %s:%d...' % (host, server.server_port) | 2037 print 'HTTP server started on %s:%d...' % (host, server.server_port) |
1978 | 2038 |
1979 server.data_dir = MakeDataDir() | 2039 server.data_dir = MakeDataDir() |
1980 server.file_root_url = options.file_root_url | 2040 server.file_root_url = options.file_root_url |
1981 server_data['port'] = server.server_port | 2041 server_data['port'] = server.server_port |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2041 else: | 2101 else: |
2042 fd = options.startup_pipe | 2102 fd = options.startup_pipe |
2043 startup_pipe = os.fdopen(fd, "w") | 2103 startup_pipe = os.fdopen(fd, "w") |
2044 # First write the data length as an unsigned 4-byte value. This | 2104 # 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 | 2105 # is _not_ using network byte ordering since the other end of the |
2046 # pipe is on the same machine. | 2106 # pipe is on the same machine. |
2047 startup_pipe.write(struct.pack('=L', server_data_len)) | 2107 startup_pipe.write(struct.pack('=L', server_data_len)) |
2048 startup_pipe.write(server_data_json) | 2108 startup_pipe.write(server_data_json) |
2049 startup_pipe.close() | 2109 startup_pipe.close() |
2050 | 2110 |
2111 if ocsp_server is not None: | |
2112 ocsp_server.serve_forever_on_thread() | |
2113 | |
2051 try: | 2114 try: |
2052 server.serve_forever() | 2115 server.serve_forever() |
2053 except KeyboardInterrupt: | 2116 except KeyboardInterrupt: |
2054 print 'shutting down server' | 2117 print 'shutting down server' |
2118 if ocsp_server is not None: | |
2119 ocsp_server.stop_serving() | |
2055 server.stop = True | 2120 server.stop = True |
2056 | 2121 |
2057 if __name__ == '__main__': | 2122 if __name__ == '__main__': |
2058 option_parser = optparse.OptionParser() | 2123 option_parser = optparse.OptionParser() |
2059 option_parser.add_option("-f", '--ftp', action='store_const', | 2124 option_parser.add_option("-f", '--ftp', action='store_const', |
2060 const=SERVER_FTP, default=SERVER_HTTP, | 2125 const=SERVER_FTP, default=SERVER_HTTP, |
2061 dest='server_type', | 2126 dest='server_type', |
2062 help='start up an FTP server.') | 2127 help='start up an FTP server.') |
2063 option_parser.add_option('', '--sync', action='store_const', | 2128 option_parser.add_option('', '--sync', action='store_const', |
2064 const=SERVER_SYNC, default=SERVER_HTTP, | 2129 const=SERVER_SYNC, default=SERVER_HTTP, |
(...skipping 10 matching lines...) Expand all Loading... | |
2075 option_parser.add_option('', '--log-to-console', action='store_const', | 2140 option_parser.add_option('', '--log-to-console', action='store_const', |
2076 const=True, default=False, | 2141 const=True, default=False, |
2077 dest='log_to_console', | 2142 dest='log_to_console', |
2078 help='Enables or disables sys.stdout logging to ' | 2143 help='Enables or disables sys.stdout logging to ' |
2079 'the console.') | 2144 'the console.') |
2080 option_parser.add_option('', '--port', default='0', type='int', | 2145 option_parser.add_option('', '--port', default='0', type='int', |
2081 help='Port used by the server. If unspecified, the ' | 2146 help='Port used by the server. If unspecified, the ' |
2082 'server will listen on an ephemeral port.') | 2147 'server will listen on an ephemeral port.') |
2083 option_parser.add_option('', '--data-dir', dest='data_dir', | 2148 option_parser.add_option('', '--data-dir', dest='data_dir', |
2084 help='Directory from which to read the files.') | 2149 help='Directory from which to read the files.') |
2085 option_parser.add_option('', '--https', dest='cert', | 2150 option_parser.add_option('', '--https', action='store_true', dest='https', |
2086 help='Specify that https should be used, specify ' | 2151 help='Specify that https should be used.') |
2087 'the path to the cert containing the private key ' | 2152 option_parser.add_option('', '--cert-and-key-file', dest='cert_and_key_file', |
2088 'the server should use.') | 2153 help='specify the path to the file containing the ' |
2154 'certificate and private key for the server in PEM ' | |
2155 'format') | |
2156 option_parser.add_option('', '--ocsp', dest='ocsp', default='ok', | |
2157 help='The type of OCSP response generated for the ' | |
2158 'automatically generated certificate. One of of ' | |
Ryan Sleevi
2012/03/13 23:06:39
nit: 'of of' -> 'of'
agl
2012/03/13 23:44:03
Done.
| |
2159 '[ok,revoked,invalid]') | |
2089 option_parser.add_option('', '--https-record-resume', dest='record_resume', | 2160 option_parser.add_option('', '--https-record-resume', dest='record_resume', |
2090 const=True, default=False, action='store_const', | 2161 const=True, default=False, action='store_const', |
2091 help='Record resumption cache events rather than' | 2162 help='Record resumption cache events rather than' |
2092 ' resuming as normal. Allows the use of the' | 2163 ' resuming as normal. Allows the use of the' |
2093 ' /ssl-session-cache request') | 2164 ' /ssl-session-cache request') |
2094 option_parser.add_option('', '--ssl-client-auth', action='store_true', | 2165 option_parser.add_option('', '--ssl-client-auth', action='store_true', |
2095 help='Require SSL client auth on every connection.') | 2166 help='Require SSL client auth on every connection.') |
2096 option_parser.add_option('', '--ssl-client-ca', action='append', default=[], | 2167 option_parser.add_option('', '--ssl-client-ca', action='append', default=[], |
2097 help='Specify that the client certificate request ' | 2168 help='Specify that the client certificate request ' |
2098 'should include the CA named in the subject of ' | 2169 'should include the CA named in the subject of ' |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2131 dest='host', | 2202 dest='host', |
2132 help='Hostname or IP upon which the server will ' | 2203 help='Hostname or IP upon which the server will ' |
2133 'listen. Client connections will also only be ' | 2204 'listen. Client connections will also only be ' |
2134 'allowed from this address.') | 2205 'allowed from this address.') |
2135 option_parser.add_option('', '--auth-token', dest='auth_token', | 2206 option_parser.add_option('', '--auth-token', dest='auth_token', |
2136 help='Specify the auth token which should be used' | 2207 help='Specify the auth token which should be used' |
2137 'in the authorization header for GData.') | 2208 'in the authorization header for GData.') |
2138 options, args = option_parser.parse_args() | 2209 options, args = option_parser.parse_args() |
2139 | 2210 |
2140 sys.exit(main(options, args)) | 2211 sys.exit(main(options, args)) |
OLD | NEW |