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 started on %s:%d...' % |
| 2000 (host, ocsp_server.server_port)) |
| 2001 |
| 2002 ocsp_der = None |
| 2003 ocsp_revoked = False |
| 2004 ocsp_invalid = False |
| 2005 |
| 2006 if options.ocsp == 'ok': |
| 2007 pass |
| 2008 elif options.ocsp == 'revoked': |
| 2009 ocsp_revoked = True |
| 2010 elif options.ocsp == 'invalid': |
| 2011 ocsp_invalid = True |
| 2012 else: |
| 2013 print 'unknown OCSP status: ' + options.ocsp_status |
| 2014 return |
| 2015 |
| 2016 (pem_cert_and_key, ocsp_der) = \ |
| 2017 minica.GenerateCertKeyAndOCSP( |
| 2018 subject = "127.0.0.1", |
| 2019 ocsp_url = ("http://%s:%d/ocsp" % |
| 2020 (host, ocsp_server.server_port)), |
| 2021 ocsp_revoked = ocsp_revoked) |
| 2022 |
| 2023 if ocsp_invalid: |
| 2024 ocsp_der = '3' |
| 2025 |
| 2026 ocsp_server.ocsp_response = ocsp_der |
| 2027 |
1966 for ca_cert in options.ssl_client_ca: | 2028 for ca_cert in options.ssl_client_ca: |
1967 if not os.path.isfile(ca_cert): | 2029 if not os.path.isfile(ca_cert): |
1968 print 'specified trusted client CA file not found: ' + ca_cert + \ | 2030 print 'specified trusted client CA file not found: ' + ca_cert + \ |
1969 ' exiting...' | 2031 ' exiting...' |
1970 return | 2032 return |
1971 server = HTTPSServer((host, port), TestPageHandler, options.cert, | 2033 server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key, |
1972 options.ssl_client_auth, options.ssl_client_ca, | 2034 options.ssl_client_auth, options.ssl_client_ca, |
1973 options.ssl_bulk_cipher, options.record_resume) | 2035 options.ssl_bulk_cipher, options.record_resume) |
1974 print 'HTTPS server started on %s:%d...' % (host, server.server_port) | 2036 print 'HTTPS server started on %s:%d...' % (host, server.server_port) |
1975 else: | 2037 else: |
1976 server = HTTPServer((host, port), TestPageHandler) | 2038 server = HTTPServer((host, port), TestPageHandler) |
1977 print 'HTTP server started on %s:%d...' % (host, server.server_port) | 2039 print 'HTTP server started on %s:%d...' % (host, server.server_port) |
1978 | 2040 |
1979 server.data_dir = MakeDataDir() | 2041 server.data_dir = MakeDataDir() |
1980 server.file_root_url = options.file_root_url | 2042 server.file_root_url = options.file_root_url |
1981 server_data['port'] = server.server_port | 2043 server_data['port'] = server.server_port |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2041 else: | 2103 else: |
2042 fd = options.startup_pipe | 2104 fd = options.startup_pipe |
2043 startup_pipe = os.fdopen(fd, "w") | 2105 startup_pipe = os.fdopen(fd, "w") |
2044 # First write the data length as an unsigned 4-byte value. This | 2106 # 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 | 2107 # is _not_ using network byte ordering since the other end of the |
2046 # pipe is on the same machine. | 2108 # pipe is on the same machine. |
2047 startup_pipe.write(struct.pack('=L', server_data_len)) | 2109 startup_pipe.write(struct.pack('=L', server_data_len)) |
2048 startup_pipe.write(server_data_json) | 2110 startup_pipe.write(server_data_json) |
2049 startup_pipe.close() | 2111 startup_pipe.close() |
2050 | 2112 |
| 2113 if ocsp_server is not None: |
| 2114 ocsp_server.serve_forever_on_thread() |
| 2115 |
2051 try: | 2116 try: |
2052 server.serve_forever() | 2117 server.serve_forever() |
2053 except KeyboardInterrupt: | 2118 except KeyboardInterrupt: |
2054 print 'shutting down server' | 2119 print 'shutting down server' |
| 2120 if ocsp_server is not None: |
| 2121 ocsp_server.stop_serving() |
2055 server.stop = True | 2122 server.stop = True |
2056 | 2123 |
2057 if __name__ == '__main__': | 2124 if __name__ == '__main__': |
2058 option_parser = optparse.OptionParser() | 2125 option_parser = optparse.OptionParser() |
2059 option_parser.add_option("-f", '--ftp', action='store_const', | 2126 option_parser.add_option("-f", '--ftp', action='store_const', |
2060 const=SERVER_FTP, default=SERVER_HTTP, | 2127 const=SERVER_FTP, default=SERVER_HTTP, |
2061 dest='server_type', | 2128 dest='server_type', |
2062 help='start up an FTP server.') | 2129 help='start up an FTP server.') |
2063 option_parser.add_option('', '--sync', action='store_const', | 2130 option_parser.add_option('', '--sync', action='store_const', |
2064 const=SERVER_SYNC, default=SERVER_HTTP, | 2131 const=SERVER_SYNC, default=SERVER_HTTP, |
(...skipping 10 matching lines...) Expand all Loading... |
2075 option_parser.add_option('', '--log-to-console', action='store_const', | 2142 option_parser.add_option('', '--log-to-console', action='store_const', |
2076 const=True, default=False, | 2143 const=True, default=False, |
2077 dest='log_to_console', | 2144 dest='log_to_console', |
2078 help='Enables or disables sys.stdout logging to ' | 2145 help='Enables or disables sys.stdout logging to ' |
2079 'the console.') | 2146 'the console.') |
2080 option_parser.add_option('', '--port', default='0', type='int', | 2147 option_parser.add_option('', '--port', default='0', type='int', |
2081 help='Port used by the server. If unspecified, the ' | 2148 help='Port used by the server. If unspecified, the ' |
2082 'server will listen on an ephemeral port.') | 2149 'server will listen on an ephemeral port.') |
2083 option_parser.add_option('', '--data-dir', dest='data_dir', | 2150 option_parser.add_option('', '--data-dir', dest='data_dir', |
2084 help='Directory from which to read the files.') | 2151 help='Directory from which to read the files.') |
2085 option_parser.add_option('', '--https', dest='cert', | 2152 option_parser.add_option('', '--https', action='store_true', dest='https', |
2086 help='Specify that https should be used, specify ' | 2153 help='Specify that https should be used.') |
2087 'the path to the cert containing the private key ' | 2154 option_parser.add_option('', '--cert-and-key-file', dest='cert_and_key_file', |
2088 'the server should use.') | 2155 help='specify the path to the file containing the ' |
| 2156 'certificate and private key for the server in PEM ' |
| 2157 'format') |
| 2158 option_parser.add_option('', '--ocsp', dest='ocsp', default='ok', |
| 2159 help='The type of OCSP response generated for the ' |
| 2160 'automatically generated certificate. One of ' |
| 2161 '[ok,revoked,invalid]') |
2089 option_parser.add_option('', '--https-record-resume', dest='record_resume', | 2162 option_parser.add_option('', '--https-record-resume', dest='record_resume', |
2090 const=True, default=False, action='store_const', | 2163 const=True, default=False, action='store_const', |
2091 help='Record resumption cache events rather than' | 2164 help='Record resumption cache events rather than' |
2092 ' resuming as normal. Allows the use of the' | 2165 ' resuming as normal. Allows the use of the' |
2093 ' /ssl-session-cache request') | 2166 ' /ssl-session-cache request') |
2094 option_parser.add_option('', '--ssl-client-auth', action='store_true', | 2167 option_parser.add_option('', '--ssl-client-auth', action='store_true', |
2095 help='Require SSL client auth on every connection.') | 2168 help='Require SSL client auth on every connection.') |
2096 option_parser.add_option('', '--ssl-client-ca', action='append', default=[], | 2169 option_parser.add_option('', '--ssl-client-ca', action='append', default=[], |
2097 help='Specify that the client certificate request ' | 2170 help='Specify that the client certificate request ' |
2098 'should include the CA named in the subject of ' | 2171 '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', | 2204 dest='host', |
2132 help='Hostname or IP upon which the server will ' | 2205 help='Hostname or IP upon which the server will ' |
2133 'listen. Client connections will also only be ' | 2206 'listen. Client connections will also only be ' |
2134 'allowed from this address.') | 2207 'allowed from this address.') |
2135 option_parser.add_option('', '--auth-token', dest='auth_token', | 2208 option_parser.add_option('', '--auth-token', dest='auth_token', |
2136 help='Specify the auth token which should be used' | 2209 help='Specify the auth token which should be used' |
2137 'in the authorization header for GData.') | 2210 'in the authorization header for GData.') |
2138 options, args = option_parser.parse_args() | 2211 options, args = option_parser.parse_args() |
2139 | 2212 |
2140 sys.exit(main(options, args)) | 2213 sys.exit(main(options, args)) |
OLD | NEW |