Skip to content

Commit f1704a9

Browse files
author
Richard Jones
committed
WSGI support via roundup.cgi.wsgi_handler
1 parent 60f32fd commit f1704a9

File tree

10 files changed

+374
-30
lines changed

10 files changed

+374
-30
lines changed

CHANGES.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
This file contains the changes to the Roundup system over time. The entries
22
are given with the most recent entry first.
33

4-
2006-??-?? 1.2.2
4+
2006-??-?? 1.3.0
5+
Feature:
6+
- WSGI support via roundup.cgi.wsgi_handler
7+
58
Fixed:
69
- sqlite module detection was broken for python 2.5 compiled without sqlite
710
support

MANIFEST.in

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ recursive-include roundup *.*
22
recursive-include frontends *.*
33
recursive-include scripts *.* *-*
44
recursive-include tools *.*
5-
recursive-include cgi-bin *.cgi
65
recursive-include test *.py *.txt
76
recursive-include doc *.html *.png *.txt *.css *.1 *.example
87
recursive-include detectors *.py

doc/installation.txt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ There are three web interfaces to choose from:
318318
2. `stand-alone web server`_
319319
3. `Zope product - ZRoundup`_
320320
4. `Apache HTTP Server with mod_python`_
321+
5. `WSGI handler`_
321322

322323
You may need to give the web server user permission to access the tracker home
323324
- see the `UNIX environment steps`_ for information. You may also need to
@@ -347,7 +348,7 @@ More information about ISS setup may be found at:
347348

348349
http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B276494
349350

350-
Copy the ``cgi-bin/roundup.cgi`` file to your web server's ``cgi-bin``
351+
Copy the ``frontends/roundup.cgi`` file to your web server's ``cgi-bin``
351352
directory. You will need to configure it to tell it where your tracker home
352353
is. You can do this either:
353354

@@ -507,6 +508,25 @@ Example mod_python configuration::
507508
PythonOption TrackerHome /var/db/roundup/devel
508509
</Directory>
509510

511+
WSGI Handler
512+
~~~~~~~~~~~~
513+
514+
The WSGI handler is quite simple. The following sample code shows how
515+
to use it::
516+
517+
from wsgiref.simple_server import make_server
518+
519+
# obtain the WSGI request dispatcher
520+
from roundup.cgi.wsgi_handler import RequestDispatcher
521+
tracker_home = 'demo'
522+
app = RequestDispatcher(tracker_home)
523+
524+
httpd = make_server('', 8917, app)
525+
httpd.serve_forever()
526+
527+
To test the above you should create a demo tracker with ``python demo.py``.
528+
Edit the ``config.ini`` to change the web URL to "http://localhost:8917/".
529+
510530

511531
Configure an Email Interface
512532
----------------------------

frontends/README.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
This directory contains alternate front-ends for Roundup.
22

3-
Zope - ZRoundup
4-
---------------
3+
roundup.cgi
4+
This is a cgi-bin script.
55

6-
This installs as a regular Zope product. See Roundup's doc/installation.txt
7-
for more info.
6+
ZRoundup
7+
This is a simple Zope frontend that installs as a regular Zope product.
88

9+
See Roundup's doc/installation.txt for more info on installing these
10+
frontends.

