Revision: 8052
          http://svn.sourceforge.net/mailman/?rev=8052&view=rev
Author:   tkikuchi
Date:     2006-10-08 19:03:45 -0700 (Sun, 08 Oct 2006)

Log Message:
-----------
WSGI HTTP Server for Mailman Web interface.
   Add: HTTPRunner.py ... Start/Restart/Stop HTTP Server under Runner framework.
        wsgi_app.py ... WSGI to CGI wrapper. Mostly taken from scripts/driver.
   loginit.py ... Add http log. Time stamp is duplicated :-(
   Defaults.py ... HTTP_HOST and HTTP_PORT.
Note that WSGI server should be used under reverse proxy environment.

Modified Paths:
--------------
    trunk/mailman/Mailman/Cgi/private.py
    trunk/mailman/Mailman/Defaults.py.in
    trunk/mailman/Mailman/loginit.py

Added Paths:
-----------
    trunk/mailman/Mailman/Cgi/wsgi_app.py
    trunk/mailman/Mailman/Queue/HTTPRunner.py

Modified: trunk/mailman/Mailman/Cgi/private.py
===================================================================
--- trunk/mailman/Mailman/Cgi/private.py        2006-10-08 18:23:22 UTC (rev 
8051)
+++ trunk/mailman/Mailman/Cgi/private.py        2006-10-09 02:03:45 UTC (rev 
8052)
@@ -29,6 +29,7 @@
 from Mailman import Errors
 from Mailman import i18n
 from Mailman.htmlformat import *
+from Mailman.configuration import config
 
 # Set up i18n.  Until we know which list is being requested, we use the
 # server's default.
@@ -80,7 +81,7 @@
         return
     # BAW: This needs to be converted to the Site module abstraction
     true_filename = os.path.join(
-        mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, tpath)
+        config.PRIVATE_ARCHIVE_FILE_DIR, tpath)
 
     listname = parts[0].lower()
     mboxfile = ''

