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