Mercurial > moin > 1.9
changeset 2339:505991ec14f3
removed renamed request and server implementations
author | Reimar Bauer <rb.proj AT googlemail DOT com> |
---|---|
date | Thu, 05 Jul 2007 21:28:36 +0200 |
parents | b902f2397c68 |
children | dbfe98af770a |
files | MoinMoin/request/CGI.py MoinMoin/request/CLI.py MoinMoin/request/FCGI.py MoinMoin/request/MODPYTHON.py MoinMoin/request/STANDALONE.py MoinMoin/request/TWISTED.py MoinMoin/request/WSGI.py MoinMoin/server/CGI.py MoinMoin/server/STANDALONE.py MoinMoin/server/TWISTED.py MoinMoin/server/WSGI.py |
diffstat | 11 files changed, 0 insertions(+), 1657 deletions(-) [+] |
line wrap: on
line diff
--- a/MoinMoin/request/CGI.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - CGI Request Implementation for std. CGI web servers - like Apache or IIS or others. - - @copyright: 2001-2003 by Juergen Hermann <jh@web.de>, - 2003-2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" -import sys, os, cgi - -from MoinMoin.request import RequestBase - -class Request(RequestBase): - """ specialized on CGI requests """ - - def __init__(self, properties={}): - try: - # force input/output to binary - if sys.platform == "win32": - import msvcrt - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) - msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) - - self._setup_vars_from_std_env(os.environ) - RequestBase.__init__(self, properties) - - except Exception, err: - self.fail(err) - - def _setup_args_from_cgi_form(self): - """ Override to create cgi form """ - form = cgi.FieldStorage() - return RequestBase._setup_args_from_cgi_form(self, form) - - def read(self, n=None): - """ Read from input stream. """ - if n is None: - return sys.stdin.read() - else: - return sys.stdin.read(n) - - def write(self, *data): - """ Write to output stream. """ - sys.stdout.write(self.encode(data)) - - def flush(self): - sys.stdout.flush() - - def finish(self): - RequestBase.finish(self) - # flush the output, ignore errors caused by the user closing the socket - try: - sys.stdout.flush() - except IOError, ex: - import errno - if ex.errno != errno.EPIPE: - raise - - def _emit_http_headers(self, headers): - """ private method to send out preprocessed list of HTTP headers """ - for header in headers: - self.write("%s\r\n" % header) - self.write("\r\n") -
--- a/MoinMoin/request/CLI.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - CLI Request Implementation for commandline usage. - - @copyright: 2001-2003 Juergen Hermann <jh@web.de>, - 2003-2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" -import sys - -from MoinMoin.request import RequestBase - -class Request(RequestBase): - """ specialized on command line interface and script requests """ - - def __init__(self, url='CLI', pagename='', properties={}): - self.saved_cookie = '' - self.path_info = '/' + pagename - self.query_string = '' - self.remote_addr = '127.0.0.1' - self.is_ssl = 0 - self.http_user_agent = 'CLI/Script' - self.url = url - self.request_method = 'GET' - self.request_uri = '/' + pagename # TODO check if /pagename works as URI for CLI usage - self.http_host = 'localhost' - self.http_referer = '' - self.script_name = '.' - self.if_modified_since = None - self.if_none_match = None - RequestBase.__init__(self, properties) - self.cfg.caching_formats = [] # don't spoil the cache - self.initTheme() # usually request.run() does this, but we don't use it - - def _setup_args_from_cgi_form(self): - """ Override to create cli form """ - #form = cgi.FieldStorage() - #return RequestBase._setup_args_from_cgi_form(self, form) - return {} - - def read(self, n=None): - """ Read from input stream. """ - if n is None: - return sys.stdin.read() - else: - return sys.stdin.read(n) - - def write(self, *data): - """ Write to output stream. """ - sys.stdout.write(self.encode(data)) - - def flush(self): - sys.stdout.flush() - - def finish(self): - RequestBase.finish(self) - # flush the output, ignore errors caused by the user closing the socket - try: - sys.stdout.flush() - except IOError, ex: - import errno - if ex.errno != errno.EPIPE: - raise - - def isForbidden(self): - """ Nothing is forbidden """ - return 0 - - # Accessors -------------------------------------------------------- - - def getQualifiedURL(self, uri=None): - """ Return a full URL starting with schema and host - - TODO: does this create correct pages when you render wiki pages - within a cli request?! - """ - return uri - - # Headers ---------------------------------------------------------- - - def setHttpHeader(self, header): - pass - - def _emit_http_headers(self, headers): - """ private method to send out preprocessed list of HTTP headers """ - pass - - def http_redirect(self, url): - """ Redirect to a fully qualified, or server-rooted URL - - TODO: Does this work for rendering redirect pages? - """ - raise Exception("Redirect not supported for command line tools!") - -
--- a/MoinMoin/request/FCGI.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - FastCGI Request Implementation for fastcgi and Apache - (and maybe others). - - @copyright: 2001-2003 Juergen Hermann <jh@web.de>, - 2003-2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" -from MoinMoin.request import RequestBase - -class Request(RequestBase): - """ specialized on FastCGI requests """ - - def __init__(self, fcgRequest, env, form, properties={}): - """ Initializes variables from FastCGI environment and saves - FastCGI request and form for further use. - - @param fcgRequest: the FastCGI request instance. - @param env: environment passed by FastCGI. - @param form: FieldStorage passed by FastCGI. - """ - try: - self.fcgreq = fcgRequest - self.fcgenv = env - self.fcgform = form - self._setup_vars_from_std_env(env) - RequestBase.__init__(self, properties) - - except Exception, err: - self.fail(err) - - def _setup_args_from_cgi_form(self): - """ Override to use FastCGI form """ - return RequestBase._setup_args_from_cgi_form(self, self.fcgform) - - def read(self, n=None): - """ Read from input stream. """ - if n is None: - return self.fcgreq.stdin.read() - else: - return self.fcgreq.stdin.read(n) - - def write(self, *data): - """ Write to output stream. """ - self.fcgreq.out.write(self.encode(data)) - - def flush(self): - """ Flush output stream. """ - self.fcgreq.flush_out() - - def finish(self): - """ Call finish method of FastCGI request to finish handling of this request. """ - RequestBase.finish(self) - self.fcgreq.finish() - - def _emit_http_headers(self, headers): - """ private method to send out preprocessed list of HTTP headers """ - for header in headers: - self.write("%s\r\n" % header) - self.write("\r\n") -
--- a/MoinMoin/request/MODPYTHON.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - mod_python Request Implementation for Apache and mod_python. - - @copyright: 2001-2003 Juergen Hermann <jh@web.de>, - 2003-2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" -from MoinMoin import wikiutil -from MoinMoin.request import RequestBase - -class Request(RequestBase): - """ specialized on mod_python requests """ - - def __init__(self, req): - """ Saves mod_pythons request and sets basic variables using - the req.subprocess_env, cause this provides a standard - way to access the values we need here. - - @param req: the mod_python request instance - """ - try: - # flags if headers sent out contained content-type or status - self._have_ct = 0 - self._have_status = 0 - - req.add_common_vars() - self.mpyreq = req - # some mod_python 2.7.X has no get method for table objects, - # so we make a real dict out of it first. - if not hasattr(req.subprocess_env, 'get'): - env = dict(req.subprocess_env) - else: - env = req.subprocess_env - self._setup_vars_from_std_env(env) - RequestBase.__init__(self) - - except Exception, err: - self.fail(err) - - def fixURI(self, env): - """ Fix problems with script_name and path_info using - PythonOption directive to rewrite URI. - - This is needed when using Apache 1 or other server which does - not support adding custom headers per request. With mod_python we - can use the PythonOption directive: - - <Location /url/to/mywiki/> - PythonOption X-Moin-Location /url/to/mywiki/ - </location> - - Note that *neither* script_name *nor* path_info can be trusted - when Moin is invoked as a mod_python handler with apache1, so we - must build both using request_uri and the provided PythonOption. - """ - # Be compatible with release 1.3.5 "Location" option - # TODO: Remove in later release, we should have one option only. - old_location = 'Location' - options_table = self.mpyreq.get_options() - if not hasattr(options_table, 'get'): - options = dict(options_table) - else: - options = options_table - location = options.get(self.moin_location) or options.get(old_location) - if location: - env[self.moin_location] = location - # Try to recreate script_name and path_info from request_uri. - import urlparse - scriptAndPath = urlparse.urlparse(self.request_uri)[2] - self.script_name = location.rstrip('/') - path = scriptAndPath.replace(self.script_name, '', 1) - self.path_info = wikiutil.url_unquote(path, want_unicode=False) - - RequestBase.fixURI(self, env) - - def _setup_args_from_cgi_form(self): - """ Override to use mod_python.util.FieldStorage - - It's little different from cgi.FieldStorage, so we need to - duplicate the conversion code. - """ - from mod_python import util - form = util.FieldStorage(self.mpyreq) - - args = {} - for key in form: - if key is None: - continue - values = form[key] - if not isinstance(values, list): - values = [values] - fixedResult = [] - - for item in values: - # Remember filenames with a name hack - if hasattr(item, 'filename') and item.filename: - args[key + '__filename__'] = item.filename - # mod_python 2.7 might return strings instead of Field - # objects. - if hasattr(item, 'value'): - item = item.value - fixedResult.append(item) - args[key] = fixedResult - - return self.decodeArgs(args) - - def run(self, req): - """ mod_python calls this with its request object. We don't - need it cause its already passed to __init__. So ignore - it and just return RequestBase.run. - - @param req: the mod_python request instance - """ - return RequestBase.run(self) - - def read(self, n=None): - """ Read from input stream. """ - if n is None: - return self.mpyreq.read() - else: - return self.mpyreq.read(n) - - def write(self, *data): - """ Write to output stream. """ - self.mpyreq.write(self.encode(data)) - - def flush(self): - """ We can't flush it, so do nothing. """ - pass - - def finish(self): - """ Just return apache.OK. Status is set in req.status. """ - RequestBase.finish(self) - # is it possible that we need to return something else here? - from mod_python import apache - return apache.OK - - def _emit_http_headers(self, headers): - """ private method to send out preprocessed list of HTTP headers """ - st_header, ct_header, other_headers = headers[0], headers[1], headers[2:] - status = st_header.split(':', 1)[1].lstrip() - self.mpyreq.status = int(status.split(' ', 1)[0]) - self.mpyreq.content_type = ct_header.split(':', 1)[1].lstrip() - for header in other_headers: - key, value = header.split(':', 1) - value = value.lstrip() - self.mpyreq.headers_out[key] = value - # this is for mod_python 2.7.X, for 3.X it's a NOP - self.mpyreq.send_http_header() -
--- a/MoinMoin/request/STANDALONE.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - Standalone Moin Server Request Implementation - - @copyright: 2001-2003 Juergen Hermann <jh@web.de>, - 2003-2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" -import cgi - -from MoinMoin.request import RequestBase - -class Request(RequestBase): - """ specialized on StandAlone Server (MoinMoin.server.standalone) requests """ - script_name = '' - - def __init__(self, sa, properties={}): - """ - @param sa: stand alone server object - @param properties: ... - """ - try: - self.sareq = sa - self.wfile = sa.wfile - self.rfile = sa.rfile - self.headers = sa.headers - self.is_ssl = 0 - - # Copy headers - self.http_accept_language = (sa.headers.getheader('accept-language') - or self.http_accept_language) - self.http_user_agent = sa.headers.getheader('user-agent', '') - co = [c for c in sa.headers.getheaders('cookie') if c] - self.saved_cookie = ', '.join(co) or '' - self.if_modified_since = sa.headers.getheader('if-modified-since') - self.if_none_match = sa.headers.getheader('if-none-match') - - # Copy rest from standalone request - self.server_name = sa.server.server_name - self.server_port = str(sa.server.server_port) - self.request_method = sa.command - self.request_uri = sa.path - self.remote_addr = sa.client_address[0] - - # Values that need more work - self.path_info, self.query_string = self.splitURI(sa.path) - self.setHttpReferer(sa.headers.getheader('referer')) - self.setHost(sa.headers.getheader('host')) - self.setURL(sa.headers) - - ##self.debugEnvironment(sa.headers) - - RequestBase.__init__(self, properties) - - except Exception, err: - self.fail(err) - - def _setup_args_from_cgi_form(self): - """ Override to create standalone form """ - form = cgi.FieldStorage(self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST'}) - return RequestBase._setup_args_from_cgi_form(self, form) - - def read(self, n=None): - """ Read from input stream - - Since self.rfile.read() will block, content-length will be used instead. - - TODO: test with n > content length, or when calling several times - with smaller n but total over content length. - """ - if n is None: - try: - n = int(self.headers.get('content-length')) - except (TypeError, ValueError): - import warnings - warnings.warn("calling request.read() when content-length is " - "not available will block") - return self.rfile.read() - return self.rfile.read(n) - - def write(self, *data): - """ Write to output stream. """ - self.wfile.write(self.encode(data)) - - def flush(self): - self.wfile.flush() - - def finish(self): - RequestBase.finish(self) - self.wfile.flush() - - # Headers ---------------------------------------------------------- - - def _emit_http_headers(self, headers): - """ private method to send out preprocessed list of HTTP headers """ - st_header, other_headers = headers[0], headers[1:] - status = st_header.split(':', 1)[1].lstrip() - status_code, status_msg = status.split(' ', 1) - status_code = int(status_code) - self.sareq.send_response(status_code, status_msg) - for header in other_headers: - key, value = header.split(':', 1) - value = value.lstrip() - self.sareq.send_header(key, value) - self.sareq.end_headers() -
--- a/MoinMoin/request/TWISTED.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - Twisted Request Implementation - - @copyright: 2001-2003 Juergen Hermann <jh@web.de>, - 2003-2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" - -from MoinMoin.request import RequestBase, MoinMoinFinish - -class Request(RequestBase): - """ specialized on Twisted requests """ - - def __init__(self, twistedRequest, pagename, reactor, properties={}): - try: - self.twistd = twistedRequest - self.reactor = reactor - - # Copy headers - self.http_accept_language = self.twistd.getHeader('Accept-Language') - self.saved_cookie = self.twistd.getHeader('Cookie') - self.http_user_agent = self.twistd.getHeader('User-Agent') - self.if_modified_since = self.twistd.getHeader('If-Modified-Since') - self.if_none_match = self.twistd.getHeader('If-None-Match') - - # Copy values from twisted request - self.server_protocol = self.twistd.clientproto - self.server_name = self.twistd.getRequestHostname().split(':')[0] - self.server_port = str(self.twistd.getHost()[2]) - self.is_ssl = self.twistd.isSecure() - self.path_info = '/' + '/'.join([pagename] + self.twistd.postpath) - self.request_method = self.twistd.method - self.remote_addr = self.twistd.getClientIP() - self.request_uri = self.twistd.uri - self.script_name = "/" + '/'.join(self.twistd.prepath[:-1]) - - # Values that need more work - self.query_string = self.splitURI(self.twistd.uri)[1] - self.setHttpReferer(self.twistd.getHeader('Referer')) - self.setHost() - self.setURL(self.twistd.getAllHeaders()) - - ##self.debugEnvironment(twistedRequest.getAllHeaders()) - - RequestBase.__init__(self, properties) - - except MoinMoinFinish: # might be triggered by http_redirect - self.emit_http_headers() # send headers (important for sending MOIN_ID cookie) - self.finish() - - except Exception, err: - # Wrap err inside an internal error if needed - from MoinMoin import error - if isinstance(err, error.FatalError): - self.delayedError = err - else: - self.delayedError = error.InternalError(str(err)) - - def run(self): - """ Handle delayed errors then invoke base class run """ - if hasattr(self, 'delayedError'): - self.fail(self.delayedError) - return self.finish() - RequestBase.run(self) - - def setup_args(self): - """ Return args dict - - Twisted already parsed args, including __filename__ hacking, - but did not decode the values. - """ - # TODO: check if for a POST this included query_string args (needed for - # TwikiDraw's action=AttachFile&do=savedrawing) - return self.decodeArgs(self.twistd.args) - - def read(self, n=None): - """ Read from input stream. """ - # XXX why is that wrong?: - #rd = self.reactor.callFromThread(self.twistd.read) - - # XXX do we need self.reactor.callFromThread with that? - # XXX if yes, why doesn't it work? - self.twistd.content.seek(0, 0) - if n is None: - rd = self.twistd.content.read() - else: - rd = self.twistd.content.read(n) - #print "request.RequestTwisted.read: data=\n" + str(rd) - return rd - - def write(self, *data): - """ Write to output stream. """ - #print "request.RequestTwisted.write: data=\n" + wd - self.reactor.callFromThread(self.twistd.write, self.encode(data)) - - def flush(self): - pass # XXX is there a flush in twisted? - - def finish(self): - RequestBase.finish(self) - self.reactor.callFromThread(self.twistd.finish) - - # Headers ---------------------------------------------------------- - - def _emit_http_headers(self, headers): - """ private method to send out preprocessed list of HTTP headers """ - st_header, other_headers = headers[0], headers[1:] - status = st_header.split(':', 1)[1].lstrip() - status_code, status_msg = status.split(' ', 1) - self.twistd.setResponseCode(int(status_code), status_msg) - for header in other_headers: - key, value = header.split(':', 1) - value = value.lstrip() - if key.lower() == 'set-cookie': - key, value = value.split('=', 1) - self.twistd.addCookie(key, value) - else: - self.twistd.setHeader(key, value) - - def http_redirect(self, url): - """ Redirect to a fully qualified, or server-rooted URL - - @param url: relative or absolute url, ascii using url encoding. - """ - url = self.getQualifiedURL(url) - self.twistd.redirect(url) - # calling finish here will send the rest of the data to the next - # request. leave the finish call to run() - #self.twistd.finish() - raise MoinMoinFinish -
--- a/MoinMoin/request/WSGI.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - WSGI Request Implementation for std. WSGI web servers. - - @copyright: 2001-2003 Juergen Hermann <jh@web.de>, - 2003-2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" -import cgi, StringIO - -from MoinMoin.request import RequestBase - -class Request(RequestBase): - """ specialized on WSGI requests """ - def __init__(self, env): - try: - self.env = env - self.hasContentType = False - - self.stdin = env['wsgi.input'] - self.stdout = StringIO.StringIO() - - # used by MoinMoin.server.wsgi: - self.status = '200 OK' - self.headers = [] - - self._setup_vars_from_std_env(env) - RequestBase.__init__(self, {}) - - except Exception, err: - self.fail(err) - - def setup_args(self): - # TODO: does this include query_string args for POST requests? - # see also how CGI works now - form = cgi.FieldStorage(fp=self.stdin, environ=self.env, keep_blank_values=1) - return RequestBase._setup_args_from_cgi_form(self, form) - - def read(self, n=None): - if n is None: - return self.stdin.read() - else: - return self.stdin.read(n) - - def write(self, *data): - self.stdout.write(self.encode(data)) - - def reset_output(self): - self.stdout = StringIO.StringIO() - - def _emit_http_headers(self, headers): - """ private method to send out preprocessed list of HTTP headers """ - st_header, other_headers = headers[0], headers[1:] - self.status = st_header.split(':', 1)[1].lstrip() - for header in other_headers: - key, value = header.split(':', 1) - value = value.lstrip() - self.headers.append((key, value)) - - def flush(self): - pass - - def output(self): - # called by MoinMoin.server.wsgi - return self.stdout.getvalue() - -
--- a/MoinMoin/server/CGI.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - CGI pseudo Server - - This is not really a server, it is just so that CGI stuff (the real - server is likely Apache or IIS or some other std. CGI server) looks - similar to what we have for Twisted and standalone server. - - Minimal usage: - - from MoinMoin.server.CGI import CgiConfig, run - - class Config(CgiConfig): - pass - - run(Config) - - See more options in CgiConfig class. - - @copyright: 2006 MoinMoin:ThomasWaldmann - @license: GNU GPL, see COPYING for details. -""" - -from MoinMoin.server import Config -from MoinMoin.request import CGI - -# Server globals -config = None - -# ------------------------------------------------------------------------ -# Public interface - -class CgiConfig(Config): - """ CGI default config """ - - name = 'moin' - properties = {} - logPath = None - - # Development options - hotshotProfile = None # e.g. "moin.prof" - - -def run(configClass): - """ Create and run a Cgi Request - - See CgiConfig for available options - - @param configClass: config class - """ - - config = configClass() - - if config.hotshotProfile: - import hotshot - config.hotshotProfile = hotshot.Profile(config.hotshotProfile) - config.hotshotProfile.start() - - request = CGI.Request(properties=config.properties) - request.run() - - if config.hotshotProfile: - config.hotshotProfile.close() - -
--- a/MoinMoin/server/STANDALONE.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,614 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - Stand-alone HTTP Server - - This is a simple, fast and very easy to install server. Its - recommended for personal wikis or public wikis with little load. - - It is not well tested in public wikis with heavy load. In these case - you might want to use twisted, fast cgi or mod python, or if you - can't use those, cgi. - - Minimal usage: - - from MoinMoin.server.STANDALONE import StandaloneConfig, run - - class Config(StandaloneConfig): - docs = '/usr/share/moin/wiki/htdocs' - user = 'www-data' - group = 'www-data' - - run(Config) - - See more options in StandaloneConfig class. - - For security, the server will not run as root. If you try to run it - as root, it will run as the user and group in the config. If you run - it as a normal user, it will run with your regular user and group. - - Significant contributions to this module by R. Church <rc@ghostbitch.org> - - @copyright: 2001-2004 MoinMoin:JuergenHermann, - 2005 MoinMoin:AlexanderSchremmer, - 2005 MoinMoin:NirSoffer - @license: GNU GPL, see COPYING for details. -""" - -import os, sys, time, socket, errno, shutil, logging -import BaseHTTPServer, SimpleHTTPServer, SocketServer - -from MoinMoin import version, wikiutil -from MoinMoin.server import Config, switchUID -from MoinMoin.request import STANDALONE -from MoinMoin.util import timefuncs - -# Server globals -httpd = None -config = None - - -class SimpleServer(BaseHTTPServer.HTTPServer): - """ Simplest server, serving one request after another - - This server is good for personal wiki, or when lowest memory - footprint is needed. - """ - use_threads = False - - def __init__(self, config): - self.htdocs = config.docs - self.request_queue_size = config.requestQueueSize - self._abort = 0 - address = (config.interface, config.port) - BaseHTTPServer.HTTPServer.__init__(self, address, MoinRequestHandler) - - def server_activate(self): - BaseHTTPServer.HTTPServer.server_activate(self) - logging.info("Serving on %s:%d" % self.server_address) - - def serve_forever(self): - """Handle one request at a time until we die """ - while not self._abort: - self.handle_request() - - def die(self): - """Abort this server instance's serving loop """ - # Close hotshot profiler - if config.hotshotProfile: - config.hotshotProfile.close() - - # Set abort flag, then make request to wake the server - self._abort = 1 - try: - import httplib - addr = self.server_address - if not addr[0]: - addr = ("localhost", addr[1]) - req = httplib.HTTP('%s:%d' % addr) - req.connect() - req.putrequest('DIE', '/') - req.endheaders() - del req - except socket.error, err: - # Ignore certain errors - if err.args[0] not in [errno.EADDRNOTAVAIL, ]: - raise - - -class ThreadingServer(SimpleServer): - """ Serve each request in a new thread - - This server is used since release 1.3 and seems to work nice with - little load. - - From release 1.3.5 there is a thread limit, that should help to - limit the load on the server. - """ - use_threads = True - - def __init__(self, config): - self.thread_limit = config.threadLimit - from threading import Condition - self.lock = Condition() - SimpleServer.__init__(self, config) - - def process_request(self, request, client_address): - """ Start a new thread to process the request - - If the thread limit has been reached, wait on the lock. The - next thread will notify when finished. - """ - from threading import Thread, activeCount - self.lock.acquire() - try: - if activeCount() > self.thread_limit: - self.lock.wait() - if self._abort: - return - t = Thread(target=self.process_request_thread, - args=(request, client_address)) - t.start() - finally: - self.lock.release() - - def process_request_thread(self, request, client_address): - """ Called for each request on a new thread - - Notify the main thread on the end of each request. - """ - try: - self.finish_request(request, client_address) - except: - self.handle_error(request, client_address) - self.close_request(request) - self.lock.acquire() - try: - # Main thread might be waiting - self.lock.notify() - finally: - self.lock.release() - - -class ThreadPoolServer(SimpleServer): - """ Threading server using a pool of threads - - This is a new experimental server, using a pool of threads instead - of creating new thread for each request. This is similar to Apache - worker mpm, with a simpler constant thread pool. - - This server is 5 times faster than ThreadingServer for static - files, and about the same for wiki pages. - - TODO: sometimes the server won't exit on Conrol-C, and continue to - run with few threads (you can kill it with kill -9). Same problem - exist with the twisted server. When the problem is finally solved, - remove the commented debug prints. - """ - use_threads = True - - def __init__(self, config): - self.queue = [] - # The size of the queue need more testing - self.queueSize = config.threadLimit * 2 - self.poolSize = config.threadLimit - from threading import Condition - self.lock = Condition() - SimpleServer.__init__(self, config) - - def serve_forever(self): - """ Create a thread pool then invoke base class method """ - from threading import Thread - for dummy in range(self.poolSize): - t = Thread(target=self.serve_forever_thread) - t.start() - SimpleServer.serve_forever(self) - - def process_request(self, request, client_address): - """ Called for each request - - Insert the request into the queue. If the queue is full, wait - until one of the request threads pop a request. During the wait, - new connections might be dropped. - """ - self.lock.acquire() - try: - if len(self.queue) >= self.queueSize: - self.lock.wait() - if self._abort: - return - self.queue.insert(0, (request, client_address)) - self.lock.notify() - finally: - self.lock.release() - - def serve_forever_thread(self): - """ The main loop of request threads - - Pop a request from the queue and process it. - """ - while not self._abort: - request, client_address = self.pop_request() - try: - self.finish_request(request, client_address) - except: - self.handle_error(request, client_address) - self.close_request(request) - # sys.stderr.write('thread exiting...\n') - - def pop_request(self): - """ Pop a request from the queue - - If the queue is empty, wait for notification. If the queue was - full, notify the main thread which may be waiting. - """ - self.lock.acquire() - try: - while not self._abort: - try: - item = self.queue.pop() - if len(self.queue) == self.queueSize - 1: - # Queue was full - main thread might be waiting - self.lock.notify() - return item - except IndexError: - self.lock.wait() - finally: - self.lock.release() - # sys.stderr.write('thread exiting...\n') - sys.exit() - - def die(self): - """ Wake all threads then invoke base class die - - Threads should exist when _abort is True. - """ - self._abort = True - self.wake_all_threads() - time.sleep(0.1) - SimpleServer.die(self) - - def wake_all_threads(self): - self.lock.acquire() - try: - # sys.stderr.write('waking up all threads...\n') - self.lock.notifyAll() - finally: - self.lock.release() - - -class ForkingServer(SocketServer.ForkingMixIn, SimpleServer): - """ Serve each request in a new process - - This is new untested server, first tests show rather pathetic cgi - like performance. No data is cached between requests. - - The mixin has its own process limit. - """ - max_children = 10 - - -class MoinRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): - - bufferSize = 8 * 1024 # used to serve static files - staticExpire = 365 * 24 * 3600 # 1 year expiry for static files - - def __init__(self, request, client_address, server): - self.server_version = "MoinMoin %s %s %s" % (version.release, - version.revision, - server.__class__.__name__) - self.expires = 0 - SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, request, - client_address, server) - - def log_message(self, format, *args): - logging.info("%s %s" % (self.address_string(), format % args)) - - # ------------------------------------------------------------------- - # do_METHOD dispatchers - called for each request - - def do_DIE(self): - if self.server._abort: - self.log_error("Shutting down") - - def do_ALL(self): - """ Handle requests (request type GET/HEAD/POST is in self.command) - - Separate between wiki pages and css and image url by similar - system as cgi and twisted, the url_prefix_static prefix. - """ - prefix = config.url_prefix_static - if self.path.startswith(prefix + '/'): - self.path = self.path[len(prefix):] - self.serve_static_file() - elif self.path in ['/favicon.ico', '/robots.txt']: - self.serve_static_file() - else: - self.serve_moin() - - do_POST = do_ALL - do_GET = do_ALL - do_HEAD = do_ALL - - # ------------------------------------------------------------------- - # Serve methods - - def serve_static_file(self): - """ Serve files from the htdocs directory """ - self.expires = self.staticExpire - path = self.path.split("?", 1) - if len(path) > 1: - self.path = path[0] # XXX ?params - - try: - fn = getattr(SimpleHTTPServer.SimpleHTTPRequestHandler, 'do_' + self.command) - fn(self) - except socket.error, err: - # Ignore certain errors - if err.args[0] not in [errno.EPIPE, errno.ECONNABORTED]: - raise - - def serve_moin(self): - """ Serve a request using moin """ - # don't make an Expires header for wiki pages - self.expires = 0 - - try: - req = STANDALONE.Request(self, properties=config.properties) - req.run() - except socket.error, err: - # Ignore certain errors - if err.args[0] not in [errno.EPIPE, errno.ECONNABORTED]: - raise - - def translate_path(self, uri): - """ 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. - """ - path = wikiutil.url_unquote(uri, want_unicode=False) - path = path.replace('\\', '/') - words = path.split('/') - words = filter(None, words) - - path = self.server.htdocs - bad_uri = 0 - for word in words: - drive, word = os.path.splitdrive(word) - if drive: - bad_uri = 1 - head, word = os.path.split(word) - if word in (os.curdir, os.pardir): - bad_uri = 1 - continue - path = os.path.join(path, word) - - if bad_uri: - self.log_error("Detected bad request URI '%s', translated to '%s'" - % (uri, path, )) - return path - - def end_headers(self): - """overload the default end_headers, inserting expires header""" - if self.expires: - now = time.time() - expires = now + self.expires - self.send_header('Expires', timefuncs.formathttpdate(expires)) - SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) - - def copyfile(self, source, outputfile): - """Copy all data between two file objects. - - Modify the base class method to change the buffer size. Test - shows that for the typical static files we serve, 8K buffer is - faster than the default 16K buffer. - """ - shutil.copyfileobj(source, outputfile, length=self.bufferSize) - - def address_string(self): - """We don't want to do reverse DNS lookups, just return IP address.""" - return self.client_address[0] - - -try: - from tlslite.api import TLSSocketServerMixIn, X509, X509CertChain, SessionCache, parsePEMKey - from tlslite.TLSConnection import TLSConnection -except ImportError: - pass -else: - class SecureRequestRedirect(BaseHTTPServer.BaseHTTPRequestHandler): - def handle(self): - self.close_connection = 1 - try: - self.raw_requestline = self.rfile.readline() - except socket.error: - return - if self.parse_request(): - host = self.headers.get('Host', socket.gethostname()) - path = self.path - else: - host = '%s:%s' % (socket.gethostname(), - self.request.getsockname()[1]) - path = '/' - - self.requestline = 'ERROR: Redirecting to https://%s%s' % (host, path) - self.request_version = 'HTTP/1.1' - self.command = 'GET' - self.send_response(301, 'Document Moved') - self.send_header('Date', self.date_time_string()) - self.send_header('Location', 'https://%s%s' % (host, path)) - self.send_header('Connection', 'close') - self.send_header('Content-Length', '0') - self.wfile.write('\r\n') - - class SecureThreadPoolServer(TLSSocketServerMixIn, ThreadPoolServer): - def __init__(self, config): - ThreadPoolServer.__init__(self, config) - - cert = open(config.ssl_certificate).read() - x509 = X509() - x509.parse(cert) - self.certChain = X509CertChain([x509]) - - priv = open(config.ssl_privkey).read() - self.privateKey = parsePEMKey(priv, private=True) - - self.sessionCache = SessionCache() - - def finish_request(self, sock, client_address): - # Peek into the packet, if it starts with GET or POS(T) then - # redirect, otherwise let TLSLite handle the connection. - peek = sock.recv(3, socket.MSG_PEEK).lower() - if peek == 'get' or peek == 'pos': - SecureRequestRedirect(sock, client_address, self) - return - tls_connection = TLSConnection(sock) - if self.handshake(tls_connection): - self.RequestHandlerClass(tls_connection, client_address, self) - else: - # This will probably fail because the TLSConnection has - # already written SSL stuff to the socket. But not sure what - # else we should do. - SecureRequestRedirect(sock, client_address, self) - - def handshake(self, tls_connection): - try: - tls_connection.handshakeServer(certChain=self.certChain, - privateKey=self.privateKey, - sessionCache=self.sessionCache) - tls_connection.ignoreAbruptClose = True - return True - except: - return False - - -def memoryProfileDecorator(func, profile): - """ Return a profiled function """ - def profiledFunction(*args, **kw): - profile.addRequest() - return func(*args, **kw) - return profiledFunction - - -def hotshotProfileDecorator(func, profile): - """ Return a profiled function """ - profile.moin_requests_done = 0 - def profiledFunction(*args, **kw): - profile.moin_requests_done += 1 - if profile.moin_requests_done == 1: - # Don't profile first request, its not interesting - return func(*args, **kw) - return profile.runcall(func, *args, **kw) - - return profiledFunction - - -def quit(signo, stackframe): - """ Signal handler for aborting signals """ - global httpd, config - logging.info("Thanks for using MoinMoin!") - - fname = config.pycallgraph_output - if fname: - import pycallgraph - if fname.endswith('.png'): - pycallgraph.make_dot_graph(fname) - elif fname.endswith('.dot'): - pycallgraph.save_dot(fname) - - if httpd: - httpd.die() - - -def registerSignalHandlers(func): - """ Register signal handlers on platforms that support it """ - try: - import signal - signal.signal(signal.SIGABRT, func) - signal.signal(signal.SIGINT, func) - signal.signal(signal.SIGTERM, func) - except ImportError: - pass - - -def makeServer(config): - """ Create a new server, based on the the platform capabilities - - Try to create the server class specified in the config. If threads - are not available, fallback to ForkingServer. If fork is not - available, fallback to a SimpleServer. - """ - serverClass = globals()[config.serverClass] - if serverClass.use_threads: - try: - import threading - except ImportError: - serverClass = ForkingServer - if serverClass is ForkingServer and not hasattr(os, "fork"): - serverClass = SimpleServer - if serverClass.__name__ != config.serverClass: - logging.error('%s is not available on this platform, falling back ' - 'to %s\n' % (config.serverClass, serverClass.__name__)) - - from MoinMoin import config as _config - _config.use_threads = serverClass.use_threads - return serverClass(config) - -# ------------------------------------------------------------------------ -# Public interface - -class StandaloneConfig(Config): - """ Standalone server default config """ - - name = 'moin' - properties = {} - docs = '/usr/share/moin/htdocs' - user = 'www-data' - group = 'www-data' - port = 8000 - interface = 'localhost' - logPath = None - - # Advanced options - serverClass = 'ThreadPoolServer' - threadLimit = 10 - # The size of the listen backlog. Twisted uses a default of 50. - # Tests on Mac OS X show many failed request with backlog of 5 or 10. - requestQueueSize = 50 - - # Development options - memoryProfile = None - hotshotProfile = None - pycallgraph_output = None - -def run(configClass): - """ Create and run a moin server - - See StandaloneConfig for available options - - @param configClass: config class - """ - # Run only once! - global httpd, config - if httpd is not None: - raise RuntimeError("You can run only one server per process!") - - config = configClass() - - # Install hotshot profiled serve_moin method. To compare with other - # servers, we profile the part that create and run the request. - if config.hotshotProfile: - import hotshot - config.hotshotProfile = hotshot.Profile(config.hotshotProfile) - MoinRequestHandler.serve_moin = hotshotProfileDecorator( - MoinRequestHandler.serve_moin, config.hotshotProfile) - - # Install a memory profiled serve_moin method - if config.memoryProfile: - config.memoryProfile.sample() - MoinRequestHandler.serve_moin = memoryProfileDecorator( - MoinRequestHandler.serve_moin, config.memoryProfile) - - # initialize pycallgraph, if wanted - if config.pycallgraph_output: - try: - import pycallgraph - pycallgraph.settings['include_stdlib'] = False - pcg_filter = pycallgraph.GlobbingFilter(exclude=['pycallgraph.*', - 'unknown.*', - ], - max_depth=9999) - pycallgraph.start_trace(reset=True, filter_func=pcg_filter) - except ImportError: - config.pycallgraph_output = None - - - registerSignalHandlers(quit) - httpd = makeServer(config) - - # Run as a safe user (posix only) - if os.name == 'posix' and os.getuid() == 0: - switchUID(config.uid, config.gid) - - httpd.serve_forever() -
--- a/MoinMoin/server/TWISTED.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin.server.twistedmoin - - Create standalone twisted based server. - - Minimal usage: - - from MoinMoin.server.TWISTED import TwistedConfig, makeApp - - class Config(TwistedConfig): - docs = '/usr/share/moin/wiki/htdocs' - user = 'www-data' - group = 'www-data' - - application = makeApp(Config) - - Then run this code with twistd -y yourcode.py. See moin_twisted script. - - @copyright: 2004 Thomas Waldmann, Oliver Graf, Nir Soffer - @license: GNU GPL, see COPYING for details. -""" - -from twisted.application import internet, service -from twisted.web import static, server, vhost, resource -from twisted.internet import threads, reactor - -try: - from twisted.internet import ssl -except ImportError: - ssl = None - -# Enable threads -from twisted.python import threadable -threadable.init(1) - -# MoinMoin imports -from MoinMoin.request import TWISTED -from MoinMoin.server import Config - -# Set threads flag, so other code can use proper locking -from MoinMoin import config -config.use_threads = True -del config - -# Server globals -config = None - - -class WikiResource(resource.Resource): - """ Wiki resource """ - isLeaf = 1 - - def render(self, request): - return server.NOT_DONE_YET - - -class WikiRoot(resource.Resource): - """ Wiki root resource """ - - def getChild(self, name, request): - # Serve images and css from url_prefix_static - if request.prepath == [] and name == config.url_prefix_static[1:]: - return resource.Resource.getChild(self, name, request) - - # Serve special 'root' files from url_prefix_static - elif name in ['favicon.ico', 'robots.txt'] and request.postpath == []: - return self.children[config.url_prefix_static[1:]].getChild(name, request) - - # All other through moin - - # TODO: fix profile code to include the request init and ignore - # first request. I'm not doing this now since its better done - # with the new twisted code waiting in fix branch. --Nir - else: - if config.memoryProfile: - config.memoryProfile.addRequest() - req = TWISTED.Request(request, name, reactor, properties=config.properties) - if config.hotshotProfile: - threads.deferToThread(config.hotshotProfile.runcall, req.run) - else: - threads.deferToThread(req.run) - return WikiResource() - - -class MoinRequest(server.Request): - """ MoinMoin request - - Enable passing of file-upload filenames - """ - - def requestReceived(self, command, path, version): - """ Called by channel when all data has been received. - - Override server.Request method for POST requests, to fix file - uploads issue. - """ - if command == 'POST': - self.requestReceivedPOST(path, version) - else: - server.Request.requestReceived(self, command, path, version) - - def requestReceivedPOST(self, path, version): - """ Handle POST requests - - This is a modified copy of server.Request.requestRecived, - modified to use cgi.FieldStorage to handle file uploads - correctly. - - Creates an extra member extended_args which also has - filenames of file uploads ( FIELDNAME__filename__ ). - """ - import cgi - - self.content.seek(0, 0) - self.args = {} - self.extended_args = {} - self.stack = [] - - self.method = 'POST' - self.uri = path - self.clientproto = version - x = self.uri.split('?') - - argstring = "" - if len(x) == 1: - self.path = self.uri - else: - if len(x) != 2: - from twisted.python import log - log.msg("May ignore parts of this invalid URI: %s" - % repr(self.uri)) - self.path, argstring = x[0], x[1] - - # cache the client and server information, we'll need this later to be - # serialized and sent with the request so CGIs will work remotely - self.client = self.channel.transport.getPeer() - self.host = self.channel.transport.getHost() - - # create dummy env for cgi.FieldStorage - env = { - 'REQUEST_METHOD': self.method, - 'QUERY_STRING': argstring, - } - form = cgi.FieldStorage(fp=self.content, - environ=env, - headers=self.received_headers) - - # Argument processing - - args = self.args - try: - keys = form.keys() - except TypeError: - pass - else: - for key in keys: - values = form[key] - if not isinstance(values, list): - values = [values] - fixedResult = [] - for i in values: - if isinstance(i, cgi.MiniFieldStorage): - fixedResult.append(i.value) - elif isinstance(i, cgi.FieldStorage): - fixedResult.append(i.value) - # multiple uploads to same form field are stupid! - if i.filename: - args[key + '__filename__'] = i.filename - args[key] = fixedResult - - self.process() - - -class MoinSite(server.Site): - """ Moin site """ - requestFactory = MoinRequest - - def startFactory(self): - """ Setup before starting """ - # Memory profile - if config.memoryProfile: - config.memoryProfile.sample() - - # hotshot profile - if config.hotshotProfile: - import hotshot - config.hotshotProfile = hotshot.Profile(config.hotshotProfile) - server.Site.startFactory(self) - - def stopFactory(self): - """ Cleaup before stoping """ - server.Site.stopFactory(self) - if config.hotshotProfile: - config.hotshotProfile.close() - - -class TwistedConfig(Config): - """ Twisted server default config """ - - name = 'mointwisted' - properties = {} - docs = '/usr/share/moin/htdocs' - user = 'www-data' - group = 'www-data' - port = 8080 - interfaces = [''] - threads = 10 - timeout = 15*60 # 15 minutes - logPath = None # moin log file - logPath_twisted = None # Twisted log file - virtualHosts = None - memoryProfile = None - hotshotProfile = None - - # sslcert = ('/whereever/cert/sitekey.pem', '/whereever/cert/sitecert.pem') - sslcert = None - - def __init__(self): - Config.__init__(self) - - # Check for '' in interfaces, then ignore other - if '' in self.interfaces: - self.interfaces = [''] - - -def makeApp(ConfigClass): - """ Generate and return an application - - See MoinMoin.server.Config for config options - - @param ConfigClass: config class - @rtype: application object - @return twisted application, needed by twistd - """ - # Create config instance (raise RuntimeError if config invalid) - global config - config = ConfigClass() - - # Set number of threads - reactor.suggestThreadPoolSize(config.threads) - - # The root of the HTTP hierarchy - default = WikiRoot() - - # Here is where img and css and some special files come from - default.putChild(config.url_prefix_static[1:], static.File(config.docs)) - - # Generate the Site factory - # TODO: Maybe we can use WikiRoot instead of this - # ---------------------------------------------- - root = vhost.NameVirtualHost() - root.default = default - # ---------------------------------------------- - site = MoinSite(root, logPath=config.logPath_twisted, timeout=config.timeout) - - # Make application - application = service.Application("web", uid=config.uid, gid=config.gid) - sc = service.IServiceCollection(application) - - # Listen to all interfaces in config.interfaces - for entry in config.interfaces: - # Add a TCPServer for each interface. - - # This is an hidden experimantal feature: each entry in - # interface may contain a port, using 'ip:port'. - # Note: the format is subject to change! - try: - interface, port = entry.split(':', 1) - except ValueError: - interface, port = entry, config.port - - # Might raise ValueError if not integer. - # TODO: check if we can use string port, like 'http' - port = int(port) - - if port == 443 and ssl and ssl.supported and config.sslcert: - sslContext = ssl.DefaultOpenSSLContextFactory(*config.sslcert) - s = internet.SSLServer(port, site, sslContext, interface=interface) - else: - s = internet.TCPServer(port, site, interface=interface) - s.setServiceParent(sc) - - return application -
--- a/MoinMoin/server/WSGI.py Thu Jul 05 21:23:29 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -""" - MoinMoin - WSGI application - - @copyright: 2005 Anakim Border <akborder@gmail.com> - @license: GNU GPL, see COPYING for details. -""" - -from MoinMoin.request import WSGI - -def moinmoinApp(environ, start_response): - request = WSGI.Request(environ) - request.run() - start_response(request.status, request.headers) - return [request.output()] -