Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / test / https_forwarder.py
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """An https server that forwards requests to another server. This allows a
6 server that supports http only to be accessed over https.
7 """
8
9 import BaseHTTPServer
10 import os
11 import SocketServer
12 import sys
13 import urllib2
14 import urlparse
15 import testserver_base
16 import tlslite.api
17
18
19 class RedirectSuppressor(urllib2.HTTPErrorProcessor):
20   """Prevents urllib2 from following http redirects.
21
22   If this class is placed in an urllib2.OpenerDirector's handler chain before
23   the default urllib2.HTTPRedirectHandler, it will terminate the processing of
24   responses containing redirect codes (301, 302, 303, 307) before they reach the
25   default redirect handler.
26   """
27
28   def http_response(self, req, response):
29     return response
30
31   def https_response(self, req, response):
32     return response
33
34
35 class RequestForwarder(BaseHTTPServer.BaseHTTPRequestHandler):
36   """Handles requests received by forwarding them to the another server."""
37
38   def do_GET(self):
39     """Forwards GET requests."""
40     self._forward(None)
41
42   def do_POST(self):
43     """Forwards POST requests."""
44     self._forward(self.rfile.read(int(self.headers['Content-Length'])))
45
46   def _forward(self, body):
47     """Forwards a GET or POST request to another server.
48
49     Args:
50       body: The request body. This should be |None| for GET requests.
51     """
52     request_url = urlparse.urlparse(self.path)
53     url = urlparse.urlunparse((self.server.forward_scheme,
54                                self.server.forward_netloc,
55                                self.server.forward_path + request_url[2],
56                                request_url[3],
57                                request_url[4],
58                                request_url[5]))
59
60     headers = dict((key, value) for key, value in dict(self.headers).iteritems()
61                    if key.lower() != 'host')
62     opener = urllib2.build_opener(RedirectSuppressor)
63     forward = opener.open(urllib2.Request(url, body, headers))
64
65     self.send_response(forward.getcode())
66     for key, value in dict(forward.info()).iteritems():
67       self.send_header(key, value)
68     self.end_headers()
69     self.wfile.write(forward.read())
70
71
72 class MultiThreadedHTTPSServer(SocketServer.ThreadingMixIn,
73                                tlslite.api.TLSSocketServerMixIn,
74                                testserver_base.ClientRestrictingServerMixIn,
75                                testserver_base.BrokenPipeHandlerMixIn,
76                                testserver_base.StoppableHTTPServer):
77   """A multi-threaded version of testserver.HTTPSServer."""
78
79   def __init__(self, server_address, request_hander_class, pem_cert_and_key):
80     """Initializes the server.
81
82     Args:
83       server_address: Server host and port.
84       request_hander_class: The class that will handle requests to the server.
85       pem_cert_and_key: Path to file containing the https cert and private key.
86     """
87     self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key)
88     # Force using only python implementation - otherwise behavior is different
89     # depending on whether m2crypto Python module is present (error is thrown
90     # when it is). m2crypto uses a C (based on OpenSSL) implementation under
91     # the hood.
92     self.private_key = tlslite.api.parsePEMKey(pem_cert_and_key,
93                                                private=True,
94                                                implementations=['python'])
95
96     testserver_base.StoppableHTTPServer.__init__(self,
97                                                  server_address,
98                                                  request_hander_class)
99
100   def handshake(self, tlsConnection):
101     """Performs the SSL handshake for an https connection.
102
103     Args:
104       tlsConnection: The https connection.
105     Returns:
106       Whether the SSL handshake succeeded.
107     """
108     try:
109       self.tlsConnection = tlsConnection
110       tlsConnection.handshakeServer(certChain=self.cert_chain,
111                                     privateKey=self.private_key)
112       tlsConnection.ignoreAbruptClose = True
113       return True
114     except:
115       return False
116
117
118 class ServerRunner(testserver_base.TestServerRunner):
119   """Runner that starts an https server which forwards requests to another
120   server.
121   """
122
123   def create_server(self, server_data):
124     """Performs the SSL handshake for an https connection.
125
126     Args:
127       server_data: Dictionary that holds information about the server.
128     Returns:
129       The started server.
130     """
131     port = self.options.port
132     host = self.options.host
133
134     if not os.path.isfile(self.options.cert_and_key_file):
135       raise testserver_base.OptionError(
136           'Specified server cert file not found: ' +
137           self.options.cert_and_key_file)
138     pem_cert_and_key = open(self.options.cert_and_key_file).read()
139
140     server = MultiThreadedHTTPSServer((host, port),
141                                       RequestForwarder,
142                                       pem_cert_and_key)
143     print 'HTTPS server started on %s:%d...' % (host, server.server_port)
144
145     forward_target = urlparse.urlparse(self.options.forward_target)
146     server.forward_scheme = forward_target[0]
147     server.forward_netloc = forward_target[1]
148     server.forward_path = forward_target[2].rstrip('/')
149     server.forward_host = forward_target.hostname
150     if forward_target.port:
151       server.forward_host += ':' + str(forward_target.port)
152     server_data['port'] = server.server_port
153     return server
154
155   def add_options(self):
156     """Specifies the command-line options understood by the server."""
157     testserver_base.TestServerRunner.add_options(self)
158     self.option_parser.add_option('--https', action='store_true',
159                                   help='Ignored (provided for compatibility '
160                                   'only).')
161     self.option_parser.add_option('--cert-and-key-file', help='The path to the '
162                                   'file containing the certificate and private '
163                                   'key for the server in PEM format.')
164     self.option_parser.add_option('--forward-target', help='The URL prefix to '
165                                   'which requests will be forwarded.')
166
167
168 if __name__ == '__main__':
169   sys.exit(ServerRunner().main())