Package gluon :: Module http
[hide private]
[frames] | no frames]

Source Code for Module gluon.http

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  """ 
  5  | This file is part of the web2py Web Framework 
  6  | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> 
  7  | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) 
  8   
  9  HTTP statuses helpers 
 10  -------------------------------------------- 
 11  """ 
 12   
 13  import re 
 14   
 15  __all__ = ['HTTP', 'redirect'] 
 16   
 17  defined_status = { 
 18      200: 'OK', 
 19      201: 'CREATED', 
 20      202: 'ACCEPTED', 
 21      203: 'NON-AUTHORITATIVE INFORMATION', 
 22      204: 'NO CONTENT', 
 23      205: 'RESET CONTENT', 
 24      206: 'PARTIAL CONTENT', 
 25      301: 'MOVED PERMANENTLY', 
 26      302: 'FOUND', 
 27      303: 'SEE OTHER', 
 28      304: 'NOT MODIFIED', 
 29      305: 'USE PROXY', 
 30      307: 'TEMPORARY REDIRECT', 
 31      400: 'BAD REQUEST', 
 32      401: 'UNAUTHORIZED', 
 33      402: 'PAYMENT REQUIRED', 
 34      403: 'FORBIDDEN', 
 35      404: 'NOT FOUND', 
 36      405: 'METHOD NOT ALLOWED', 
 37      406: 'NOT ACCEPTABLE', 
 38      407: 'PROXY AUTHENTICATION REQUIRED', 
 39      408: 'REQUEST TIMEOUT', 
 40      409: 'CONFLICT', 
 41      410: 'GONE', 
 42      411: 'LENGTH REQUIRED', 
 43      412: 'PRECONDITION FAILED', 
 44      413: 'REQUEST ENTITY TOO LARGE', 
 45      414: 'REQUEST-URI TOO LONG', 
 46      415: 'UNSUPPORTED MEDIA TYPE', 
 47      416: 'REQUESTED RANGE NOT SATISFIABLE', 
 48      417: 'EXPECTATION FAILED', 
 49      422: 'UNPROCESSABLE ENTITY', 
 50      429: 'TOO MANY REQUESTS', 
 51      451: 'UNAVAILABLE FOR LEGAL REASONS', # http://www.451unavailable.org/ 
 52      500: 'INTERNAL SERVER ERROR', 
 53      501: 'NOT IMPLEMENTED', 
 54      502: 'BAD GATEWAY', 
 55      503: 'SERVICE UNAVAILABLE', 
 56      504: 'GATEWAY TIMEOUT', 
 57      505: 'HTTP VERSION NOT SUPPORTED', 
 58      509: 'BANDWIDTH LIMIT EXCEEDED', 
 59  } 
 60   
 61  regex_status = re.compile('^\d{3} [0-9A-Z ]+$') 
62 63 -class HTTP(Exception):
64 """Raises an HTTP response 65 66 Args: 67 status: usually an integer. If it's a well known status code, the ERROR 68 message will be automatically added. A string can also be passed 69 as `510 Foo Bar` and in that case the status code and the error 70 message will be parsed accordingly 71 body: what to return as body. If left as is, will return the error code 72 and the status message in the body itself 73 cookies: pass cookies along (usually not needed) 74 headers: pass headers as usual dict mapping 75 """ 76
77 - def __init__( 78 self, 79 status, 80 body='', 81 cookies=None, 82 **headers 83 ):
84 self.status = status 85 self.body = body 86 self.headers = headers 87 self.cookies2headers(cookies)
88
89 - def cookies2headers(self, cookies):
90 if cookies and len(cookies) > 0: 91 self.headers['Set-Cookie'] = [ 92 str(cookie)[11:] for cookie in cookies.values()]
93
94 - def to(self, responder, env=None):
95 env = env or {} 96 status = self.status 97 headers = self.headers 98 if status in defined_status: 99 status = '%d %s' % (status, defined_status[status]) 100 elif isinstance(status, int): 101 status = '%d UNKNOWN ERROR' % status 102 else: 103 status = str(status) 104 if not regex_status.match(status): 105 status = '500 %s' % (defined_status[500]) 106 headers.setdefault('Content-Type', 'text/html; charset=UTF-8') 107 body = self.body 108 if status[:1] == '4': 109 if not body: 110 body = status 111 if isinstance(body, str): 112 headers['Content-Length'] = len(body) 113 rheaders = [] 114 for k, v in headers.iteritems(): 115 if isinstance(v, list): 116 rheaders += [(k, str(item)) for item in v] 117 elif not v is None: 118 rheaders.append((k, str(v))) 119 responder(status, rheaders) 120 if env.get('request_method', '') == 'HEAD': 121 return [''] 122 elif isinstance(body, str): 123 return [body] 124 elif hasattr(body, '__iter__'): 125 return body 126 else: 127 return [str(body)]
128 129 @property
130 - def message(self):
131 """ 132 compose a message describing this exception 133 134 "status defined_status [web2py_error]" 135 136 message elements that are not defined are omitted 137 """ 138 msg = '%(status)s' 139 if self.status in defined_status: 140 msg = '%(status)s %(defined_status)s' 141 if 'web2py_error' in self.headers: 142 msg += ' [%(web2py_error)s]' 143 return msg % dict( 144 status=self.status, 145 defined_status=defined_status.get(self.status), 146 web2py_error=self.headers.get('web2py_error'))
147
148 - def __str__(self):
149 "stringify me" 150 return self.message
151
152 153 -def redirect(location='', how=303, client_side=False):
154 """Raises a redirect (303) 155 156 Args: 157 location: the url where to redirect 158 how: what HTTP status code to use when redirecting 159 client_side: if set to True, it triggers a reload of the entire page 160 when the fragment has been loaded as a component 161 """ 162 if location: 163 from gluon import current 164 loc = location.replace('\r', '%0D').replace('\n', '%0A') 165 if client_side and current.request.ajax: 166 raise HTTP(200, **{'web2py-redirect-location': loc}) 167 else: 168 raise HTTP(how, 169 'You are being redirected <a href="%s">here</a>' % loc, 170 Location=loc) 171 else: 172 from gluon import current 173 if client_side and current.request.ajax: 174 raise HTTP(200, **{'web2py-component-command': 'window.location.reload(true)'})
175