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): 30 self.path = path.lstrip('/') 31 self.host = host.rstrip('/') 32 self.headers = RequestHeaders(headers) 33 34 @staticmethod 35 def ForTest(path, host=None, headers=None): 36 return Request(path, host or 'http://developer.chrome.com', headers or {}) 37 38 def __repr__(self): 39 return 'Request(path=%s, host=%s, headers=%s)' % ( 40 self.path, self.host, self.headers) 41 42 def __str__(self): 43 return repr(self) 44 45class _ContentBuilder(object): 46 '''Builds the response content. 47 ''' 48 def __init__(self): 49 self._buf = [] 50 51 def Append(self, content): 52 if isinstance(content, unicode): 53 content = content.encode('utf-8', 'replace') 54 self._buf.append(content) 55 56 def ToString(self): 57 self._Collapse() 58 return self._buf[0] 59 60 def __str__(self): 61 return self.ToString() 62 63 def __len__(self): 64 return len(self.ToString()) 65 66 def _Collapse(self): 67 self._buf = [''.join(self._buf)] 68 69class Response(object): 70 '''The response from Get(). 71 ''' 72 def __init__(self, content=None, headers=None, status=None): 73 self.content = _ContentBuilder() 74 if content is not None: 75 self.content.Append(content) 76 self.headers = {} 77 if headers is not None: 78 self.headers.update(headers) 79 self.status = status 80 81 @staticmethod 82 def Ok(content, headers=None): 83 '''Returns an OK (200) response. 84 ''' 85 return Response(content=content, headers=headers, status=200) 86 87 @staticmethod 88 def Redirect(url, permanent=False): 89 '''Returns a redirect (301 or 302) response. 90 ''' 91 status = 301 if permanent else 302 92 return Response(headers={'Location': url}, status=status) 93 94 @staticmethod 95 def NotFound(content, headers=None): 96 '''Returns a not found (404) response. 97 ''' 98 return Response(content=content, headers=headers, status=404) 99 100 @staticmethod 101 def NotModified(content, headers=None): 102 return Response(content=content, headers=headers, status=304) 103 104 @staticmethod 105 def InternalError(content, headers=None): 106 '''Returns an internal error (500) response. 107 ''' 108 return Response(content=content, headers=headers, status=500) 109 110 def Append(self, content): 111 '''Appends |content| to the response content. 112 ''' 113 self.content.append(content) 114 115 def AddHeader(self, key, value): 116 '''Adds a header to the response. 117 ''' 118 self.headers[key] = value 119 120 def AddHeaders(self, headers): 121 '''Adds several headers to the response. 122 ''' 123 self.headers.update(headers) 124 125 def SetStatus(self, status): 126 self.status = status 127 128 def GetRedirect(self): 129 if self.headers.get('Location') is None: 130 return (None, None) 131 return (self.headers.get('Location'), self.status == 301) 132 133 def IsNotFound(self): 134 return self.status == 404 135 136 def __eq__(self, other): 137 return (isinstance(other, self.__class__) and 138 str(other.content) == str(self.content) and 139 other.headers == self.headers and 140 other.status == self.status) 141 142 def __ne__(self, other): 143 return not (self == other) 144 145 def __repr__(self): 146 return 'Response(content=%s bytes, status=%s, headers=%s)' % ( 147 len(self.content), self.status, self.headers) 148 149 def __str__(self): 150 return repr(self) 151 152class Servlet(object): 153 def __init__(self, request): 154 self._request = request 155 156 def Get(self): 157 '''Returns a Response. 158 ''' 159 raise NotImplemented() 160