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 import socket 31 32 class HandlerFactory: 33 34 "A factory class creating WebStack dispatcher objects." 35 36 def __init__(self, resource, authenticator=None, handle_errors=1, error_resource=None, handler_class=None): 37 38 """ 39 Initialise the root application-specific 'resource' and optional 40 'authenticator'. The optional 'handle_errors' parameter (if true) causes 41 handlers to deal with uncaught exceptions cleanly, and the optional 42 'error_resource' specifies an alternative error message generation 43 resource. 44 45 If the optional 'handler_class' is specified, it will be used to 46 instantiate handlers rather than the default Handler class. 47 """ 48 49 self.webstack_resource = resource 50 self.webstack_authenticator = authenticator 51 self.handle_errors = handle_errors 52 self.error_resource = error_resource or ErrorResource() 53 self.handler_class = handler_class or Handler 54 55 def __call__(self, request, client_address, server): 56 57 "Act as a factory for the server objects." 58 59 handler = self.handler_class(request, client_address, server, self.webstack_resource, 60 self.webstack_authenticator, self.handle_errors, self.error_resource) 61 return handler 62 63 class Handler(BaseHTTPServer.BaseHTTPRequestHandler): 64 65 "A class dispatching requests to WebStack resources." 66 67 def __init__(self, request, client_address, server, resource, authenticator, handle_errors, error_resource): 68 69 """ 70 Initialise the root application-specific 'resource' and 'authenticator'. 71 Where 'handle_errors' is true, uncaught exceptions are dealt with by the 72 handler and reported using the 'error_resource' provided. 73 """ 74 75 self.webstack_resource = resource 76 self.webstack_authenticator = authenticator 77 self.handle_errors = handle_errors 78 self.error_resource = error_resource 79 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server) 80 81 def handle(self): 82 83 "Dispatch the request to the root application-specific resource." 84 85 # NOTE: Overriding and trimming back the method's functionality. 86 87 self.raw_requestline = self.rfile.readline() 88 if not self.parse_request(): # An error code has been sent, just exit 89 return 90 91 trans = WebStack.BaseHTTPRequestHandler.Transaction(self) 92 try: 93 if self.webstack_authenticator is None or self.webstack_authenticator.authenticate(trans): 94 try: 95 self.webstack_resource.respond(trans) 96 except EndOfResponse: 97 pass 98 except: 99 if self.handle_errors: 100 trans.rollback() 101 trans.set_response_code(500) # Internal error 102 self.error_resource.respond(trans) 103 else: 104 raise 105 else: 106 trans.set_response_code(401) # Unauthorized 107 trans.set_header_value("WWW-Authenticate", '%s realm="%s"' % ( 108 self.webstack_authenticator.get_auth_type(), self.webstack_authenticator.get_realm())) 109 110 finally: 111 trans.commit() 112 113 # Support for secure servers. 114 115 class SecureHTTPServer(BaseHTTPServer.HTTPServer): 116 117 "An HTTP server supporting https URLs." 118 119 def __init__(self, server_address, HandlerClass, key_filename, certificate_filename): 120 121 """ 122 Initialise the server using the given 'server_address' and 123 'HandlerClass', along with the specified 'key_filename' and 124 'certificate_filename'. 125 """ 126 127 # SSL-related import. 128 129 from OpenSSL import SSL 130 131 # Initialisation using SSL. 132 133 BaseHTTPServer.HTTPServer.__init__(self, server_address, HandlerClass) 134 context = SSL.Context(SSL.SSLv23_METHOD) 135 context.use_privatekey_file(key_filename) 136 context.use_certificate_file(certificate_filename) 137 self.socket = SSL.Connection(context, socket.socket(self.address_family, self.socket_type)) 138 self.server_bind() 139 self.server_activate() 140 141 class SecureHandler(Handler): 142 143 "A secure version of the handler." 144 145 def setup(self): 146 147 "Set up the connection and streams." 148 149 self.connection = self.request 150 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) 151 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) 152 153 def SecureHandlerFactory(resource, authenticator=None, handle_errors=1, error_resource=None, handler_class=None): 154 155 "Return a secure handler factory, based on HandlerFactory." 156 157 return HandlerFactory(resource, authenticator, handle_errors, error_resource, handler_class=(handler_class or SecureHandler)) 158 159 default_address = ("", 8080) 160 161 def deploy(resource, authenticator=None, address=None, handle_errors=1, error_resource=None, server=None, factory=None, **server_args): 162 163 """ 164 Deploy the given 'resource', with the given optional 'authenticator', at the 165 given optional 'address', where 'address' is a 2-tuple of the form 166 (host_string, port_integer). 167 168 The optional 'handle_errors' flag (true by default) specifies whether error 169 conditions are handled gracefully, and the optional 'error_resource' 170 specifies an alternative error message generation resource, if desired. 171 172 If the optional 'server' is specified, use the server provided as opposed to 173 the default BaseHTTPServer.HTTPServer class. Use any 'server_args' (provided 174 as additional keyword arguments) to instantiate the server. 175 176 If the optional 'factory' is specified, use the factory provided as opposed 177 to the default HandlerFactory class. 178 """ 179 180 factory = factory or HandlerFactory 181 handler = factory(resource, authenticator, handle_errors, error_resource) 182 server = server or BaseHTTPServer.HTTPServer 183 app = server(address or default_address, handler, **(server_args or {})) 184 app.serve_forever() 185 186 def secure_deploy(resource, authenticator=None, address=None, handle_errors=1, error_resource=None, server=None, factory=None, **server_args): 187 188 """ 189 Deploy the given 'resource' using a secure version of the server, employing 190 the deploy function. 191 """ 192 193 return deploy( 194 resource, authenticator, address, handle_errors, error_resource, 195 server=(server or SecureHTTPServer), 196 factory=(factory or SecureHandlerFactory), 197 **server_args 198 ) 199 200 # vim: tabstop=4 expandtab shiftwidth=4