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)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class Request(object):
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''Request data.
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''
8b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def __init__(self, path, host, headers):
9b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    self.path = path.lstrip('/')
10b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    self.host = host.rstrip('/')
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.headers = headers
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
13b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  @staticmethod
14a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  def ForTest(path, host='http://developer.chrome.com', headers=None):
15a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    return Request(path, host, headers or {})
16b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
17b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def __repr__(self):
18ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return 'Request(path=%s, host=%s, headers=%s)' % (
19ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        self.path, self.host, self.headers)
20b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
21b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def __str__(self):
22b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return repr(self)
23b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class _ContentBuilder(object):
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''Builds the response content.
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def __init__(self):
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self._buf = []
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def Append(self, content):
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if isinstance(content, unicode):
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content = content.encode('utf-8', 'replace')
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self._buf.append(content)
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def ToString(self):
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self._Collapse()
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return self._buf[0]
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def __str__(self):
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return self.ToString()
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def __len__(self):
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return len(self.ToString())
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def _Collapse(self):
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self._buf = [''.join(self._buf)]
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class Response(object):
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''The response from Get().
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def __init__(self, content=None, headers=None, status=None):
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.content = _ContentBuilder()
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if content is not None:
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self.content.Append(content)
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.headers = {}
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if headers is not None:
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self.headers.update(headers)
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.status = status
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  @staticmethod
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def Ok(content, headers=None):
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Returns an OK (200) response.
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return Response(content=content, headers=headers, status=200)
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  @staticmethod
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def Redirect(url, permanent=False):
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Returns a redirect (301 or 302) response.
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    status = 301 if permanent else 302
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return Response(headers={'Location': url}, status=status)
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  @staticmethod
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def NotFound(content, headers=None):
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Returns a not found (404) response.
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return Response(content=content, headers=headers, status=404)
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  @staticmethod
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def InternalError(content, headers=None):
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Returns an internal error (500) response.
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return Response(content=content, headers=headers, status=500)
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def Append(self, content):
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Appends |content| to the response content.
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.content.append(content)
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def AddHeader(self, key, value):
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Adds a header to the response.
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.headers[key] = value
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def AddHeaders(self, headers):
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Adds several headers to the response.
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.headers.update(headers)
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def SetStatus(self, status):
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.status = status
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
103a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  def GetRedirect(self):
104a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if self.headers.get('Location') is None:
105a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      return (None, None)
106a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    return (self.headers.get('Location'), self.status == 301)
107a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
1080f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  def IsNotFound(self):
1090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    return self.status == 404
1100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
111ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def __eq__(self, other):
112ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return (isinstance(other, self.__class__) and
113ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            str(other.content) == str(self.content) and
114ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            other.headers == self.headers and
115ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            other.status == self.status)
116ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
117ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def __ne__(self, other):
118ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return not (self == other)
119ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def __repr__(self):
121ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return 'Response(content=%s bytes, status=%s, headers=%s)' % (
122ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        len(self.content), self.status, self.headers)
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
124b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def __str__(self):
125b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return repr(self)
126b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class Servlet(object):
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def __init__(self, request):
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self._request = request
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def Get(self):
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Returns a Response.
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    raise NotImplemented()
135