]>
git.proxmox.com Git - mirror_edk2.git/blob - AppPkg/Applications/Python/Python-2.7.2/Lib/wsgiref/handlers.py
1 """Base classes for server/gateway implementations"""
3 from types
import StringType
4 from util
import FileWrapper
, guess_scheme
, is_hop_by_hop
5 from headers
import Headers
9 __all__
= ['BaseHandler', 'SimpleHandler', 'BaseCGIHandler', 'CGIHandler']
20 # Uncomment for 2.2 compatibility.
29 # Weekday and month names for HTTP date/time formatting; always English!
30 _weekdayname
= ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
31 _monthname
= [None, # Dummy so we can use 1-based month numbers
32 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
33 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
35 def format_date_time(timestamp
):
36 year
, month
, day
, hh
, mm
, ss
, wd
, y
, z
= time
.gmtime(timestamp
)
37 return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
38 _weekdayname
[wd
], day
, _monthname
[month
], year
, hh
, mm
, ss
43 """Manage the invocation of a WSGI application"""
45 # Configuration parameters; can override per-subclass or per-instance
47 wsgi_multithread
= True
48 wsgi_multiprocess
= True
51 origin_server
= True # We are transmitting direct to client
52 http_version
= "1.0" # Version that should be used for response
53 server_software
= None # String name of server software, if any
55 # os_environ is used to supply configuration from the OS environment:
56 # by default it's a copy of 'os.environ' as of import time, but you can
57 # override this in e.g. your __init__ method.
58 os_environ
= dict(os
.environ
.items())
60 # Collaborator classes
61 wsgi_file_wrapper
= FileWrapper
# set to None to disable
62 headers_class
= Headers
# must be a Headers-like class
64 # Error handling (also per-subclass or per-instance)
65 traceback_limit
= None # Print entire traceback to self.get_stderr()
66 error_status
= "500 Internal Server Error"
67 error_headers
= [('Content-Type','text/plain')]
68 error_body
= "A server error occurred. Please contact the administrator."
70 # State variables (don't mess with these)
71 status
= result
= None
76 def run(self
, application
):
77 """Invoke the application"""
78 # Note to self: don't move the close()! Asynchronous servers shouldn't
79 # call close() from finish_response(), so if you close() anywhere but
80 # the double-error branch here, you'll break asynchronous servers by
81 # prematurely closing. Async servers must return from 'run()' without
82 # closing if there might still be output to iterate over.
85 self
.result
= application(self
.environ
, self
.start_response
)
86 self
.finish_response()
91 # If we get an error handling an error, just give up already!
93 raise # ...and let the actual server figure it out.
96 def setup_environ(self
):
97 """Set up the environment for one request"""
99 env
= self
.environ
= self
.os_environ
.copy()
102 env
['wsgi.input'] = self
.get_stdin()
103 env
['wsgi.errors'] = self
.get_stderr()
104 env
['wsgi.version'] = self
.wsgi_version
105 env
['wsgi.run_once'] = self
.wsgi_run_once
106 env
['wsgi.url_scheme'] = self
.get_scheme()
107 env
['wsgi.multithread'] = self
.wsgi_multithread
108 env
['wsgi.multiprocess'] = self
.wsgi_multiprocess
110 if self
.wsgi_file_wrapper
is not None:
111 env
['wsgi.file_wrapper'] = self
.wsgi_file_wrapper
113 if self
.origin_server
and self
.server_software
:
114 env
.setdefault('SERVER_SOFTWARE',self
.server_software
)
117 def finish_response(self
):
118 """Send any iterable data, then close self and the iterable
120 Subclasses intended for use in asynchronous servers will
121 want to redefine this method, such that it sets up callbacks
122 in the event loop to iterate over the data, and to call
123 'self.close()' once the response is finished.
125 if not self
.result_is_file() or not self
.sendfile():
126 for data
in self
.result
:
128 self
.finish_content()
132 def get_scheme(self
):
133 """Return the URL scheme being used"""
134 return guess_scheme(self
.environ
)
137 def set_content_length(self
):
138 """Compute Content-Length or switch to chunked encoding if possible"""
140 blocks
= len(self
.result
)
141 except (TypeError,AttributeError,NotImplementedError):
145 self
.headers
['Content-Length'] = str(self
.bytes_sent
)
147 # XXX Try for chunked encoding if origin server and client is 1.1
150 def cleanup_headers(self
):
151 """Make any necessary header changes or defaults
153 Subclasses can extend this to add other defaults.
155 if 'Content-Length' not in self
.headers
:
156 self
.set_content_length()
158 def start_response(self
, status
, headers
,exc_info
=None):
159 """'start_response()' callable as specified by PEP 333"""
163 if self
.headers_sent
:
164 # Re-raise original exception if headers sent
165 raise exc_info
[0], exc_info
[1], exc_info
[2]
167 exc_info
= None # avoid dangling circular ref
168 elif self
.headers
is not None:
169 raise AssertionError("Headers already set!")
171 assert type(status
) is StringType
,"Status must be a string"
172 assert len(status
)>=4,"Status must be at least 4 characters"
173 assert int(status
[:3]),"Status message must begin w/3-digit code"
174 assert status
[3]==" ", "Status message must have a space after code"
176 for name
,val
in headers
:
177 assert type(name
) is StringType
,"Header names must be strings"
178 assert type(val
) is StringType
,"Header values must be strings"
179 assert not is_hop_by_hop(name
),"Hop-by-hop headers not allowed"
181 self
.headers
= self
.headers_class(headers
)
185 def send_preamble(self
):
186 """Transmit version/status/date/server, via self._write()"""
187 if self
.origin_server
:
188 if self
.client_is_modern():
189 self
._write
('HTTP/%s %s\r\n' % (self
.http_version
,self
.status
))
190 if 'Date' not in self
.headers
:
192 'Date: %s\r\n' % format_date_time(time
.time())
194 if self
.server_software
and 'Server' not in self
.headers
:
195 self
._write
('Server: %s\r\n' % self
.server_software
)
197 self
._write
('Status: %s\r\n' % self
.status
)
199 def write(self
, data
):
200 """'write()' callable as specified by PEP 333"""
202 assert type(data
) is StringType
,"write() argument must be string"
205 raise AssertionError("write() before start_response()")
207 elif not self
.headers_sent
:
208 # Before the first output, send the stored headers
209 self
.bytes_sent
= len(data
) # make sure we know content-length
212 self
.bytes_sent
+= len(data
)
214 # XXX check Content-Length and truncate if too many bytes written?
220 """Platform-specific file transmission
222 Override this method in subclasses to support platform-specific
223 file transmission. It is only called if the application's
224 return iterable ('self.result') is an instance of
225 'self.wsgi_file_wrapper'.
227 This method should return a true value if it was able to actually
228 transmit the wrapped file-like object using a platform-specific
229 approach. It should return a false value if normal iteration
230 should be used instead. An exception can be raised to indicate
231 that transmission was attempted, but failed.
233 NOTE: this method should call 'self.send_headers()' if
234 'self.headers_sent' is false and it is going to attempt direct
235 transmission of the file.
237 return False # No platform-specific transmission by default
240 def finish_content(self
):
241 """Ensure headers and content have both been sent"""
242 if not self
.headers_sent
:
243 # Only zero Content-Length if not set by the application (so
244 # that HEAD requests can be satisfied properly, see #3839)
245 self
.headers
.setdefault('Content-Length', "0")
248 pass # XXX check if content-length was too short?
251 """Close the iterable (if needed) and reset all instance vars
253 Subclasses may want to also drop the client connection.
256 if hasattr(self
.result
,'close'):
259 self
.result
= self
.headers
= self
.status
= self
.environ
= None
260 self
.bytes_sent
= 0; self
.headers_sent
= False
263 def send_headers(self
):
264 """Transmit headers to the client, via self._write()"""
265 self
.cleanup_headers()
266 self
.headers_sent
= True
267 if not self
.origin_server
or self
.client_is_modern():
269 self
._write
(str(self
.headers
))
272 def result_is_file(self
):
273 """True if 'self.result' is an instance of 'self.wsgi_file_wrapper'"""
274 wrapper
= self
.wsgi_file_wrapper
275 return wrapper
is not None and isinstance(self
.result
,wrapper
)
278 def client_is_modern(self
):
279 """True if client can accept status and headers"""
280 return self
.environ
['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
283 def log_exception(self
,exc_info
):
284 """Log the 'exc_info' tuple in the server log
286 Subclasses may override to retarget the output or change its format.
289 from traceback
import print_exception
290 stderr
= self
.get_stderr()
292 exc_info
[0], exc_info
[1], exc_info
[2],
293 self
.traceback_limit
, stderr
299 def handle_error(self
):
300 """Log current error, and send error output to client if possible"""
301 self
.log_exception(sys
.exc_info())
302 if not self
.headers_sent
:
303 self
.result
= self
.error_output(self
.environ
, self
.start_response
)
304 self
.finish_response()
305 # XXX else: attempt advanced recovery techniques for HTML or text?
307 def error_output(self
, environ
, start_response
):
308 """WSGI mini-app to create error output
310 By default, this just uses the 'error_status', 'error_headers',
311 and 'error_body' attributes to generate an output page. It can
312 be overridden in a subclass to dynamically generate diagnostics,
313 choose an appropriate message for the user's preferred language, etc.
315 Note, however, that it's not recommended from a security perspective to
316 spit out diagnostics to any old user; ideally, you should have to do
317 something special to enable diagnostic output, which is why we don't
320 start_response(self
.error_status
,self
.error_headers
[:],sys
.exc_info())
321 return [self
.error_body
]
324 # Pure abstract methods; *must* be overridden in subclasses
326 def _write(self
,data
):
327 """Override in subclass to buffer data for send to client
329 It's okay if this method actually transmits the data; BaseHandler
330 just separates write and flush operations for greater efficiency
331 when the underlying system actually has such a distinction.
333 raise NotImplementedError
336 """Override in subclass to force sending of recent '_write()' calls
338 It's okay if this method is a no-op (i.e., if '_write()' actually
341 raise NotImplementedError
344 """Override in subclass to return suitable 'wsgi.input'"""
345 raise NotImplementedError
347 def get_stderr(self
):
348 """Override in subclass to return suitable 'wsgi.errors'"""
349 raise NotImplementedError
351 def add_cgi_vars(self
):
352 """Override in subclass to insert CGI variables in 'self.environ'"""
353 raise NotImplementedError
356 class SimpleHandler(BaseHandler
):
357 """Handler that's just initialized with streams, environment, etc.
359 This handler subclass is intended for synchronous HTTP/1.0 origin servers,
360 and handles sending the entire response output, given the correct inputs.
364 handler = SimpleHandler(
365 inp,out,err,env, multithread=False, multiprocess=True
369 def __init__(self
,stdin
,stdout
,stderr
,environ
,
370 multithread
=True, multiprocess
=False
375 self
.base_env
= environ
376 self
.wsgi_multithread
= multithread
377 self
.wsgi_multiprocess
= multiprocess
382 def get_stderr(self
):
385 def add_cgi_vars(self
):
386 self
.environ
.update(self
.base_env
)
388 def _write(self
,data
):
389 self
.stdout
.write(data
)
390 self
._write
= self
.stdout
.write
394 self
._flush
= self
.stdout
.flush
397 class BaseCGIHandler(SimpleHandler
):
399 """CGI-like systems using input/output/error streams and environ mapping
403 handler = BaseCGIHandler(inp,out,err,env)
406 This handler class is useful for gateway protocols like ReadyExec and
407 FastCGI, that have usable input/output/error streams and an environment
408 mapping. It's also the base class for CGIHandler, which just uses
409 sys.stdin, os.environ, and so on.
411 The constructor also takes keyword arguments 'multithread' and
412 'multiprocess' (defaulting to 'True' and 'False' respectively) to control
413 the configuration sent to the application. It sets 'origin_server' to
414 False (to enable CGI-like output), and assumes that 'wsgi.run_once' is
418 origin_server
= False
421 class CGIHandler(BaseCGIHandler
):
423 """CGI-based invocation via sys.stdin/stdout/stderr and os.environ
427 CGIHandler().run(app)
429 The difference between this class and BaseCGIHandler is that it always
430 uses 'wsgi.run_once' of 'True', 'wsgi.multithread' of 'False', and
431 'wsgi.multiprocess' of 'True'. It does not take any initialization
432 parameters, but always uses 'sys.stdin', 'os.environ', and friends.
434 If you need to override any of these parameters, use BaseCGIHandler
439 # Do not allow os.environ to leak between requests in Google App Engine
440 # and other multi-run CGI use cases. This is not easily testable.
441 # See http://bugs.python.org/issue7250
445 BaseCGIHandler
.__init
__(
446 self
, sys
.stdin
, sys
.stdout
, sys
.stderr
, dict(os
.environ
.items()),
447 multithread
=False, multiprocess
=True