Added: trunk/mailman/Mailman/Cgi/wsgi_app.py
===================================================================
--- trunk/mailman/Mailman/Cgi/wsgi_app.py                               (rev 0)
+++ trunk/mailman/Mailman/Cgi/wsgi_app.py       2006-10-09 02:03:45 UTC (rev 
8052)
@@ -0,0 +1,253 @@
+# -*- python -*-
+
+# Copyright (C) 2006 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+import sys
+STEALTH_MODE = False
+# Shoul this be configurable in Defaults.py?
+
+def websafe(s):
+    return s
+
+import os
+import StringIO
+from email import message_from_string
+from Mailman.configuration import config
+
+SCRIPTS = ['admin', 'admindb', 'confirm', 'create',
+           'edithtml', 'listinfo', 'options', 'private',
+           'rmlist', 'roster', 'subscribe']
+SLASH = '/'
+NL2 = '\n\n'
+CRLF2 = '\r\n\r\n'
+
+# WSGI to CGI wrapper.  Mostly copied from scripts/driver.
+
+def mailman_app(environ, start_response):
+    """wrapper to *.py cgi commands"""
+
+    global STEALTH_MODE, websafe
+    try:
+        try:
+            if not STEALTH_MODE:
+                from Mailman.Utils import websafe
+        except:
+            STEALTH_MODE = True
+            raise
+
+        import logging
+        log = logging.getLogger('mailman.error')
+
+        from Mailman import i18n
+        i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
+
+        path = environ['PATH_INFO']
+        paths = path.split(SLASH)
+        script = paths[1]
+        if script in SCRIPTS:
+            environ['SCRIPT_NAME'] = script
+            if len(paths) > 2:
+                environ['PATH_INFO'] = SLASH + SLASH.join(paths[2:])
+            else:
+                environ['PATH_INFO'] = ''
+           # reverse proxy environment.
+            if environ.has_key('HTTP_X_FORWARDED_HOST'):
+                environ['HTTP_HOST'] = environ['HTTP_X_FORWARDED_HOST']
+            if environ.has_key('HTTP_X_FORWARDED_FOR'):
+                environ['REMOTE_HOST'] = environ['HTTP_X_FORWARDED_FOR']
+            modname = 'Mailman.Cgi.' + script
+            # Clear previous cookie before setting new one.
+            os.environ['HTTP_COOKIE'] = ''
+            for k, v in environ.items():
+                os.environ[k] = str(v)
+            # Prepare for redirection.
+            save_stdin = sys.stdin
+            # CGI writes its output to sys.stdout, while wsgi app should
+            # return (list of) strings.
+            save_stdout = sys.stdout
+            save_stderr = sys.stderr
+            tmpstdout = StringIO.StringIO()
+            tmpstderr = StringIO.StringIO()
+            response = ''
+            try:
+                try:
+                    sys.stdin  = environ['wsgi.input']
+                    sys.stdout = tmpstdout
+                    sys.stderr = tmpstderr
+                    __import__(modname)
+                    sys.modules[modname].main()
+                    response = sys.stdout.getvalue()
+                finally:
+                    sys.stdin  = save_stdin
+                    sys.stdout = save_stdout
+                    sys.stderr = save_stderr
+            except SystemExit:
+                sys.stdout.write(tmpstdout.getvalue())
+            if response:
+                try:
+                    head, content = response.split(NL2, 1)
+                except ValueError:
+                    head, content = response.split(CRLF2, 1)
+                m = message_from_string(head + CRLF2)
+                start_response('200 OK', m.items())
+                return [content]
+            else:
+                # TBD: Error Code/Message
+                start_response('500 Server Error', [])
+                return '500 Internal Server Error'
+        else:
+             # TBD: Error Message
+             start_response('404 Not Found', [])
+             return '404 Not Found'
+    except:
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        retstring = print_traceback(log)
+        retstring +=print_environment(log)
+        return retstring
+
+
+
+# These functions are extracted and modified from scripts/driver.
+#
+# If possible, we print the error to two places.  One will always be stdout
+# and the other will be the log file if a log file was created.  It is assumed
+# that stdout is an HTML sink.
+def print_traceback(log=None):
+    try:
+        import traceback
+    except ImportError:
+        traceback = None
+    try:
+        from Mailman.mm_cfg import VERSION
+    except ImportError:
+        VERSION = '<undetermined>'
+
+    # Write to the log file first.
+    if log:
+        outfp = StringIO.StringIO()
+
+        print >> outfp, '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
+        print >> outfp, '[----- Mailman Version: %s -----]' % VERSION
+        print >> outfp, '[----- Traceback ------]'
+        if traceback:
+            traceback.print_exc(file=outfp)
+        else:
+            print >> outfp, '[failed to import module traceback]'
+            print >> outfp, '[exc: %s, var: %s]' % sys.exc_info()[0:2]
+        # Don't use .exception() since that'll give us the exception twice.
+        # IWBNI we could print directly to the log's stream, or treat a log
+        # like an output stream.
+        log.error('%s', outfp.getvalue())
+
+    # return HTML sink.
+    htfp = StringIO.StringIO()
+    print >> htfp, """\
+<head><title>Bug in Mailman version %(VERSION)s</title></head>
+<body bgcolor=#ffffff><h2>Bug in Mailman version %(VERSION)s</h2>
+<p><h3>We're sorry, we hit a bug!</h3>
+""" % locals()
+    if not STEALTH_MODE:
+        print >> htfp, '''<p>If you would like to help us identify the problem,
+please email a copy of this page to the webmaster for this site with
+a description of what happened.  Thanks!
+
+<h4>Traceback:</h4><p><pre>'''
+        exc_info = sys.exc_info()
+        if traceback:
+            for line in traceback.format_exception(*exc_info):
+                print >> htfp, websafe(line)
+        else:
+            print >> htfp, '[failed to import module traceback]'
+            print >> htfp, '[exc: %s, var: %s]' %\
+                            [websafe(x) for x in exc_info[0:2]]
+        print >> htfp, '\n\n</pre></body>'
+    else:
+        print >> htfp, '''<p>Please inform the webmaster for this site of this
+problem.  Printing of traceback and other system information has been
+explicitly inhibited, but the webmaster can find this information in the
+Mailman error logs.'''
+    return htfp.getvalue()
+
+
+
+def print_environment(log=None):
+    try:
+        import os
+    except ImportError:
+        os = None
+
+    if log:
+        outfp = StringIO.StringIO()
+
+        # Write some information about our Python executable to the log file.
+        print >> outfp, '[----- Python Information -----]'
+        print >> outfp, 'sys.version     =', sys.version
+        print >> outfp, 'sys.executable  =', sys.executable
+        print >> outfp, 'sys.prefix      =', sys.prefix
+        print >> outfp, 'sys.exec_prefix =', sys.exec_prefix
+        print >> outfp, 'sys.path        =', sys.exec_prefix
+        print >> outfp, 'sys.platform    =', sys.platform
+
+    # Write the same information to the HTML sink.
+    htfp = StringIO.StringIO()
+    if not STEALTH_MODE:
+        print >> htfp, '''\
+<p><hr><h4>Python information:</h4>
+
+<p><table>
+<tr><th>Variable</th><th>Value</th></tr>
+<tr><td><tt>sys.version</tt></td><td> %s </td></tr>
+<tr><td><tt>sys.executable</tt></td><td> %s </td></tr>
+<tr><td><tt>sys.prefix</tt></td><td> %s </td></tr>'
+<tr><td><tt>sys.exec_prefix</tt></td><td> %s </td></tr>
+<tr><td><tt>sys.path</tt></td><td> %s </td></tr>
+<tr><td><tt>sys.platform</tt></td><td> %s </td></tr>
+</table>''' % (sys.version, sys.executable, sys.prefix,
+               sys.exec_prefix, sys.path, sys.platform)
+
+    # Write environment variables to the log file.
+    if log:
+        print >> outfp, '[----- Environment Variables -----]'
+        if os:
+            for k, v in os.environ.items():
+                print >> outfp, '\t%s: %s' % (k, v)
+        else:
+            print >> outfp, '[failed to import module os]'
+
+    # Write environment variables to the HTML sink.
+    if not STEALTH_MODE:
+        print >> htfp, '''\
+<p><hr><h4>Environment variables:</h4>
+
+<p><table>
+<tr><th>Variable</th><th>Value</th></tr>
+'''
+        if os:
+            for k, v in os.environ.items():
+                print >> htfp, '<tr><td><tt>' + websafe(k) + \
+                      '</tt></td><td>' + websafe(v) + \
+                      '</td></tr>'
+            print >> htfp, '</table>'
+        else:
+            print >> htfp, '<p><hr>[failed to import module os]'
+
+    # Dump the log output
+    if log:
+        log.error('%s', outfp.getvalue())
+
+    return htfp.getvalue()

