Mercurial > moin > 1.9
changeset 2340:dbfe98af770a
Merge with main.
author | Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl> |
---|---|
date | Thu, 05 Jul 2007 22:11:16 +0200 |
parents | 1d85514968a2 (current diff) 505991ec14f3 (diff) |
children | 338334a0d38d 24242585fd70 |
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 MoinMoin/xmlrpc/_tests/test_multicall.py |
diffstat | 35 files changed, 1682 insertions(+), 1681 deletions(-) [+] |
line wrap: on
line diff
--- a/MoinMoin/action/SubscribeUser.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/action/SubscribeUser.py Thu Jul 05 22:11:16 2007 +0200 @@ -116,8 +116,8 @@ request_url = "localhost/" # Setup MoinMoin environment - from MoinMoin.request import CLI - request = CLI.Request(url=request_url) + from MoinMoin.request import request_cli + request = request_cli.Request(url=request_url) request.form = request.args = request.setup_args() from MoinMoin.formatter.text_plain import Formatter
--- a/MoinMoin/auth/http.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/auth/http.py Thu Jul 05 22:11:16 2007 +0200 @@ -12,8 +12,9 @@ 2007 MoinMoin:JohannesBerg @license: GNU GPL, see COPYING for details. """ + from MoinMoin import config, user -from MoinMoin.request import TWISTED, CLI, STANDALONE +from MoinMoin.request import request_twisted, request_cli, request_standalone from MoinMoin.auth import BaseAuth from base64 import decodestring @@ -33,7 +34,7 @@ # for standalone, request authorization and verify it, # deny access if it isn't verified - if isinstance(request, STANDALONE.Request): + if isinstance(request, request_standalone.Request): request.setHttpHeader('WWW-Authenticate: Basic realm="MoinMoin"') auth = request.headers.get('Authorization') if auth: @@ -45,14 +46,14 @@ if not u: request.makeForbidden(401, _('You need to log in.')) # for Twisted, just check - elif isinstance(request, TWISTED.Request): + elif isinstance(request, request_twisted.Request): username = request.twistd.getUser().decode(config.charset) password = request.twistd.getPassword().decode(config.charset) # when using Twisted http auth, we use username and password from # the moin user profile, so both can be changed by user. u = user.User(request, auth_username=username, password=password, auth_method=self.name, auth_attribs=()) - elif not isinstance(request, CLI.Request): + elif not isinstance(request, request_cli.Request): env = request.env auth_type = env.get('AUTH_TYPE', '') if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', ]:
--- a/MoinMoin/auth/sslclientcert.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/auth/sslclientcert.py Thu Jul 05 22:11:16 2007 +0200 @@ -11,7 +11,7 @@ """ from MoinMoin import config, user -from MoinMoin.request import TWISTED +from MoinMoin.request import request_twisted from MoinMoin.auth import BaseAuth class SSLClientCertAuth(BaseAuth): @@ -34,7 +34,7 @@ u = None changed = False # check if we are running Twisted - if isinstance(request, TWISTED.Request): + if isinstance(request, request_twisted.Request): return user_obj, True # not supported if we run twisted # Addendum: this seems to need quite some twisted insight and coding. # A pointer i got on #twisted: divmod's vertex.sslverify
--- a/MoinMoin/conftest.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/conftest.py Thu Jul 05 22:11:16 2007 +0200 @@ -71,13 +71,13 @@ def init_test_request(static_state=[False]): - from MoinMoin.request import CLI + from MoinMoin.request import request_cli from MoinMoin.user import User from MoinMoin.formatter.text_html import Formatter as HtmlFormatter if not static_state[0]: maketestwiki.run(True) static_state[0] = True - request = CLI.Request() + request = request_cli.Request() request.form = request.args = request.setup_args() request.user = User(request) request.html_formatter = HtmlFormatter(request)
--- a/MoinMoin/mail/mailimport.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/mail/mailimport.py Thu Jul 05 22:11:16 2007 +0200 @@ -17,7 +17,7 @@ from MoinMoin.action.AttachFile import add_attachment, AttachmentAlreadyExists from MoinMoin.Page import Page from MoinMoin.PageEditor import PageEditor -from MoinMoin.request.CLI import Request as RequestCLI +from MoinMoin.request.request_cli import Request as RequestCLI # python, at least up to 2.4, ships a broken parser for headers from MoinMoin.support.HeaderFixed import decode_header
--- a/MoinMoin/packages.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/packages.py Thu Jul 05 22:11:16 2007 +0200 @@ -533,8 +533,8 @@ request_url = "localhost/" # Setup MoinMoin environment - from MoinMoin.request import CLI - request = CLI.Request(url='localhost/') + from MoinMoin.request import request_cli + request = request_cli.Request(url='localhost/') request.form = request.args = request.setup_args() package = ZipPackage(request, packagefile)
--- a/MoinMoin/request/CGI.py Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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() - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/request/request_cgi.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,65 @@ +# -*- 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") +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/request/request_cli.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,95 @@ +# -*- 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!") + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/request/request_fcgi.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,62 @@ +# -*- 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") +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/request/request_modpython.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,151 @@ +# -*- 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() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/request/request_standalone.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,106 @@ +# -*- 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() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/request/request_twisted.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,132 @@ +# -*- 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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/request/request_wsgi.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,67 @@ +# -*- 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/script/__init__.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/script/__init__.py Thu Jul 05 22:11:16 2007 +0200 @@ -45,7 +45,7 @@ class ScriptRequestCLI(ScriptRequest): """ When a script runs directly on the shell, we just use the CLI request - object (see MoinMoin.request.CLI) to do I/O (which will use stdin/out/err). + object (see MoinMoin.request.request_cli) to do I/O (which will use stdin/out/err). """ def __init__(self, request): self.request = request @@ -172,11 +172,11 @@ def init_request(self): """ create request """ - from MoinMoin.request import CLI + from MoinMoin.request import request_cli if self.options.wiki_url: - self.request = CLI.Request(self.options.wiki_url, self.options.page) + self.request = request_cli.Request(self.options.wiki_url, self.options.page) else: - self.request = CLI.Request(pagename=self.options.page) + self.request = request_cli.Request(pagename=self.options.page) def mainloop(self): # Insert config dir or the current directory to the start of the path.
--- a/MoinMoin/search/builtin.py Wed Jul 04 12:12:00 2007 +0200 +++ b/MoinMoin/search/builtin.py Thu Jul 05 22:11:16 2007 +0200 @@ -399,7 +399,7 @@ @param request: current request """ - from MoinMoin.request.CLI import Request + from MoinMoin.request.request_cli import Request from MoinMoin.security import Permissions request = Request(request.url) class SecurityPolicy(Permissions):
--- a/MoinMoin/server/CGI.py Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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 Wed Jul 04 12:12:00 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()] -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/server/server_cgi.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,65 @@ +# -*- 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.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 request_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 = request_cgi.Request(properties=config.properties) + request.run() + + if config.hotshotProfile: + config.hotshotProfile.close() + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/server/server_standalone.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,614 @@ +# -*- 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.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 request_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 = request_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() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/server/server_twisted.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,285 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin.server.twistedmoin + + Create standalone twisted based server. + + Minimal usage: + + from MoinMoin.server.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 request_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 = request_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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinMoin/server/server_wsgi.py Thu Jul 05 22:11:16 2007 +0200 @@ -0,0 +1,15 @@ +""" + MoinMoin - WSGI application + + @copyright: 2005 Anakim Border <akborder@gmail.com> + @license: GNU GPL, see COPYING for details. +""" + +from MoinMoin.request import request_wsgi + +def moinmoinApp(environ, start_response): + request = request_wsgi.Request(environ) + request.run() + start_response(request.status, request.headers) + return [request.output()] +
--- a/contrib/phpwiki_migration/phpwiki2moinmoin.py Wed Jul 04 12:12:00 2007 +0200 +++ b/contrib/phpwiki_migration/phpwiki2moinmoin.py Thu Jul 05 22:11:16 2007 +0200 @@ -110,7 +110,7 @@ sys.path.append(wikipath) from MoinMoin.PageEditor import PageEditor -from MoinMoin.request.CLI import Request +from MoinMoin.request.request_cli import Request # the block parser deals with the whole text to be converted #
--- a/moin.py Wed Jul 04 12:12:00 2007 +0200 +++ b/moin.py Thu Jul 05 22:11:16 2007 +0200 @@ -44,7 +44,7 @@ ## import os ## os.environ['MOIN_DEBUG'] = '1' -from MoinMoin.server.STANDALONE import StandaloneConfig, run +from MoinMoin.server.server_standalone import StandaloneConfig, run from MoinMoin.version import project, release, revision print "%s - %s [%s]" % (project, release, revision)
--- a/wiki/server/moinmodpy.py Wed Jul 04 12:12:00 2007 +0200 +++ b/wiki/server/moinmodpy.py Thu Jul 05 22:11:16 2007 +0200 @@ -56,9 +56,9 @@ del config -from MoinMoin.request import MODPYTHON +from MoinMoin.request import request_modpython def handler(request): - moinreq = MODPYTHON.Request(request) + moinreq = request_modpython.Request(request) return moinreq.run(request)
--- a/wiki/server/mointwisted.py Wed Jul 04 12:12:00 2007 +0200 +++ b/wiki/server/mointwisted.py Thu Jul 05 22:11:16 2007 +0200 @@ -27,7 +27,7 @@ ## import os ## os.environ['MOIN_DEBUG'] = '1' -from MoinMoin.server.TWISTED import TwistedConfig, makeApp +from MoinMoin.server.server_twisted import TwistedConfig, makeApp class Config(TwistedConfig):
--- a/wiki/server/moinwsgi.py Wed Jul 04 12:12:00 2007 +0200 +++ b/wiki/server/moinwsgi.py Thu Jul 05 22:11:16 2007 +0200 @@ -14,7 +14,7 @@ del config from flup.server.fcgi import WSGIServer -from MoinMoin.server.WSGI import moinmoinApp +from MoinMoin.server.server_wsgi import moinmoinApp import os if __name__ == '__main__':