symbian-qemu-0.9.1-12/python-2.6.1/Lib/wsgiref/handlers.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """Base classes for server/gateway implementations"""
       
     2 
       
     3 from types import StringType
       
     4 from util import FileWrapper, guess_scheme, is_hop_by_hop
       
     5 from headers import Headers
       
     6 
       
     7 import sys, os, time
       
     8 
       
     9 __all__ = ['BaseHandler', 'SimpleHandler', 'BaseCGIHandler', 'CGIHandler']
       
    10 
       
    11 try:
       
    12     dict
       
    13 except NameError:
       
    14     def dict(items):
       
    15         d = {}
       
    16         for k,v in items:
       
    17             d[k] = v
       
    18         return d
       
    19 
       
    20 # Uncomment for 2.2 compatibility.
       
    21 #try:
       
    22 #    True
       
    23 #    False
       
    24 #except NameError:
       
    25 #    True = not None
       
    26 #    False = not True
       
    27 
       
    28 
       
    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"]
       
    34 
       
    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
       
    39     )
       
    40 
       
    41 
       
    42 
       
    43 class BaseHandler:
       
    44     """Manage the invocation of a WSGI application"""
       
    45 
       
    46     # Configuration parameters; can override per-subclass or per-instance
       
    47     wsgi_version = (1,0)
       
    48     wsgi_multithread = True
       
    49     wsgi_multiprocess = True
       
    50     wsgi_run_once = False
       
    51 
       
    52     origin_server = True    # We are transmitting direct to client
       
    53     http_version  = "1.0"   # Version that should be used for response
       
    54     server_software = None  # String name of server software, if any
       
    55 
       
    56     # os_environ is used to supply configuration from the OS environment:
       
    57     # by default it's a copy of 'os.environ' as of import time, but you can
       
    58     # override this in e.g. your __init__ method.
       
    59     os_environ = dict(os.environ.items())
       
    60 
       
    61     # Collaborator classes
       
    62     wsgi_file_wrapper = FileWrapper     # set to None to disable
       
    63     headers_class = Headers             # must be a Headers-like class
       
    64 
       
    65     # Error handling (also per-subclass or per-instance)
       
    66     traceback_limit = None  # Print entire traceback to self.get_stderr()
       
    67     error_status = "500 Dude, this is whack!"
       
    68     error_headers = [('Content-Type','text/plain')]
       
    69     error_body = "A server error occurred.  Please contact the administrator."
       
    70 
       
    71     # State variables (don't mess with these)
       
    72     status = result = None
       
    73     headers_sent = False
       
    74     headers = None
       
    75     bytes_sent = 0
       
    76 
       
    77 
       
    78 
       
    79 
       
    80 
       
    81 
       
    82 
       
    83 
       
    84     def run(self, application):
       
    85         """Invoke the application"""
       
    86         # Note to self: don't move the close()!  Asynchronous servers shouldn't
       
    87         # call close() from finish_response(), so if you close() anywhere but
       
    88         # the double-error branch here, you'll break asynchronous servers by
       
    89         # prematurely closing.  Async servers must return from 'run()' without
       
    90         # closing if there might still be output to iterate over.
       
    91         try:
       
    92             self.setup_environ()
       
    93             self.result = application(self.environ, self.start_response)
       
    94             self.finish_response()
       
    95         except:
       
    96             try:
       
    97                 self.handle_error()
       
    98             except:
       
    99                 # If we get an error handling an error, just give up already!
       
   100                 self.close()
       
   101                 raise   # ...and let the actual server figure it out.
       
   102 
       
   103 
       
   104     def setup_environ(self):
       
   105         """Set up the environment for one request"""
       
   106 
       
   107         env = self.environ = self.os_environ.copy()
       
   108         self.add_cgi_vars()
       
   109 
       
   110         env['wsgi.input']        = self.get_stdin()
       
   111         env['wsgi.errors']       = self.get_stderr()
       
   112         env['wsgi.version']      = self.wsgi_version
       
   113         env['wsgi.run_once']     = self.wsgi_run_once
       
   114         env['wsgi.url_scheme']   = self.get_scheme()
       
   115         env['wsgi.multithread']  = self.wsgi_multithread
       
   116         env['wsgi.multiprocess'] = self.wsgi_multiprocess
       
   117 
       
   118         if self.wsgi_file_wrapper is not None:
       
   119             env['wsgi.file_wrapper'] = self.wsgi_file_wrapper
       
   120 
       
   121         if self.origin_server and self.server_software:
       
   122             env.setdefault('SERVER_SOFTWARE',self.server_software)
       
   123 
       
   124 
       
   125     def finish_response(self):
       
   126         """Send any iterable data, then close self and the iterable
       
   127 
       
   128         Subclasses intended for use in asynchronous servers will
       
   129         want to redefine this method, such that it sets up callbacks
       
   130         in the event loop to iterate over the data, and to call
       
   131         'self.close()' once the response is finished.
       
   132         """
       
   133         if not self.result_is_file() or not self.sendfile():
       
   134             for data in self.result:
       
   135                 self.write(data)
       
   136             self.finish_content()
       
   137         self.close()
       
   138 
       
   139 
       
   140     def get_scheme(self):
       
   141         """Return the URL scheme being used"""
       
   142         return guess_scheme(self.environ)
       
   143 
       
   144 
       
   145     def set_content_length(self):
       
   146         """Compute Content-Length or switch to chunked encoding if possible"""
       
   147         try:
       
   148             blocks = len(self.result)
       
   149         except (TypeError,AttributeError,NotImplementedError):
       
   150             pass
       
   151         else:
       
   152             if blocks==1:
       
   153                 self.headers['Content-Length'] = str(self.bytes_sent)
       
   154                 return
       
   155         # XXX Try for chunked encoding if origin server and client is 1.1
       
   156 
       
   157 
       
   158     def cleanup_headers(self):
       
   159         """Make any necessary header changes or defaults
       
   160 
       
   161         Subclasses can extend this to add other defaults.
       
   162         """
       
   163         if not self.headers.has_key('Content-Length'):
       
   164             self.set_content_length()
       
   165 
       
   166     def start_response(self, status, headers,exc_info=None):
       
   167         """'start_response()' callable as specified by PEP 333"""
       
   168 
       
   169         if exc_info:
       
   170             try:
       
   171                 if self.headers_sent:
       
   172                     # Re-raise original exception if headers sent
       
   173                     raise exc_info[0], exc_info[1], exc_info[2]
       
   174             finally:
       
   175                 exc_info = None        # avoid dangling circular ref
       
   176         elif self.headers is not None:
       
   177             raise AssertionError("Headers already set!")
       
   178 
       
   179         assert type(status) is StringType,"Status must be a string"
       
   180         assert len(status)>=4,"Status must be at least 4 characters"
       
   181         assert int(status[:3]),"Status message must begin w/3-digit code"
       
   182         assert status[3]==" ", "Status message must have a space after code"
       
   183         if __debug__:
       
   184             for name,val in headers:
       
   185                 assert type(name) is StringType,"Header names must be strings"
       
   186                 assert type(val) is StringType,"Header values must be strings"
       
   187                 assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
       
   188         self.status = status
       
   189         self.headers = self.headers_class(headers)
       
   190         return self.write
       
   191 
       
   192 
       
   193     def send_preamble(self):
       
   194         """Transmit version/status/date/server, via self._write()"""
       
   195         if self.origin_server:
       
   196             if self.client_is_modern():
       
   197                 self._write('HTTP/%s %s\r\n' % (self.http_version,self.status))
       
   198                 if not self.headers.has_key('Date'):
       
   199                     self._write(
       
   200                         'Date: %s\r\n' % format_date_time(time.time())
       
   201                     )
       
   202                 if self.server_software and not self.headers.has_key('Server'):
       
   203                     self._write('Server: %s\r\n' % self.server_software)
       
   204         else:
       
   205             self._write('Status: %s\r\n' % self.status)
       
   206 
       
   207     def write(self, data):
       
   208         """'write()' callable as specified by PEP 333"""
       
   209 
       
   210         assert type(data) is StringType,"write() argument must be string"
       
   211 
       
   212         if not self.status:
       
   213             raise AssertionError("write() before start_response()")
       
   214 
       
   215         elif not self.headers_sent:
       
   216             # Before the first output, send the stored headers
       
   217             self.bytes_sent = len(data)    # make sure we know content-length
       
   218             self.send_headers()
       
   219         else:
       
   220             self.bytes_sent += len(data)
       
   221 
       
   222         # XXX check Content-Length and truncate if too many bytes written?
       
   223         self._write(data)
       
   224         self._flush()
       
   225 
       
   226 
       
   227     def sendfile(self):
       
   228         """Platform-specific file transmission
       
   229 
       
   230         Override this method in subclasses to support platform-specific
       
   231         file transmission.  It is only called if the application's
       
   232         return iterable ('self.result') is an instance of
       
   233         'self.wsgi_file_wrapper'.
       
   234 
       
   235         This method should return a true value if it was able to actually
       
   236         transmit the wrapped file-like object using a platform-specific
       
   237         approach.  It should return a false value if normal iteration
       
   238         should be used instead.  An exception can be raised to indicate
       
   239         that transmission was attempted, but failed.
       
   240 
       
   241         NOTE: this method should call 'self.send_headers()' if
       
   242         'self.headers_sent' is false and it is going to attempt direct
       
   243         transmission of the file.
       
   244         """
       
   245         return False   # No platform-specific transmission by default
       
   246 
       
   247 
       
   248     def finish_content(self):
       
   249         """Ensure headers and content have both been sent"""
       
   250         if not self.headers_sent:
       
   251             self.headers['Content-Length'] = "0"
       
   252             self.send_headers()
       
   253         else:
       
   254             pass # XXX check if content-length was too short?
       
   255 
       
   256     def close(self):
       
   257         """Close the iterable (if needed) and reset all instance vars
       
   258 
       
   259         Subclasses may want to also drop the client connection.
       
   260         """
       
   261         try:
       
   262             if hasattr(self.result,'close'):
       
   263                 self.result.close()
       
   264         finally:
       
   265             self.result = self.headers = self.status = self.environ = None
       
   266             self.bytes_sent = 0; self.headers_sent = False
       
   267 
       
   268 
       
   269     def send_headers(self):
       
   270         """Transmit headers to the client, via self._write()"""
       
   271         self.cleanup_headers()
       
   272         self.headers_sent = True
       
   273         if not self.origin_server or self.client_is_modern():
       
   274             self.send_preamble()
       
   275             self._write(str(self.headers))
       
   276 
       
   277 
       
   278     def result_is_file(self):
       
   279         """True if 'self.result' is an instance of 'self.wsgi_file_wrapper'"""
       
   280         wrapper = self.wsgi_file_wrapper
       
   281         return wrapper is not None and isinstance(self.result,wrapper)
       
   282 
       
   283 
       
   284     def client_is_modern(self):
       
   285         """True if client can accept status and headers"""
       
   286         return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
       
   287 
       
   288 
       
   289     def log_exception(self,exc_info):
       
   290         """Log the 'exc_info' tuple in the server log
       
   291 
       
   292         Subclasses may override to retarget the output or change its format.
       
   293         """
       
   294         try:
       
   295             from traceback import print_exception
       
   296             stderr = self.get_stderr()
       
   297             print_exception(
       
   298                 exc_info[0], exc_info[1], exc_info[2],
       
   299                 self.traceback_limit, stderr
       
   300             )
       
   301             stderr.flush()
       
   302         finally:
       
   303             exc_info = None
       
   304 
       
   305     def handle_error(self):
       
   306         """Log current error, and send error output to client if possible"""
       
   307         self.log_exception(sys.exc_info())
       
   308         if not self.headers_sent:
       
   309             self.result = self.error_output(self.environ, self.start_response)
       
   310             self.finish_response()
       
   311         # XXX else: attempt advanced recovery techniques for HTML or text?
       
   312 
       
   313     def error_output(self, environ, start_response):
       
   314         """WSGI mini-app to create error output
       
   315 
       
   316         By default, this just uses the 'error_status', 'error_headers',
       
   317         and 'error_body' attributes to generate an output page.  It can
       
   318         be overridden in a subclass to dynamically generate diagnostics,
       
   319         choose an appropriate message for the user's preferred language, etc.
       
   320 
       
   321         Note, however, that it's not recommended from a security perspective to
       
   322         spit out diagnostics to any old user; ideally, you should have to do
       
   323         something special to enable diagnostic output, which is why we don't
       
   324         include any here!
       
   325         """
       
   326         start_response(self.error_status,self.error_headers[:],sys.exc_info())
       
   327         return [self.error_body]
       
   328 
       
   329 
       
   330     # Pure abstract methods; *must* be overridden in subclasses
       
   331 
       
   332     def _write(self,data):
       
   333         """Override in subclass to buffer data for send to client
       
   334 
       
   335         It's okay if this method actually transmits the data; BaseHandler
       
   336         just separates write and flush operations for greater efficiency
       
   337         when the underlying system actually has such a distinction.
       
   338         """
       
   339         raise NotImplementedError
       
   340 
       
   341     def _flush(self):
       
   342         """Override in subclass to force sending of recent '_write()' calls
       
   343 
       
   344         It's okay if this method is a no-op (i.e., if '_write()' actually
       
   345         sends the data.
       
   346         """
       
   347         raise NotImplementedError
       
   348 
       
   349     def get_stdin(self):
       
   350         """Override in subclass to return suitable 'wsgi.input'"""
       
   351         raise NotImplementedError
       
   352 
       
   353     def get_stderr(self):
       
   354         """Override in subclass to return suitable 'wsgi.errors'"""
       
   355         raise NotImplementedError
       
   356 
       
   357     def add_cgi_vars(self):
       
   358         """Override in subclass to insert CGI variables in 'self.environ'"""
       
   359         raise NotImplementedError
       
   360 
       
   361 
       
   362 
       
   363 
       
   364 
       
   365 
       
   366 
       
   367 
       
   368 
       
   369 
       
   370 
       
   371 class SimpleHandler(BaseHandler):
       
   372     """Handler that's just initialized with streams, environment, etc.
       
   373 
       
   374     This handler subclass is intended for synchronous HTTP/1.0 origin servers,
       
   375     and handles sending the entire response output, given the correct inputs.
       
   376 
       
   377     Usage::
       
   378 
       
   379         handler = SimpleHandler(
       
   380             inp,out,err,env, multithread=False, multiprocess=True
       
   381         )
       
   382         handler.run(app)"""
       
   383 
       
   384     def __init__(self,stdin,stdout,stderr,environ,
       
   385         multithread=True, multiprocess=False
       
   386     ):
       
   387         self.stdin = stdin
       
   388         self.stdout = stdout
       
   389         self.stderr = stderr
       
   390         self.base_env = environ
       
   391         self.wsgi_multithread = multithread
       
   392         self.wsgi_multiprocess = multiprocess
       
   393 
       
   394     def get_stdin(self):
       
   395         return self.stdin
       
   396 
       
   397     def get_stderr(self):
       
   398         return self.stderr
       
   399 
       
   400     def add_cgi_vars(self):
       
   401         self.environ.update(self.base_env)
       
   402 
       
   403     def _write(self,data):
       
   404         self.stdout.write(data)
       
   405         self._write = self.stdout.write
       
   406 
       
   407     def _flush(self):
       
   408         self.stdout.flush()
       
   409         self._flush = self.stdout.flush
       
   410 
       
   411 
       
   412 class BaseCGIHandler(SimpleHandler):
       
   413 
       
   414     """CGI-like systems using input/output/error streams and environ mapping
       
   415 
       
   416     Usage::
       
   417 
       
   418         handler = BaseCGIHandler(inp,out,err,env)
       
   419         handler.run(app)
       
   420 
       
   421     This handler class is useful for gateway protocols like ReadyExec and
       
   422     FastCGI, that have usable input/output/error streams and an environment
       
   423     mapping.  It's also the base class for CGIHandler, which just uses
       
   424     sys.stdin, os.environ, and so on.
       
   425 
       
   426     The constructor also takes keyword arguments 'multithread' and
       
   427     'multiprocess' (defaulting to 'True' and 'False' respectively) to control
       
   428     the configuration sent to the application.  It sets 'origin_server' to
       
   429     False (to enable CGI-like output), and assumes that 'wsgi.run_once' is
       
   430     False.
       
   431     """
       
   432 
       
   433     origin_server = False
       
   434 
       
   435 
       
   436 
       
   437 
       
   438 
       
   439 
       
   440 
       
   441 
       
   442 
       
   443 
       
   444 
       
   445 
       
   446 
       
   447 
       
   448 
       
   449 
       
   450 
       
   451 
       
   452 
       
   453 class CGIHandler(BaseCGIHandler):
       
   454 
       
   455     """CGI-based invocation via sys.stdin/stdout/stderr and os.environ
       
   456 
       
   457     Usage::
       
   458 
       
   459         CGIHandler().run(app)
       
   460 
       
   461     The difference between this class and BaseCGIHandler is that it always
       
   462     uses 'wsgi.run_once' of 'True', 'wsgi.multithread' of 'False', and
       
   463     'wsgi.multiprocess' of 'True'.  It does not take any initialization
       
   464     parameters, but always uses 'sys.stdin', 'os.environ', and friends.
       
   465 
       
   466     If you need to override any of these parameters, use BaseCGIHandler
       
   467     instead.
       
   468     """
       
   469 
       
   470     wsgi_run_once = True
       
   471 
       
   472     def __init__(self):
       
   473         BaseCGIHandler.__init__(
       
   474             self, sys.stdin, sys.stdout, sys.stderr, dict(os.environ.items()),
       
   475             multithread=False, multiprocess=True
       
   476         )
       
   477 
       
   478 
       
   479 
       
   480 
       
   481 
       
   482 
       
   483 
       
   484 
       
   485 
       
   486 
       
   487 
       
   488 
       
   489 
       
   490 
       
   491 
       
   492 
       
   493 #