frontends/roundup.cgi

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
4+
# This module is free software, and you may redistribute it and/or modify
5+
# under the same terms as Python, so long as this copyright message and
6+
# disclaimer are retained in their original form.
7+
#
8+
# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
9+
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
10+
# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
11+
# POSSIBILITY OF SUCH DAMAGE.
12+
#
13+
# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
14+
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
15+
# FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
16+
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
17+
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
18+
#
19+
# $Id: roundup.cgi,v 1.1 2006-11-09 00:36:21 richard Exp $
20+
21+
# python version check
22+
from roundup import version_check
23+
from roundup.i18n import _
24+
import sys, time
25+
26+
#
27+
## Configuration
28+
#
29+
30+
# Configuration can also be provided through the OS environment (or via
31+
# the Apache "SetEnv" configuration directive). If the variables
32+
# documented below are set, they _override_ any configuation defaults
33+
# given in this file.
34+
35+
# TRACKER_HOMES is a list of trackers, in the form
36+
# "NAME=DIR<sep>NAME2=DIR2<sep>...", where <sep> is the directory path
37+
# separator (";" on Windows, ":" on Unix).
38+
39+
# Make sure the NAME part doesn't include any url-unsafe characters like
40+
# spaces, as these confuse the cookie handling in browsers like IE.
41+
42+
# ROUNDUP_LOG is the name of the logfile; if it's empty or does not exist,
43+
# logging is turned off (unless you changed the default below).
44+
45+
# DEBUG_TO_CLIENT specifies whether debugging goes to the HTTP server (via
46+
# stderr) or to the web client (via cgitb).
47+
DEBUG_TO_CLIENT = False
48+
49+
# This indicates where the Roundup tracker lives
50+
TRACKER_HOMES = {
51+
# 'example': '/path/to/example',
52+
}
53+
54+
# Where to log debugging information to. Use an instance of DevNull if you
55+
# don't want to log anywhere.
56+
class DevNull:
57+
def write(self, info):
58+
pass
59+
def close(self):
60+
pass
61+
def flush(self):
62+
pass
63+
#LOG = open('/var/log/roundup.cgi.log', 'a')
64+
LOG = DevNull()
65+
66+
#
67+
## end configuration
68+
#
69+
70+
71+
#
72+
# Set up the error handler
73+
#
74+
try:
75+
import traceback, StringIO, cgi
76+
from roundup.cgi import cgitb
77+
except:
78+
print "Content-Type: text/plain\n"
79+
print _("Failed to import cgitb!\n\n")
80+
s = StringIO.StringIO()
81+
traceback.print_exc(None, s)
82+
print s.getvalue()
83+
84+
85+
#
86+
# Check environment for config items
87+
#
88+
def checkconfig():
89+
import os, string
90+
global TRACKER_HOMES, LOG
91+
92+
# see if there's an environment var. ROUNDUP_INSTANCE_HOMES is the
93+
# old name for it.
94+
if os.environ.has_key('ROUNDUP_INSTANCE_HOMES'):
95+
homes = os.environ.get('ROUNDUP_INSTANCE_HOMES')
96+
else:
97+
homes = os.environ.get('TRACKER_HOMES', '')
98+
if homes:
99+
TRACKER_HOMES = {}
100+
for home in string.split(homes, os.pathsep):
101+
try:
102+
name, dir = string.split(home, '=', 1)
103+
except ValueError:
104+
# ignore invalid definitions
105+
continue
106+
if name and dir:
107+
TRACKER_HOMES[name] = dir
108+
109+
logname = os.environ.get('ROUNDUP_LOG', '')
110+
if logname:
111+
LOG = open(logname, 'a')
112+
113+
# ROUNDUP_DEBUG is checked directly in "roundup.cgi.client"
114+
115+
116+
#
117+
# Provide interface to CGI HTTP response handling
118+
#
119+
class RequestWrapper:
120+
'''Used to make the CGI server look like a BaseHTTPRequestHandler
121+
'''
122+
def __init__(self, wfile):
123+
self.wfile = wfile
124+
def write(self, data):
125+
self.wfile.write(data)
126+
def send_response(self, code):
127+
self.write('Status: %s\r\n'%code)
128+
def send_header(self, keyword, value):
129+
self.write("%s: %s\r\n" % (keyword, value))
130+
def end_headers(self):
131+
self.write("\r\n")
132+
133+
#
134+
# Main CGI handler
135+
#
136+
def main(out, err):
137+
import os, string
138+
import roundup.instance
139+
path = string.split(os.environ.get('PATH_INFO', '/'), '/')
140+
request = RequestWrapper(out)
141+
request.path = os.environ.get('PATH_INFO', '/')
142+
tracker = path[1]
143+
os.environ['TRACKER_NAME'] = tracker
144+
os.environ['PATH_INFO'] = string.join(path[2:], '/')
145+
if TRACKER_HOMES.has_key(tracker):
146+
# redirect if we need a trailing '/'
147+
if len(path) == 2:
148+
request.send_response(301)
149+
# redirect
150+
if os.environ.get('HTTPS', '') == 'on':
151+
protocol = 'https'
152+
else:
153+
protocol = 'http'
154+
absolute_url = '%s://%s%s/'%(protocol, os.environ['HTTP_HOST'],
155+
os.environ.get('REQUEST_URI', ''))
156+
request.send_header('Location', absolute_url)
157+
request.end_headers()
158+
out.write('Moved Permanently')
159+
else:
160+
tracker_home = TRACKER_HOMES[tracker]
161+
tracker = roundup.instance.open(tracker_home)
162+
import roundup.cgi.client
163+
if hasattr(tracker, 'Client'):
164+
client = tracker.Client(tracker, request, os.environ)
165+
else:
166+
client = roundup.cgi.client.Client(tracker, request, os.environ)
167+
try:
168+
client.main()
169+
except roundup.cgi.client.Unauthorised:
170+
request.send_response(403)
171+
request.send_header('Content-Type', 'text/html')
172+
request.end_headers()
173+
out.write('Unauthorised')
174+
except roundup.cgi.client.NotFound:
175+
request.send_response(404)
176+
request.send_header('Content-Type', 'text/html')
177+
request.end_headers()
178+
out.write('Not found: %s'%client.path)
179+
180+
else:
181+
import urllib
182+
request.send_response(200)
183+
request.send_header('Content-Type', 'text/html')
184+
request.end_headers()
185+
w = request.write
186+
w(_('<html><head><title>Roundup trackers index</title></head>\n'))
187+
w(_('<body><h1>Roundup trackers index</h1><ol>\n'))
188+
homes = TRACKER_HOMES.keys()
189+
homes.sort()
190+
for tracker in homes:
191+
w(_('<li><a href="%(tracker_url)s/index">%(tracker_name)s</a>\n')%{
192+
'tracker_url': os.environ['SCRIPT_NAME']+'/'+
193+
urllib.quote(tracker),
194+
'tracker_name': cgi.escape(tracker)})
195+
w(_('</ol></body></html>'))
196+
197+
#
198+
# Now do the actual CGI handling
199+
#
200+
out, err = sys.stdout, sys.stderr
201+
try:
202+
# force input/output to binary (important for file up/downloads)
203+
if sys.platform == "win32":
204+
import os, msvcrt
205+
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
206+
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
207+
checkconfig()
208+
sys.stdout = sys.stderr = LOG
209+
main(out, err)
210+
except SystemExit:
211+
pass
212+
except:
213+
sys.stdout, sys.stderr = out, err
214+
out.write('Content-Type: text/html\n\n')
215+
if DEBUG_TO_CLIENT:
216+
cgitb.handler()
217+
else:
218+
out.write(cgitb.breaker())
219+
ts = time.ctime()
220+
out.write('''<p>%s: An error occurred. Please check
221+
the server log for more infomation.</p>'''%ts)
222+
print >> sys.stderr, 'EXCEPTION AT', ts
223+
traceback.print_exc(0, sys.stderr)
224+
225+
sys.stdout.flush()
226+
sys.stdout, sys.stderr = out, err
227+
LOG.close()
228+
229+
# vim: set filetype=python ts=4 sw=4 et si

roundup/cgi/apache.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
# instead of mod_python FieldStorage
2121
# 29-apr-2004 [als] created
2222

23-
__version__ = "$Revision: 1.5 $"[11:-2]
24-
__date__ = "$Date: 2006-11-03 05:43:00 $"[7:-2]
23+
__version__ = "$Revision: 1.6 $"[11:-2]
24+
__date__ = "$Date: 2006-11-09 00:36:21 $"[7:-2]
2525

2626
import cgi
2727
import os
@@ -52,6 +52,12 @@ def __init__(self, request):
5252
# .wfile.write()
5353
self.wfile = self._req
5454

55+
def start_response(self, headers, response):
56+
self.send_response(response)
57+
for key, value in headers:
58+
self.send_header(key, value)
59+
self.end_headers()
60+
5561
def send_response(self, response_code):
5662
"""Set HTTP response code"""
5763
self._req.status = response_code

0 commit comments

Comments
 (0)