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