1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5 6class RequestHeaders(object): 7 '''A custom dictionary impementation for headers which ignores the case 8 of requests, since different HTTP libraries seem to mangle them. 9 ''' 10 def __init__(self, dict_): 11 if isinstance(dict_, RequestHeaders): 12 self._dict = dict_ 13 else: 14 self._dict = dict((k.lower(), v) for k, v in dict_.iteritems()) 15 16 def get(self, key, default=None): 17 return self._dict.get(key.lower(), default) 18 19 def __repr__(self): 20 return repr(self._dict) 21 22 def __str__(self): 23 return repr(self._dict) 24 25 26class Request(object): 27 '''Request data. 28 ''' 29 def __init__(self, path, host, headers, arguments={}): 30 self.path = path.lstrip('/') 31 self.host = host.rstrip('/') 32 self.headers = RequestHeaders(headers) 33 self.arguments = arguments 34 35 @staticmethod 36 def ForTest(path, host=None, headers=None, arguments=None): 37 return Request(path, 38 host or 'http://developer.chrome.com', 39 headers or {}, 40 arguments or {}) 41 42 def __repr__(self): 43 return 'Request(path=%s, host=%s, headers=%s)' % ( 44 self.path, self.host, self.headers) 45 46 def __str__(self): 47 return repr(self) 48 49class _ContentBuilder(object): 50 '''Builds the response content. 51 ''' 52 def __init__(self): 53 self._buf = [] 54 55 def Append(self, content): 56 if isinstance(content, unicode): 57 content = content.encode('utf-8', 'replace') 58 self._buf.append(content) 59 60 def ToString(self): 61 self._Collapse() 62 return self._buf[0] 63 64 def __str__(self): 65 return self.ToString() 66 67 def __len__(self): 68 return len(self.ToString()) 69 70 def _Collapse(self): 71 self._buf = [''.join(self._buf)] 72 73class Response(object): 74 '''The response from Get(). 75 ''' 76 def __init__(self, content=None, headers=None, status=None): 77 self.content = _ContentBuilder() 78 if content is not None: 79 self.content.Append(content) 80 self.headers = {} 81 if headers is not None: 82 self.headers.update(headers) 83 self.status = status 84 85 @staticmethod 86 def Ok(content, headers=None): 87 '''Returns an OK (200) response. 88 ''' 89 return Response(content=content, headers=headers, status=200) 90 91 @staticmethod 92 def Redirect(url, permanent=False): 93 '''Returns a redirect (301 or 302) response. 94 ''' 95 status = 301 if permanent else 302 96 return Response(headers={'Location': url}, status=status) 97 98 @staticmethod 99 def NotFound(content, headers=None): 100 '''Returns a not found (404) response. 101 ''' 102 return Response(content=content, headers=headers, status=404) 103 104 @staticmethod 105 def NotModified(content, headers=None): 106 return Response(content=content, headers=headers, status=304) 107 108 @staticmethod 109 def InternalError(content, headers=None): 110 '''Returns an internal error (500) response. 111 ''' 112 return Response(content=content, headers=headers, status=500) 113 114 @staticmethod 115 def ThrottledError(content, headers=None): 116 '''Returns an HTTP throttle error (429) response. 117 ''' 118 return Response(content=content, headers=headers, status=429) 119 120 def Append(self, content): 121 '''Appends |content| to the response content. 122 ''' 123 self.content.append(content) 124 125 def AddHeader(self, key, value): 126 '''Adds a header to the response. 127 ''' 128 self.headers[key] = value 129 130 def AddHeaders(self, headers): 131 '''Adds several headers to the response. 132 ''' 133 self.headers.update(headers) 134 135 def SetStatus(self, status): 136 self.status = status 137 138 def GetRedirect(self): 139 if self.headers.get('Location') is None: 140 return (None, None) 141 return (self.headers.get('Location'), self.status == 301) 142 143 def IsNotFound(self): 144 return self.status == 404 145 146 def __eq__(self, other): 147 return (isinstance(other, self.__class__) and 148 str(other.content) == str(self.content) and 149 other.headers == self.headers and 150 other.status == self.status) 151 152 def __ne__(self, other): 153 return not (self == other) 154 155 def __repr__(self): 156 return 'Response(content=%s bytes, status=%s, headers=%s)' % ( 157 len(self.content), self.status, self.headers) 158 159 def __str__(self): 160 return repr(self) 161 162class Servlet(object): 163 def __init__(self, request): 164 self._request = request 165 166 def Get(self): 167 '''Returns a Response. 168 ''' 169 raise NotImplemented() 170