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