root/software/scgiserver/trunk/scgiserver.py

Revision 8, 5.2 kB (checked in by t, 12 years ago)

fixed layout x 2

Line 
1 #! /usr/bin/env python
2 """
3 SCGI-->WSGI application proxy, "SWAP".
4
5 (Originally written by Titus Brown.)
6
7 This lets an SCGI front-end like mod_scgi be used to execute WSGI
8 application objects.  To use it, subclass the SWAP class like so:
9
10
11    class TestAppHandler(swap.SWAP):
12        def __init__(self, *args, **kwargs):
13            self.prefix = '/canal'
14            self.app_obj = TestAppClass
15            swap.SWAP.__init__(self, *args, **kwargs)
16
17 where 'TestAppClass' is the application object from WSGI and '/canal'
18 is the prefix for what is served by the SCGI Web-server-side process.
19
20 Then execute the SCGI handler "as usual" by doing something like this
21
22    scgi_server.SCGIServer(TestAppHandler, port=4000).serve()
23
24 and point mod_scgi (or whatever your SCGI front end is) at port 4000.
25
26 Kudos to the WSGI folk for writing a nice PEP & the Quixote folk for
27 writing a nice extensible SCGI server for Python!
28 """
29
30 import sys
31 import time
32 import os
33 from scgi import scgi_server
34
35 def debug(msg):
36     timestamp = time.strftime("%Y-%m-%d %H:%M:%S",
37                               time.localtime(time.time()))
38     sys.stderr.write("[%s] %s\n" % (timestamp, msg))
39
40 class SWAP(scgi_server.SCGIHandler):
41     """
42     SCGI->WSGI application proxy: let an SCGI server execute WSGI
43     application objects.
44     """
45     app_obj = None
46     prefix = None
47    
48     def __init__(self, *args, **kwargs):
49         assert self.app_obj, "must set app_obj"
50         if self.prefix is None:
51             raise Exception("must set prefix")
52        
53         args = (self,) + args
54         scgi_server.SCGIHandler.__init__(*args, **kwargs)
55
56     def handle_connection(self, conn):
57         """
58         Handle an individual connection.
59         """
60         input = conn.makefile("r")
61         output = conn.makefile("w")
62
63         environ = self.read_env(input)
64         environ['wsgi.input']        = input
65         environ['wsgi.errors']       = sys.stderr
66         environ['wsgi.version']      = (1,0)
67         environ['wsgi.multithread']  = False
68         environ['wsgi.multiprocess'] = True
69         environ['wsgi.run_once']    = False
70
71         # dunno how SCGI does HTTPS signalling; can't test it myself... @CTB
72         if environ.get('HTTPS','off') in ('on','1'):
73             environ['wsgi.url_scheme'] = 'https'
74         else:
75             environ['wsgi.url_scheme'] = 'http'
76
77         ## SCGI does some weird environ manglement.  We need to set
78         ## SCRIPT_NAME from 'prefix' and then set PATH_INFO from
79         ## REQUEST_URI.
80
81         prefix = self.prefix
82         request_uri = environ['REQUEST_URI']
83         if request_uri.startswith(prefix):
84             path = request_uri[len(prefix):]
85             environ['SCRIPT_NAME'] = prefix
86         else:
87             sys.stderr.write('WARNING: URL does not start with prefix "%s"\n' % (prefix,))
88             path = request_uri
89             environ['SCRIPT_NAME'] = ''
90
91         if '?' in path:
92             (path, query_string) = path.split('?', 1)
93             environ['QUERY_STRING'] = query_string
94
95         environ['PATH_INFO'] = path
96
97         headers_set = []
98         headers_sent = []
99         chunks = []
100         def write(data):
101             chunks.append(data)
102        
103         def start_response(status,response_headers,exc_info=None):
104             if exc_info:
105                 try:
106                     if headers_sent:
107                         # Re-raise original exception if headers sent
108                         raise exc_info[0], exc_info[1], exc_info[2]
109                 finally:
110                     exc_info = None     # avoid dangling circular ref
111             elif headers_set:
112                 raise AssertionError("Headers already set!")
113
114             headers_set[:] = [status,response_headers]
115             return write
116
117         ###
118
119         result = self.app_obj(environ, start_response)
120         try:
121             for data in result:
122                 chunks.append(data)
123                
124             # Before the first output, send the stored headers
125             if not headers_set:
126                 # Error -- the app never called start_response
127                 status = '500 Server Error'
128                 response_headers = [('Content-type', 'text/html')]
129                 chunks = ["XXX start_response never called"]
130             else:
131                 status, response_headers = headers_sent[:] = headers_set
132                
133             output.write('Status: %s\r\n' % status)
134             for header in response_headers:
135                 output.write('%s: %s\r\n' % header)
136             output.write('\r\n')
137
138             for data in chunks:
139                 output.write(data)
140         finally:
141             if hasattr(result,'close'):
142                 result.close()
143
144         # SCGI backends use connection closing to signal 'fini'.
145         try:
146             input.close()
147             output.close()
148             conn.close()
149         except IOError, err:
150             debug("IOError while closing connection ignored: %s" % err)
151
152
153 def serve_application (application, prefix, port):
154     class SCGIAppHandler(SWAP):
155         def __init__ (self, *args, **kwargs):
156             self.prefix = prefix
157             self.app_obj = application
158             SWAP.__init__(self, *args, **kwargs)
159
160     try:
161         scgi_server.SCGIServer(SCGIAppHandler, port=port).serve()
162     except KeyboardInterrupt:
163         pass                            # exit silently
Note: See TracBrowser for help on using the browser.