comparison examples/trac/trac/web/wsgi.py @ 39:93b4dcbafd7b trunk

Copy Trac to main branch.
author cmlenz
date Mon, 03 Jul 2006 18:53:27 +0000
parents
children
comparison
equal deleted inserted replaced
38:ee669cb9cccc 39:93b4dcbafd7b
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2005 Edgewall Software
4 # Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
5 # All rights reserved.
6 #
7 # This software is licensed as described in the file COPYING, which
8 # you should have received as part of this distribution. The terms
9 # are also available at http://trac.edgewall.com/license.html.
10 #
11 # This software consists of voluntary contributions made by many
12 # individuals. For the exact contribution history, see the revision
13 # history and logs, available at http://projects.edgewall.com/trac/.
14 #
15 # Author: Christopher Lenz <cmlenz@gmx.de>
16
17 import sys
18 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
19 from SocketServer import ForkingMixIn, ThreadingMixIn
20 import urllib
21
22
23 class _ErrorsWrapper(object):
24
25 def __init__(self, logfunc):
26 self.logfunc = logfunc
27
28 def flush(self):
29 pass
30
31 def write(self, msg):
32 self.logfunc(msg)
33
34 def writelines(self, seq):
35 map(self.write, seq)
36
37
38 class _FileWrapper(object):
39 """Wrapper for sending a file as response."""
40
41 def __init__(self, fileobj, blocksize=None):
42 self.fileobj = fileobj
43 self.blocksize = blocksize
44 self.read = self.fileobj.read
45 if hasattr(fileobj, 'close'):
46 self.close = fileobj.close
47
48 def __iter__(self):
49 return self
50
51 def next(self):
52 data = self.fileobj.read(self.blocksize)
53 if not data:
54 raise StopIteration
55 return data
56
57
58 class WSGIGateway(object):
59 """Abstract base class for WSGI servers or gateways."""
60
61 wsgi_version = (1, 0)
62 wsgi_multithread = True
63 wsgi_multiprocess = True
64 wsgi_run_once = False
65 wsgi_file_wrapper = _FileWrapper
66
67 def __init__(self, environ, stdin=sys.stdin, stderr=sys.stderr):
68 """Initialize the gateway object."""
69 environ['wsgi.version'] = self.wsgi_version
70 environ['wsgi.url_scheme'] = 'http'
71 if environ.get('HTTPS', '').lower() in ('yes', 'on', '1'):
72 environ['wsgi.url_scheme'] = 'https'
73 environ['wsgi.input'] = stdin
74 environ['wsgi.errors'] = stderr
75 environ['wsgi.multithread'] = self.wsgi_multithread
76 environ['wsgi.multiprocess'] = self.wsgi_multiprocess
77 environ['wsgi.run_once'] = self.wsgi_run_once
78 if self.wsgi_file_wrapper is not None:
79 environ['wsgi.file_wrapper'] = self.wsgi_file_wrapper
80 self.environ = environ
81
82 self.headers_set = []
83 self.headers_sent = []
84
85 def run(self, application):
86 """Start the gateway with the given WSGI application."""
87 response = application(self.environ, self._start_response)
88 try:
89 if isinstance(response, self.wsgi_file_wrapper) \
90 and hasattr(self, '_sendfile'):
91 self._sendfile(response.fileobj)
92 else:
93 for chunk in response:
94 if chunk:
95 self._write(chunk)
96 if not self.headers_sent:
97 self._write('')
98 finally:
99 if hasattr(response, 'close'):
100 response.close()
101
102 def _start_response(self, status, headers, exc_info=None):
103 """Callback for starting a HTTP response."""
104 if exc_info:
105 try:
106 if self.headers_sent: # Re-raise original exception
107 raise exc_info[0], exc_info[1], exc_info[2]
108 finally:
109 exc_info = None # avoid dangling circular ref
110 else:
111 assert not self.headers_set, 'Response already started'
112
113 self.headers_set = [status, headers]
114 return self._write
115
116 def _write(self, data):
117 """Callback for writing data to the response.
118
119 Concrete subclasses must implement this method."""
120 raise NotImplementedError
121
122
123 class WSGIRequestHandler(BaseHTTPRequestHandler):
124
125 def setup_environ(self):
126 self.raw_requestline = self.rfile.readline()
127 if not self.parse_request(): # An error code has been sent, just exit
128 self.close_connection = 1
129 return
130
131 environ = self.server.environ.copy()
132 environ['SERVER_PROTOCOL'] = self.request_version
133 environ['REQUEST_METHOD'] = self.command
134
135 if '?' in self.path:
136 path_info, query_string = self.path.split('?', 1)
137 else:
138 path_info, query_string = self.path, ''
139 environ['PATH_INFO'] = urllib.unquote(path_info)
140 environ['QUERY_STRING'] = query_string
141
142 host = self.address_string()
143 if host != self.client_address[0]:
144 environ['REMOTE_HOST'] = host
145 environ['REMOTE_ADDR'] = self.client_address[0]
146
147 if self.headers.typeheader is None:
148 environ['CONTENT_TYPE'] = self.headers.type
149 else:
150 environ['CONTENT_TYPE'] = self.headers.typeheader
151
152 length = self.headers.getheader('content-length')
153 if length:
154 environ['CONTENT_LENGTH'] = length
155
156 for name, value in [header.split(':', 1) for header
157 in self.headers.headers]:
158 name = name.replace('-', '_').upper();
159 value = value.strip()
160 if name in environ:
161 # skip content length, type, etc.
162 continue
163 if 'HTTP_' + name in environ:
164 # comma-separate multiple headers
165 environ['HTTP_' + name] += ',' + value
166 else:
167 environ['HTTP_' + name] = value
168
169 return environ
170
171 def handle_one_request(self):
172 environ = self.setup_environ()
173 gateway = self.server.gateway(self, environ)
174 gateway.run(self.server.application)
175
176 def finish(self):
177 """We need to help the garbage collector a little."""
178 BaseHTTPRequestHandler.finish(self)
179 self.wfile = None
180 self.rfile = None
181
182
183 class WSGIServerGateway(WSGIGateway):
184
185 def __init__(self, handler, environ):
186 WSGIGateway.__init__(self, environ, handler.rfile,
187 _ErrorsWrapper(lambda x: handler.log_error('%s', x)))
188 self.handler = handler
189
190 def _write(self, data):
191 assert self.headers_set, 'Response not started'
192
193 if not self.headers_sent:
194 status, headers = self.headers_sent = self.headers_set
195 self.handler.send_response(int(status[:3]))
196 for name, value in headers:
197 self.handler.send_header(name, value)
198 self.handler.end_headers()
199 self.handler.wfile.write(data)
200
201
202 class WSGIServer(HTTPServer):
203
204 def __init__(self, server_address, application, gateway=WSGIServerGateway,
205 request_handler=WSGIRequestHandler):
206 HTTPServer.__init__(self, server_address, request_handler)
207
208 self.application = application
209
210 gateway.wsgi_multithread = isinstance(self, ThreadingMixIn)
211 gateway.wsgi_multiprocess = isinstance(self, ForkingMixIn)
212 self.gateway = gateway
213
214 self.environ = {'SERVER_NAME': self.server_name,
215 'SERVER_PORT': str(self.server_port),
216 'SCRIPT_NAME': ''}
Copyright (C) 2012-2017 Edgewall Software