#!/usr/bin/python -t # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Copyright 2005 Dan Williams and Red Hat, Inc. # import SimpleHTTPServer import threading import urllib import posixpath import os import sys import SSLCommon import time import socket from OpenSSL import SSL import DebugUtils import cgi """ So we need a way of getting data from the RequestHandler instance, which is unique to each request, into the actual handler instance that we've registered. To do this without overriding a bunch of member functions, we have a global dict of data that maps authorization info to a particular thread, since each request is handled in its own thread. Therefore, the handler instance can access the data that our request handler has set. """ __authinfos = {} def _add_authinfo(authinfo): __authinfos[threading.currentThread()] = authinfo def get_authinfo(): return __authinfos[threading.currentThread()] def _del_authinfo(): del __authinfos[threading.currentThread()] class PlgHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): # For some reason, httplib closes the connection right after headers # have been sent if the connection is _not_ HTTP/1.1, which results in # a "Bad file descriptor" error when the client tries to read from the socket protocol_version = "HTTP/1.1" def __init__(self, request, client_address, server): self._server = server try: SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, request, client_address, server) except socket.timeout: pass def do_POST(self): handler = self._server.get_POST_handler(self.path) if not handler: self.send_error(404) return environ = {} environ['REQUEST_METHOD'] = "POST" fs = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ=environ) authinfo = self._server.get_authinfo(self.request, self.client_address) _add_authinfo(authinfo) try: handler(self, fs) except Exception, e: self.send_error(500, "Error processing POST request: %s" % e) del fs, environ _del_authinfo() def list_directory(self, path): self.send_error(404, "No permission to list directory") def log_request(self, code='-', size='-'): # Don't log requests pass def translate_path(self, path): """Translate a /-separated PATH to the local filename syntax. Components that mean special things to the local file system (e.g. drive or directory names) are ignored. (XXX They should probably be diagnosed.) This code is lifted from SimpleHTTPRequestHandler so that we can make sure the request is always based in our download directory, not the current directory. """ path = posixpath.normpath(urllib.unquote(path)) words = path.split('/') words = filter(None, words) path = self._server.http_dir for word in words: drive, word = os.path.splitdrive(word) head, word = os.path.split(word) if word in (os.curdir, os.pardir): continue path = os.path.join(path, word) return path if sys.version_info[:3] >= (2, 3, 0): def handle(self): """ Always close the connection when done """ self.close_connection = 1 try: self.handle_one_request() except SSL.Error, e: print e[0] except socket.timeout: pass class PlgBaseHTTPServer(object): def __init__(self, http_dir, auth_cb=None): self.http_dir = http_dir self._auth_callback = auth_cb self._POST_handlers = {} def set_POST_handler(self, location, handler): if handler: self._POST_handlers[location] = handler else: del self._POST_handlers[location] def get_POST_handler(self, location): if self._POST_handlers.has_key(location): return self._POST_handlers[location] return None def get_authinfo(self, request, client_address): if self._auth_callback: return self._auth_callback(request, client_address) return None class PlgHTTPSServer(SSLCommon.PlgBaseSSLServer, PlgBaseHTTPServer): def __init__(self, server_address, http_dir, certs, auth_cb=None): PlgBaseHTTPServer.__init__(self, http_dir, auth_cb) SSLCommon.PlgBaseSSLServer.__init__(self, server_address, PlgHTTPRequestHandler, certs) class PlgHTTPServer(SSLCommon.PlgBaseServer, PlgBaseHTTPServer): def __init__(self, server_address, http_dir, auth_cb=None): PlgBaseHTTPServer.__init__(self, http_dir, auth_cb) SSLCommon.PlgBaseServer.__init__(self, server_address, PlgHTTPRequestHandler) class PlgHTTPServerManager(threading.Thread): def __init__(self, addr, http_dir, certs=None, auth_cb=None): if certs and len(certs) > 0: self._server = PlgHTTPSServer(addr, http_dir, certs, auth_cb) else: self._server = PlgHTTPServer(addr, http_dir, auth_cb) self._stopped = False threading.Thread.__init__(self) self.setName("PlgHTTPServerManager: %s/%s" % (addr, http_dir)) def set_POST_handler(self, location, handler): self._server.set_POST_handler(location, handler) def get_POST_handler(self, location): return self._server.get_POST_handler(location) def run(self): DebugUtils.registerThreadName(self) self._server.serve_forever() self._stopped = True def stop(self): self._server.stop() t = time.time() try: while not self._stopped: if time.time() > t + 2: break except KeyboardInterrupt: pass ########################################################### # Testing stuff ########################################################### """ Use this HTML:
File to process:
""" def upload_callback(request_handler, fs): # Search for filename fslist = [fs] if not fs.name and not fs.filename and fs.value: fslist = fs.value jobid = arch = filename = tmpfile = None for item in fslist: if item.name == 'jobid': try: jobid = int(item.value) except ValueError: pass elif item.name == 'arch': arch = item.value elif item.name == 'filedata': filename = item.filename tmpfile = item.file if jobid is not None and arch and filename and tmpfile: import shutil dest = file("/tmp/server_dir/%s" % filename, "w+b") shutil.copyfileobj(tmpfile, dest) dest.close() request_handler.send_response(200, "Success") request_handler.send_header("Content-type", "text/html") request_handler.end_headers() request_handler.wfile.write("Success!") print "Finished with %d (%s) at %s" % (jobid, arch, filename) else: request_handler.send_error(400, "Invalid request for %s" % request_handler.path) def auth_cb(request, client_address): peer_cert = email = None try: peer_cert = request.get_peer_certificate() email = peer_cert.get_subject().emailAddress except Exception, e: print "Error getting auth info: %s" % e else: print "AuthInfo: peer=%s, email=%s" % (peer_cert, peer_cert.get_subject()) return None if __name__ == '__main__': if len(sys.argv) < 4: print "Usage: python HTTPServer.py key_and_cert ca_cert peer_ca_cert" sys.exit(1) certs = {} certs['key_and_cert'] = sys.argv[1] certs['ca_cert'] = sys.argv[2] certs['peer_ca_cert'] = sys.argv[3] print "Starting..." srcdir = os.path.join("/tmp", "server_dir") if not os.path.exists(srcdir): os.makedirs(srcdir) print "Creating random test data..." f = open(os.path.join(srcdir, "testfile.dat"), "w") x = 1 import random while x < 10000: try: f.write(os.urandom(50)) except AttributeError: s = str(random.randint(0, 255)) f.write(s) x = x + 1 f.close() print "Starting the server." server = PlgHTTPServerManager(('localhost', 8886), srcdir, certs) server.set_POST_handler('/upload', upload_callback) server.start() while True: try: time.sleep(1) except KeyboardInterrupt: print "Quitting..." os._exit(0)