15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""HTTP proxy request handler with SSL support.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequestHandler: Utility class for parsing HTTP requests.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProxyHandler: HTTP proxy handler.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import BaseHTTPServer
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import cgi
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import OpenSSL
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import socket
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import SocketServer
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import traceback
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urlparse
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Class for reading HTTP requests and writing HTTP responses"""
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protocol_version = "HTTP/1.1"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_version = protocol_version
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  class HTTPRequestException(Exception): pass
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, rfile, wfile, server):
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.rfile = rfile
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile = wfile
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server = server
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ReadRequest(self):
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "Reads and parses single HTTP request from self.rfile"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.raw_requestline = self.rfile.readline()
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.raw_requestline:
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.close_connection = 1
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise HTTPRequestException('failed to read request line')
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.parse_request():
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise HTTPRequestException('failed to parse request')
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.headers = dict(self.headers)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.body = None
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'content-length' in self.headers:
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.body = self.rfile.read(int(self.headers['content-length']))
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def log_message(self, format, *args):
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pass
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "Request handler class for proxy server"
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  server_version = "PlaybackProxy/0.0.1"
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protocol_version = "HTTP/1.1"
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_CONNECT(self):
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "Handles CONNECT HTTP request"
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    server = self.path.split(':')[0]
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    certificate_file = os.path.join(self.certificate_directory, server)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.isfile(certificate_file):
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sys.stderr.write('request to connect %s is ignored\n' % server)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(501)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Proxy-agent', self.version_string())
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Send confirmation to browser.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200, 'Connection established')
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Proxy-agent', self.version_string())
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Create SSL context.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context.use_privatekey_file(certificate_file)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context.use_certificate_file(certificate_file)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Create and initialize SSL connection atop of tcp socket.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ssl_connection = OpenSSL.SSL.Connection(context, self.connection)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ssl_connection.set_accept_state()
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ssl_connection.do_handshake()
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ssl_rfile = socket._fileobject(ssl_connection, "rb", self.rbufsize)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ssl_wfile = socket._fileobject(ssl_connection, "wb", self.wbufsize)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Handle http requests coming from ssl_connection.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handler = RequestHandler(ssl_rfile, ssl_wfile, self.path)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      handler.close_connection = 1
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while True:
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        handler.ReadRequest()
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.driver.ProcessRequest(handler)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if handler.close_connection: break
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except (OpenSSL.SSL.SysCallError, OpenSSL.SSL.ZeroReturnError):
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pass
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.close_connection = 1
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_GET(self):
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.driver.ProcessRequest(self)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_POST(self):
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'content-length' in self.headers:
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.body = self.rfile.read(int(self.headers['content-length']))
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.driver.ProcessRequest(self)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def log_message(self, format, *args):
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sys.stdout.write((format % args) + '\n')
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           BaseHTTPServer.HTTPServer):
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CreateServer(driver, port, certificate_directory=None):
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not certificate_directory:
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    certificate_directory = os.path.join(os.getcwd(), 'certificates')
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProxyHandler.driver = driver
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProxyHandler.certificate_directory = certificate_directory
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ThreadingHTTPServer(('', port), ProxyHandler)
123