Back to index

moin  1.9.0~rc2
serving.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - This module contains additional code related to serving
00004                requests with the standalone server. It uses werkzeug's
00005                BaseRequestHandler and overrides some functions that
00006                need to be handled different in MoinMoin than in werkzeug
00007 
00008     @copyright: 2008-2008 MoinMoin:FlorianKrupicka
00009     @license: GNU GPL, see COPYING for details.
00010 """
00011 import os
00012 from MoinMoin import config
00013 
00014 from MoinMoin import version, log
00015 logging = log.getLogger(__name__)
00016 
00017 # make werkzeug use our logging framework and configuration:
00018 import werkzeug._internal
00019 werkzeug._internal._logger = log.getLogger('werkzeug')
00020 
00021 from werkzeug import run_simple
00022 from werkzeug.serving import BaseRequestHandler
00023 
00024 class RequestHandler(BaseRequestHandler):
00025     """
00026     A request-handler for WSGI, that overrides the default logging
00027     mechanisms to log via MoinMoin's logging framework.
00028     """
00029     server_version = "MoinMoin %s %s" % (version.release,
00030                                          version.revision)
00031 
00032     # override the logging functions
00033     def log_request(self, code='-', size='-'):
00034         self.log_message('"%s" %s %s',
00035                          self.requestline, code, size)
00036 
00037     def log_error(self, format, *args):
00038         self.log_message(format, *args)
00039 
00040     def log_message(self, format, *args):
00041         logging.info("%s %s", self.address_string(), (format % args))
00042 
00043 class ProxyTrust(object):
00044     """
00045     Middleware that rewrites the remote address according to trusted
00046     proxies in the forward chain.
00047     """
00048 
00049     def __init__(self, app, proxies):
00050         self.app = app
00051         self.proxies = proxies
00052 
00053     def __call__(self, environ, start_response):
00054         if 'HTTP_X_FORWARDED_FOR' in environ:
00055             addrs = environ.pop('HTTP_X_FORWARDED_FOR').split(',')
00056             addrs = [addr.strip() for addr in addrs]
00057         elif 'REMOTE_ADDR' in environ:
00058             addrs = [environ['REMOTE_ADDR']]
00059         else:
00060             addrs = [None]
00061         result = [addr for addr in addrs if addr not in self.proxies]
00062         if result:
00063             environ['REMOTE_ADDR'] = result[-1]
00064         elif addrs[-1] is not None:
00065             environ['REMOTE_ADDR'] = addrs[-1]
00066         else:
00067             del environ['REMOTE_ADDR']
00068         return self.app(environ, start_response)
00069 
00070 def make_application(shared=None, trusted_proxies=None):
00071     """
00072     Make an instance of the MoinMoin WSGI application. This involves
00073     wrapping it in middlewares as needed (static files, debugging, etc.).
00074 
00075     @param shared: see MoinMoin.web.static.make_static_serving_app.
00076                    If falsy, do not use static serving app.
00077     @param trusted_proxies: list of trusted proxies. If None or empty, do not
00078                             use the ProxyTrust middleware.
00079     @rtype: callable
00080     @return: a WSGI callable
00081     """
00082     from MoinMoin.wsgiapp import application
00083 
00084     if trusted_proxies:
00085         application = ProxyTrust(application, trusted_proxies)
00086 
00087     if shared:
00088         from MoinMoin.web.static import make_static_serving_app
00089         application = make_static_serving_app(application, shared)
00090 
00091     return application
00092 
00093 def switch_user(uid, gid=None):
00094     """ Switch identity to safe user and group
00095 
00096     Does not support Windows, because the necessary calls are not available.
00097     TODO: can we use win32api calls to achieve the same effect on Windows?
00098 
00099     Raise RuntimeError if can't switch or trying to switch to root.
00100     """
00101     # no switch on windows
00102     if os.name == 'nt':
00103         return
00104 
00105     import pwd, grp
00106     if isinstance(uid, basestring):
00107         try:
00108             uid = pwd.getpwnam(uid)[2]
00109         except KeyError:
00110             raise RuntimeError("Unknown user: '%s', check user setting" % uid)
00111     if gid is not None and isinstance(gid, basestring):
00112         try:
00113             gid = grp.getgrnam(gid)[2]
00114         except KeyError:
00115             raise RuntimeError("Unknown group: '%s', check group setting" % gid)
00116 
00117     if uid == 0 or gid == 0:
00118         # We will not run as root. If you like to run a web
00119         # server as root, then hack this code.
00120         raise RuntimeError('will not run as root!')
00121     try:
00122         if gid:
00123             os.setgid(gid)
00124         os.setuid(uid)
00125     except (OSError, AttributeError):
00126         # Either we can't switch, or we are on windows, which does not have
00127         # those calls.
00128         raise RuntimeError("can't change uid/gid to %s/%s" % (uid, gid))
00129     logging.info("Running as uid/gid %d/%d" % (uid, gid))
00130 
00131 def run_server(hostname='localhost', port=8080,
00132                docs=True,
00133                debug='off',
00134                user=None, group=None,
00135                threaded=True,
00136                **kw):
00137     """ Run a standalone server on specified host/port. """
00138     application = make_application(shared=docs)
00139 
00140     if port < 1024 and os.getuid() != 0:
00141         raise RuntimeError('Must run as root to serve port number under 1024. '
00142                            'Run as root or change port setting.')
00143 
00144     if user:
00145         switch_user(user, group)
00146 
00147     if debug == 'external':
00148         # no threading is better for debugging, the main (and only)
00149         # thread then will just terminate when an exception happens
00150         threaded = False
00151 
00152     run_simple(hostname=hostname, port=port,
00153                application=application,
00154                threaded=threaded,
00155                use_debugger=(debug == 'web'),
00156                passthrough_errors=(debug == 'external'),
00157                request_handler=RequestHandler,
00158                **kw)
00159