import BaseHTTPServer, mimetools try: from cStringIO import StringIO except ImportError: from StringIO import StringIO import threading, socket from WebKit.NewThreadedAppServer import Handler from WebKit.ASStreamOut import ASStreamOut import time class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler): """Handles incoming requests. Recreated with every request. Abstract base class. """ ## This sends certain CGI variables. These are some that ## should be sent, but aren't: ## SERVER_ADDR ## SERVER_PORT ## SERVER_SOFTWARE ## SERVER_NAME ## HTTP_CONNECTION ## SERVER_PROTOCOL ## HTTP_KEEP_ALIVE ## These I don't think are needed: ## DOCUMENT_ROOT ## PATH_TRANSLATED ## GATEWAY_INTERFACE ## PATH ## SERVER_SIGNATURE ## SCRIPT_NAME (?) ## SCRIPT_FILENAME (?) ## SERVER_ADMIN (?) def doRequest(self): """ Actually performs the request, creating the environment and calling self.doTransaction(env, myInput) to perform the response. """ self.server_version = 'Webware/0.1' env = {} if self.headers.has_key('Content-Type'): env['CONTENT_TYPE'] = self.headers['Content-Type'] del self.headers['Content-Type'] self.headersToEnviron(self.headers, env) env['REMOTE_ADDR'], env['REMOTE_PORT'] = map(str, self.client_address) env['REQUEST_METHOD'] = self.command path = self.path if path.find('?') != -1: env['REQUEST_URI'], env['QUERY_STRING'] = path.split('?', 1) else: env['REQUEST_URI'] = path env['QUERY_STRING'] = '' env['PATH_INFO'] = env['REQUEST_URI'] myInput = '' if self.headers.has_key('Content-Length'): myInput = self.rfile.read(int(self.headers['Content-Length'])) self.doTransaction(env, myInput) do_GET = do_POST = do_HEAD = doRequest # These methods are used in WebDAV requests: do_OPTIONS = do_PUT = do_DELETE = doRequest do_MKCOL = do_COPY = do_MOVE = doRequest do_PROPFIND = doRequest def headersToEnviron(self, headers, env): """Use a simple heuristic to convert all the headers to environmental variables...""" for header, value in headers.items(): env['HTTP_%s' % (header.upper().replace('-', '_'))] = value return env def processResponse(self, data): """ Takes a string (like what a CGI script would print) and sends the actual HTTP response (response code, headers, body). """ s = StringIO(data) headers = mimetools.Message(s) self.doLocation(headers) self.sendStatus(headers) self.sendHeaders(headers) self.sendBody(s) def doLocation(self, headers): """If there's a Location header and no Status header, we need to add a Status header ourselves.""" if headers.has_key('Location'): if not headers.has_key('Status'): ## @@: is this the right status header? headers['Status'] = '301 Moved Temporarily' def sendStatus(self, headers): if not headers.has_key('Status'): status = "200 OK" else: status = headers['Status'] del headers['Status'] pos = status.find(' ') if pos == -1: code = int(status) message = '' else: code = int(status[:pos]) message = status[pos:].strip() self.send_response(code, message) def sendHeaders(self, headers): for header, value in headers.items(): self.send_header(header, value) self.end_headers() def sendBody(self, bodyFile): self.wfile.write(bodyFile.read()) bodyFile.close() class HTTPAppServerHandler(Handler, HTTPHandler): """ Adapters HTTPHandler to fit with ThreadedAppServer's model of an adapter """ protocolName = 'http' def handleRequest(self): HTTPHandler.__init__(self, self._sock, self._sock.getpeername(), None) def doTransaction(self, env, myInput): streamOut = ASStreamOut() requestDict = { 'format': 'CGI', 'time': time.time(), 'environ': env, 'input': StringIO(myInput), } self.dispatchRawRequest(requestDict, streamOut) self.processResponse(streamOut._buffer) self._sock.shutdown(2) def dispatchRawRequest(self, requestDict, streamOut): transaction = self._server._app.dispatchRawRequest(requestDict, streamOut) streamOut.close() transaction._application=None transaction.die() del transaction