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