1 #!/usr/bin/env python 2 3 """ 4 BaseHTTPRequestHandler adapter. 5 6 Copyright (C) 2004, 2005, 2006, 2007 Paul Boddie <paul@boddie.org.uk> 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 21 """ 22 23 import WebStack.BaseHTTPRequestHandler 24 import BaseHTTPServer 25 from WebStack.Generic import EndOfResponse 26 from WebStack.Adapters.Helpers.Error import ErrorResource 27 28 # SSL-related imports. 29 30 from OpenSSL import SSL 31 import socket 32 33 class HandlerFactory: 34 35 "A factory class creating WebStack dispatcher objects." 36 37 def __init__(self, resource, authenticator=None, handle_errors=1, error_resource=None, handler_class=None): 38 39 """ 40 Initialise the root application-specific 'resource' and optional 41 'authenticator'. The optional 'handle_errors' parameter (if true) causes 42 handlers to deal with uncaught exceptions cleanly, and the optional 43 'error_resource' specifies an alternative error message generation 44 resource. 45 46 If the optional 'handler_class' is specified, it will be used to 47 instantiate handlers rather than the default Handler class. 48 """ 49 50 self.webstack_resource = resource 51 self.webstack_authenticator = authenticator 52 self.handle_errors = handle_errors 53 self.error_resource = error_resource or ErrorResource() 54 self.handler_class = handler_class or Handler 55 56 def __call__(self, request, client_address, server): 57 58 "Act as a factory for the server objects." 59 60 handler = self.handler_class(request, client_address, server, self.webstack_resource, 61 self.webstack_authenticator, self.handle_errors, self.error_resource) 62 return handler 63 64 class Handler(BaseHTTPServer.BaseHTTPRequestHandler): 65 66 "A class dispatching requests to WebStack resources." 67 68 def __init__(self, request, client_address, server, resource, authenticator, handle_errors, error_resource): 69 70 """ 71 Initialise the root application-specific 'resource' and 'authenticator'. 72 Where 'handle_errors' is true, uncaught exceptions are dealt with by the 73 handler and reported using the 'error_resource' provided. 74 """ 75 76 self.webstack_resource = resource 77 self.webstack_authenticator = authenticator 78 self.handle_errors = handle_errors 79 self.error_resource = error_resource 80 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server) 81 82 def handle(self): 83 84 "Dispatch the request to the root application-specific resource." 85 86 # NOTE: Overriding and trimming back the method's functionality. 87 88 self.raw_requestline = self.rfile.readline() 89 if not self.parse_request(): # An error code has been sent, just exit 90 return 91 92 trans = WebStack.BaseHTTPRequestHandler.Transaction(self) 93 try: 94 if self.webstack_authenticator is None or self.webstack_authenticator.authenticate(trans): 95 try: 96 self.webstack_resource.respond(trans) 97 except EndOfResponse: 98 pass 99 except: 100 if self.handle_errors: 101 trans.rollback() 102 trans.set_response_code(500) # Internal error 103 self.error_resource.respond(trans) 104 else: 105 raise 106 else: 107 trans.set_response_code(401) # Unauthorized 108 trans.set_header_value("WWW-Authenticate", '%s realm="%s"' % ( 109 self.webstack_authenticator.get_auth_type(), self.webstack_authenticator.get_realm())) 110 111 finally: 112 trans.commit() 113 114 # Support for secure servers. 115 116 class SecureHTTPServer(BaseHTTPServer.HTTPServer): 117 118 "An HTTP server supporting https URLs." 119 120 def __init__(self, server_address, HandlerClass, key_filename, certificate_filename): 121 122 """ 123 Initialise the server using the given 'server_address' and 124 'HandlerClass', along with the specified 'key_filename' and 125 'certificate_filename'. 126 """ 127 128 BaseHTTPServer.HTTPServer.__init__(self, server_address, HandlerClass) 129 context = SSL.Context(SSL.SSLv23_METHOD) 130 context.use_privatekey_file(key_filename) 131 context.use_certificate_file(certificate_filename) 132 self.socket = SSL.Connection(context, socket.socket(self.address_family, self.socket_type)) 133 self.server_bind() 134 self.server_activate() 135 136 class SecureHandler(Handler): 137 138 "A secure version of the handler." 139 140 def setup(self): 141 142 "Set up the connection and streams." 143 144 self.connection = self.request 145 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) 146 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) 147 148 def SecureHandlerFactory(resource, authenticator=None, handle_errors=1, error_resource=None, handler_class=None): 149 150 "Return a secure handler factory, based on HandlerFactory." 151 152 return HandlerFactory(resource, authenticator, handle_errors, error_resource, handler_class=(handler_class or SecureHandler)) 153 154 default_address = ("", 8080) 155 156 def deploy(resource, authenticator=None, address=None, handle_errors=1, error_resource=None, server=None, factory=None, **server_args): 157 158 """ 159 Deploy the given 'resource', with the given optional 'authenticator', at the 160 given optional 'address', where 'address' is a 2-tuple of the form 161 (host_string, port_integer). 162 163 The optional 'handle_errors' flag (true by default) specifies whether error 164 conditions are handled gracefully, and the optional 'error_resource' 165 specifies an alternative error message generation resource, if desired. 166 167 If the optional 'server' is specified, use the server provided as opposed to 168 the default BaseHTTPServer.HTTPServer class. Use any 'server_args' (provided 169 as additional keyword arguments) to instantiate the server. 170 171 If the optional 'factory' is specified, use the factory provided as opposed 172 to the default HandlerFactory class. 173 """ 174 175 factory = factory or HandlerFactory 176 handler = factory(resource, authenticator, handle_errors, error_resource) 177 server = server or BaseHTTPServer.HTTPServer 178 app = server(address or default_address, handler, **(server_args or {})) 179 app.serve_forever() 180 181 def secure_deploy(resource, authenticator=None, address=None, handle_errors=1, error_resource=None, server=None, factory=None, **server_args): 182 183 """ 184 Deploy the given 'resource' using a secure version of the server, employing 185 the deploy function. 186 """ 187 188 return deploy( 189 resource, authenticator, address, handle_errors, error_resource, 190 server=(server or SecureHTTPServer), 191 factory=(factory or SecureHandlerFactory), 192 **server_args 193 ) 194 195 # vim: tabstop=4 expandtab shiftwidth=4