Modified: trunk/mailman/Mailman/Defaults.py.in
===================================================================
--- trunk/mailman/Mailman/Defaults.py.in        2006-10-08 18:23:22 UTC (rev 
8051)
+++ trunk/mailman/Mailman/Defaults.py.in        2006-10-09 02:03:45 UTC (rev 
8052)
@@ -726,6 +726,15 @@
 LMTP_HOST = 'localhost'
 LMTP_PORT = 8025
 
+# Experimental WSGI Server.
+# You must enable PROXY of Apache httpd server and configure to pass
+# mailman CGI requests to this WSGI Server:
+#     ProxyPass /mailman/ http://localhost:2580/
+HTTP_HOST = 'localhost'
+HTTP_PORT = 2580
+# Also you have to add following line to <prefix>/etc/mailman.cfg
+# QRUNNERS.append(('HTTPRunner', 1))
+
 # After processing every file in the qrunner's slice, how long should the
 # runner sleep for before checking the queue directory again for new files?
 # This can be a fraction of a second, or zero to check immediately

Added: trunk/mailman/Mailman/Queue/HTTPRunner.py
===================================================================
--- trunk/mailman/Mailman/Queue/HTTPRunner.py                           (rev 0)
+++ trunk/mailman/Mailman/Queue/HTTPRunner.py   2006-10-09 02:03:45 UTC (rev 
8052)
@@ -0,0 +1,63 @@
+# Copyright (C) 2006 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Mailman HTTP runner (server).
+
+"""
+
+import sys
+import logging
+import StringIO
+
+from wsgiref.simple_server import make_server, WSGIRequestHandler
+
+from Mailman.Queue.Runner import Runner
+from Mailman.configuration import config
+from Mailman.Cgi.wsgi_app import mailman_app
+
+hlog = logging.getLogger('mailman.http')
+qlog = logging.getLogger('mailman.qrunner')
+
+class HTTPRunner(Runner):
+
+    def __init__(self, slice=None, numslices=1):
+        pass
+
+    def _cleanup(self):
+        pass
+
+
+class MailmanWSGIRequestHandler(WSGIRequestHandler):
+
+    def handle(self):
+        """Handle a single HTTP request with error output to elog"""
+        stderr = StringIO.StringIO()
+        save_stderr = sys.stderr
+        sys.stderr = stderr
+        WSGIRequestHandler.handle(self)
+        sys.stderr = save_stderr
+        hlog.info(stderr.getvalue().strip())
+
+
+server = make_server(config.HTTP_HOST, config.HTTP_PORT,
+                     mailman_app,
+                     handler_class=MailmanWSGIRequestHandler)
+qlog.info('HTTPRunner qrunner started.')
+server.serve_forever()
+# We'll never get here, but just in case...
+qlog.info('HTTPRunner qrunner exiting.')
+

Modified: trunk/mailman/Mailman/loginit.py
===================================================================
--- trunk/mailman/Mailman/loginit.py    2006-10-08 18:23:22 UTC (rev 8051)
+++ trunk/mailman/Mailman/loginit.py    2006-10-09 02:03:45 UTC (rev 8052)
@@ -32,6 +32,7 @@
     'bounce',
     'config',
     'error',
+    'http',
     'locks',
     'mischief',
     'post',


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to