OpenSecurity/install/web.py-0.37/web/http.py
author om
Mon, 02 Dec 2013 14:02:05 +0100
changeset 3 65432e6c6042
permissions -rwxr-xr-x
initial deployment and project layout commit
     1 """
     2 HTTP Utilities
     3 (from web.py)
     4 """
     5 
     6 __all__ = [
     7   "expires", "lastmodified", 
     8   "prefixurl", "modified", 
     9   "changequery", "url",
    10   "profiler",
    11 ]
    12 
    13 import sys, os, threading, urllib, urlparse
    14 try: import datetime
    15 except ImportError: pass
    16 import net, utils, webapi as web
    17 
    18 def prefixurl(base=''):
    19     """
    20     Sorry, this function is really difficult to explain.
    21     Maybe some other time.
    22     """
    23     url = web.ctx.path.lstrip('/')
    24     for i in xrange(url.count('/')): 
    25         base += '../'
    26     if not base: 
    27         base = './'
    28     return base
    29 
    30 def expires(delta):
    31     """
    32     Outputs an `Expires` header for `delta` from now. 
    33     `delta` is a `timedelta` object or a number of seconds.
    34     """
    35     if isinstance(delta, (int, long)):
    36         delta = datetime.timedelta(seconds=delta)
    37     date_obj = datetime.datetime.utcnow() + delta
    38     web.header('Expires', net.httpdate(date_obj))
    39 
    40 def lastmodified(date_obj):
    41     """Outputs a `Last-Modified` header for `datetime`."""
    42     web.header('Last-Modified', net.httpdate(date_obj))
    43 
    44 def modified(date=None, etag=None):
    45     """
    46     Checks to see if the page has been modified since the version in the
    47     requester's cache.
    48     
    49     When you publish pages, you can include `Last-Modified` and `ETag`
    50     with the date the page was last modified and an opaque token for
    51     the particular version, respectively. When readers reload the page, 
    52     the browser sends along the modification date and etag value for
    53     the version it has in its cache. If the page hasn't changed, 
    54     the server can just return `304 Not Modified` and not have to 
    55     send the whole page again.
    56     
    57     This function takes the last-modified date `date` and the ETag `etag`
    58     and checks the headers to see if they match. If they do, it returns 
    59     `True`, or otherwise it raises NotModified error. It also sets 
    60     `Last-Modified` and `ETag` output headers.
    61     """
    62     try:
    63         from __builtin__ import set
    64     except ImportError:
    65         # for python 2.3
    66         from sets import Set as set
    67 
    68     n = set([x.strip('" ') for x in web.ctx.env.get('HTTP_IF_NONE_MATCH', '').split(',')])
    69     m = net.parsehttpdate(web.ctx.env.get('HTTP_IF_MODIFIED_SINCE', '').split(';')[0])
    70     validate = False
    71     if etag:
    72         if '*' in n or etag in n:
    73             validate = True
    74     if date and m:
    75         # we subtract a second because 
    76         # HTTP dates don't have sub-second precision
    77         if date-datetime.timedelta(seconds=1) <= m:
    78             validate = True
    79     
    80     if date: lastmodified(date)
    81     if etag: web.header('ETag', '"' + etag + '"')
    82     if validate:
    83         raise web.notmodified()
    84     else:
    85         return True
    86 
    87 def urlencode(query, doseq=0):
    88     """
    89     Same as urllib.urlencode, but supports unicode strings.
    90     
    91         >>> urlencode({'text':'foo bar'})
    92         'text=foo+bar'
    93         >>> urlencode({'x': [1, 2]}, doseq=True)
    94         'x=1&x=2'
    95     """
    96     def convert(value, doseq=False):
    97         if doseq and isinstance(value, list):
    98             return [convert(v) for v in value]
    99         else:
   100             return utils.safestr(value)
   101         
   102     query = dict([(k, convert(v, doseq)) for k, v in query.items()])
   103     return urllib.urlencode(query, doseq=doseq)
   104 
   105 def changequery(query=None, **kw):
   106     """
   107     Imagine you're at `/foo?a=1&b=2`. Then `changequery(a=3)` will return
   108     `/foo?a=3&b=2` -- the same URL but with the arguments you requested
   109     changed.
   110     """
   111     if query is None:
   112         query = web.rawinput(method='get')
   113     for k, v in kw.iteritems():
   114         if v is None:
   115             query.pop(k, None)
   116         else:
   117             query[k] = v
   118     out = web.ctx.path
   119     if query:
   120         out += '?' + urlencode(query, doseq=True)
   121     return out
   122 
   123 def url(path=None, doseq=False, **kw):
   124     """
   125     Makes url by concatenating web.ctx.homepath and path and the 
   126     query string created using the arguments.
   127     """
   128     if path is None:
   129         path = web.ctx.path
   130     if path.startswith("/"):
   131         out = web.ctx.homepath + path
   132     else:
   133         out = path
   134 
   135     if kw:
   136         out += '?' + urlencode(kw, doseq=doseq)
   137     
   138     return out
   139 
   140 def profiler(app):
   141     """Outputs basic profiling information at the bottom of each response."""
   142     from utils import profile
   143     def profile_internal(e, o):
   144         out, result = profile(app)(e, o)
   145         return list(out) + ['<pre>' + net.websafe(result) + '</pre>']
   146     return profile_internal
   147 
   148 if __name__ == "__main__":
   149     import doctest
   150     doctest.testmod()