http.py (8856B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\werkzeug\http.py 2 import re 3 import inspect 4 try: 5 from email.utils import parsedate_tz 6 except ImportError: 7 from email.Utils import parsedate_tz 8 9 from urllib2 import parse_http_list as _parse_list_header 10 from datetime import datetime, timedelta 11 try: 12 from hashlib import md5 13 except ImportError: 14 from md5 import new as md5 15 16 from werkzeug._internal import HTTP_STATUS_CODES 17 _accept_re = re.compile('([^\\s;,]+)(?:[^,]*?;\\s*q=(\\d*(?:\\.\\d+)?))?') 18 _token_chars = frozenset("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~") 19 _etag_re = re.compile('([Ww]/)?(?:"(.*?)"|(.*?))(?:\\s*,\\s*|$)') 20 _unsafe_header_chars = set('()<>@,;:"/[]?={} \t') 21 _quoted_string_re = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"' 22 _option_header_piece_re = re.compile(';\\s*([^\\s;=]+|%s)\\s*(?:=\\s*([^;]+|%s))?\\s*' % (_quoted_string_re, _quoted_string_re)) 23 _entity_headers = frozenset(['allow', 24 'content-encoding', 25 'content-language', 26 'content-length', 27 'content-location', 28 'content-md5', 29 'content-range', 30 'content-type', 31 'expires', 32 'last-modified']) 33 _hop_by_pop_headers = frozenset(['connection', 34 'keep-alive', 35 'proxy-authenticate', 36 'proxy-authorization', 37 'te', 38 'trailers', 39 'transfer-encoding', 40 'upgrade']) 41 42 def quote_header_value(value, extra_chars = '', allow_token = True): 43 value = str(value) 44 if allow_token: 45 token_chars = _token_chars | set(extra_chars) 46 if set(value).issubset(token_chars): 47 return value 48 return '"%s"' % value.replace('\\', '\\\\').replace('"', '\\"') 49 50 51 def unquote_header_value(value, is_filename = False): 52 if value and value[0] == value[-1] == '"': 53 value = value[1:-1] 54 if not is_filename or value[:2] != '\\\\': 55 return value.replace('\\\\', '\\').replace('\\"', '"') 56 return value 57 58 59 def dump_options_header(header, options): 60 segments = [] 61 if header is not None: 62 segments.append(header) 63 for key, value in options.iteritems(): 64 if value is None: 65 segments.append(key) 66 else: 67 segments.append('%s=%s' % (key, quote_header_value(value))) 68 69 return '; '.join(segments) 70 71 72 def dump_header(iterable, allow_token = True): 73 if isinstance(iterable, dict): 74 items = [] 75 for key, value in iterable.iteritems(): 76 if value is None: 77 items.append(key) 78 else: 79 items.append('%s=%s' % (key, quote_header_value(value, allow_token=allow_token))) 80 81 else: 82 items = [ quote_header_value(x, allow_token=allow_token) for x in iterable ] 83 return ', '.join(items) 84 85 86 def parse_list_header(value): 87 result = [] 88 for item in _parse_list_header(value): 89 if item[:1] == item[-1:] == '"': 90 item = unquote_header_value(item[1:-1]) 91 result.append(item) 92 93 return result 94 95 96 def parse_dict_header(value): 97 result = {} 98 for item in _parse_list_header(value): 99 if '=' not in item: 100 result[item] = None 101 continue 102 name, value = item.split('=', 1) 103 if value[:1] == value[-1:] == '"': 104 value = unquote_header_value(value[1:-1]) 105 result[name] = value 106 107 return result 108 109 110 def parse_options_header(value): 111 112 def _tokenize(string): 113 for match in _option_header_piece_re.finditer(string): 114 key, value = match.groups() 115 key = unquote_header_value(key) 116 if value is not None: 117 value = unquote_header_value(value, key == 'filename') 118 yield (key, value) 119 120 if not value: 121 return ('', {}) 122 parts = _tokenize(';' + value) 123 name = parts.next()[0] 124 extra = dict(parts) 125 return (name, extra) 126 127 128 def parse_accept_header(value, cls = None): 129 if cls is None: 130 cls = Accept 131 if not value: 132 return cls(None) 133 result = [] 134 for match in _accept_re.finditer(value): 135 quality = match.group(2) 136 if not quality: 137 quality = 1 138 else: 139 quality = max(min(float(quality), 1), 0) 140 result.append((match.group(1), quality)) 141 142 return cls(result) 143 144 145 def parse_cache_control_header(value, on_update = None, cls = None): 146 if cls is None: 147 cls = RequestCacheControl 148 if not value: 149 return cls(None, on_update) 150 return cls(parse_dict_header(value), on_update) 151 152 153 def parse_set_header(value, on_update = None): 154 if not value: 155 return HeaderSet(None, on_update) 156 return HeaderSet(parse_list_header(value), on_update) 157 158 159 def parse_authorization_header(value): 160 if not value: 161 return 162 try: 163 auth_type, auth_info = value.split(None, 1) 164 auth_type = auth_type.lower() 165 except ValueError: 166 return 167 168 if auth_type == 'basic': 169 try: 170 username, password = auth_info.decode('base64').split(':', 1) 171 except Exception as e: 172 return 173 174 return Authorization('basic', {'username': username, 175 'password': password}) 176 if auth_type == 'digest': 177 auth_map = parse_dict_header(auth_info) 178 for key in ('username', 'realm', 'nonce', 'uri', 'nc', 'cnonce', 'response'): 179 if key not in auth_map: 180 return 181 182 return Authorization('digest', auth_map) 183 184 185 def parse_www_authenticate_header(value, on_update = None): 186 if not value: 187 return WWWAuthenticate(on_update=on_update) 188 try: 189 auth_type, auth_info = value.split(None, 1) 190 auth_type = auth_type.lower() 191 except (ValueError, AttributeError): 192 return WWWAuthenticate(value.strip().lower(), on_update=on_update) 193 194 return WWWAuthenticate(auth_type, parse_dict_header(auth_info), on_update) 195 196 197 def quote_etag(etag, weak = False): 198 if '"' in etag: 199 raise ValueError('invalid etag') 200 etag = '"%s"' % etag 201 if weak: 202 etag = 'w/' + etag 203 return etag 204 205 206 def unquote_etag(etag): 207 if not etag: 208 return (None, None) 209 etag = etag.strip() 210 weak = False 211 if etag[:2] in ('w/', 'W/'): 212 weak = True 213 etag = etag[2:] 214 if etag[:1] == etag[-1:] == '"': 215 etag = etag[1:-1] 216 return (etag, weak) 217 218 219 def parse_etags(value): 220 if not value: 221 return ETags() 222 strong = [] 223 weak = [] 224 end = len(value) 225 pos = 0 226 while pos < end: 227 match = _etag_re.match(value, pos) 228 if match is None: 229 break 230 is_weak, quoted, raw = match.groups() 231 if raw == '*': 232 return ETags(star_tag=True) 233 if quoted: 234 raw = quoted 235 if is_weak: 236 weak.append(raw) 237 else: 238 strong.append(raw) 239 pos = match.end() 240 241 return ETags(strong, weak) 242 243 244 def generate_etag(data): 245 return md5(data).hexdigest() 246 247 248 def parse_date(value): 249 if value: 250 t = parsedate_tz(value.strip()) 251 if t is not None: 252 try: 253 year = t[0] 254 if year >= 0 and year <= 68: 255 year += 2000 256 elif year >= 69 and year <= 99: 257 year += 1900 258 return datetime(*((year,) + t[1:7])) - timedelta(seconds=t[-1] or 0) 259 except (ValueError, OverflowError): 260 return 261 262 263 def is_resource_modified(environ, etag = None, data = None, last_modified = None): 264 if etag is None and data is not None: 265 etag = generate_etag(data) 266 elif data is not None: 267 raise TypeError('both data and etag given') 268 if environ['REQUEST_METHOD'] not in ('GET', 'HEAD'): 269 return False 270 unmodified = False 271 if isinstance(last_modified, basestring): 272 last_modified = parse_date(last_modified) 273 modified_since = parse_date(environ.get('HTTP_IF_MODIFIED_SINCE')) 274 if modified_since and last_modified and last_modified <= modified_since: 275 unmodified = True 276 if etag: 277 if_none_match = parse_etags(environ.get('HTTP_IF_NONE_MATCH')) 278 if if_none_match: 279 unmodified = if_none_match.contains_raw(etag) 280 return not unmodified 281 282 283 def remove_entity_headers(headers, allowed = ('expires', 'content-location')): 284 allowed = set((x.lower() for x in allowed)) 285 headers[:] = [ (key, value) for key, value in headers if not is_entity_header(key) or key.lower() in allowed ] 286 287 288 def remove_hop_by_hop_headers(headers): 289 headers[:] = [ (key, value) for key, value in headers if not is_hop_by_hop_header(key) ] 290 291 292 def is_entity_header(header): 293 return header.lower() in _entity_headers 294 295 296 def is_hop_by_hop_header(header): 297 return header.lower() in _hop_by_pop_headers 298 299 300 from werkzeug.datastructures import Headers, Accept, RequestCacheControl, ResponseCacheControl, HeaderSet, ETags, Authorization, WWWAuthenticate 301 from werkzeug.datastructures import MIMEAccept, CharsetAccept, LanguageAccept