root / lib / http / server.py @ 7e7fa841
History | View | Annotate | Download (18.4 kB)
1 | 02cab3e7 | Michael Hanselmann | #
|
---|---|---|---|
2 | 02cab3e7 | Michael Hanselmann | #
|
3 | 02cab3e7 | Michael Hanselmann | |
4 | 5ae4945a | Iustin Pop | # Copyright (C) 2007, 2008, 2010, 2012 Google Inc.
|
5 | 02cab3e7 | Michael Hanselmann | #
|
6 | 02cab3e7 | Michael Hanselmann | # This program is free software; you can redistribute it and/or modify
|
7 | 02cab3e7 | Michael Hanselmann | # it under the terms of the GNU General Public License as published by
|
8 | 02cab3e7 | Michael Hanselmann | # the Free Software Foundation; either version 2 of the License, or
|
9 | 02cab3e7 | Michael Hanselmann | # (at your option) any later version.
|
10 | 02cab3e7 | Michael Hanselmann | #
|
11 | 02cab3e7 | Michael Hanselmann | # This program is distributed in the hope that it will be useful, but
|
12 | 02cab3e7 | Michael Hanselmann | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 02cab3e7 | Michael Hanselmann | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 02cab3e7 | Michael Hanselmann | # General Public License for more details.
|
15 | 02cab3e7 | Michael Hanselmann | #
|
16 | 02cab3e7 | Michael Hanselmann | # You should have received a copy of the GNU General Public License
|
17 | 02cab3e7 | Michael Hanselmann | # along with this program; if not, write to the Free Software
|
18 | 02cab3e7 | Michael Hanselmann | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 02cab3e7 | Michael Hanselmann | # 02110-1301, USA.
|
20 | 02cab3e7 | Michael Hanselmann | |
21 | 02cab3e7 | Michael Hanselmann | """HTTP server module.
|
22 | 02cab3e7 | Michael Hanselmann |
|
23 | 02cab3e7 | Michael Hanselmann | """
|
24 | 02cab3e7 | Michael Hanselmann | |
25 | 02cab3e7 | Michael Hanselmann | import BaseHTTPServer |
26 | 02cab3e7 | Michael Hanselmann | import cgi |
27 | 02cab3e7 | Michael Hanselmann | import logging |
28 | 02cab3e7 | Michael Hanselmann | import os |
29 | 02cab3e7 | Michael Hanselmann | import socket |
30 | 02cab3e7 | Michael Hanselmann | import time |
31 | 02cab3e7 | Michael Hanselmann | import signal |
32 | 112d240d | Guido Trotter | import asyncore |
33 | 02cab3e7 | Michael Hanselmann | |
34 | 02cab3e7 | Michael Hanselmann | from ganeti import http |
35 | 82869978 | Michael Hanselmann | from ganeti import utils |
36 | db4e138b | Manuel Franceschini | from ganeti import netutils |
37 | d859a2cf | Michael Hanselmann | from ganeti import compat |
38 | 352e1a26 | Michael Hanselmann | from ganeti import errors |
39 | 02cab3e7 | Michael Hanselmann | |
40 | 02cab3e7 | Michael Hanselmann | |
41 | d0c8c01d | Iustin Pop | WEEKDAYNAME = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] |
42 | 02cab3e7 | Michael Hanselmann | MONTHNAME = [None,
|
43 | d0c8c01d | Iustin Pop | "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
44 | d0c8c01d | Iustin Pop | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] |
45 | 02cab3e7 | Michael Hanselmann | |
46 | 02cab3e7 | Michael Hanselmann | # Default error message
|
47 | 02cab3e7 | Michael Hanselmann | DEFAULT_ERROR_CONTENT_TYPE = "text/html"
|
48 | 02cab3e7 | Michael Hanselmann | DEFAULT_ERROR_MESSAGE = """\
|
49 | 02cab3e7 | Michael Hanselmann | <html>
|
50 | 02cab3e7 | Michael Hanselmann | <head>
|
51 | 02cab3e7 | Michael Hanselmann | <title>Error response</title>
|
52 | 02cab3e7 | Michael Hanselmann | </head>
|
53 | 02cab3e7 | Michael Hanselmann | <body>
|
54 | 02cab3e7 | Michael Hanselmann | <h1>Error response</h1>
|
55 | 02cab3e7 | Michael Hanselmann | <p>Error code %(code)d.
|
56 | 02cab3e7 | Michael Hanselmann | <p>Message: %(message)s.
|
57 | 02cab3e7 | Michael Hanselmann | <p>Error code explanation: %(code)s = %(explain)s.
|
58 | 02cab3e7 | Michael Hanselmann | </body>
|
59 | 02cab3e7 | Michael Hanselmann | </html>
|
60 | 02cab3e7 | Michael Hanselmann | """
|
61 | 02cab3e7 | Michael Hanselmann | |
62 | 02cab3e7 | Michael Hanselmann | |
63 | f30ca1e6 | Michael Hanselmann | def _DateTimeHeader(gmnow=None): |
64 | 02cab3e7 | Michael Hanselmann | """Return the current date and time formatted for a message header.
|
65 | 02cab3e7 | Michael Hanselmann |
|
66 | f30ca1e6 | Michael Hanselmann | The time MUST be in the GMT timezone.
|
67 | f30ca1e6 | Michael Hanselmann |
|
68 | 02cab3e7 | Michael Hanselmann | """
|
69 | f30ca1e6 | Michael Hanselmann | if gmnow is None: |
70 | f30ca1e6 | Michael Hanselmann | gmnow = time.gmtime() |
71 | f30ca1e6 | Michael Hanselmann | (year, month, day, hh, mm, ss, wd, _, _) = gmnow |
72 | 02cab3e7 | Michael Hanselmann | return ("%s, %02d %3s %4d %02d:%02d:%02d GMT" % |
73 | 02cab3e7 | Michael Hanselmann | (WEEKDAYNAME[wd], day, MONTHNAME[month], year, hh, mm, ss)) |
74 | 02cab3e7 | Michael Hanselmann | |
75 | 02cab3e7 | Michael Hanselmann | |
76 | 02cab3e7 | Michael Hanselmann | class _HttpServerRequest(object): |
77 | 02cab3e7 | Michael Hanselmann | """Data structure for HTTP request on server side.
|
78 | 02cab3e7 | Michael Hanselmann |
|
79 | 02cab3e7 | Michael Hanselmann | """
|
80 | a8950eb7 | Michael Hanselmann | def __init__(self, method, path, headers, body): |
81 | 02cab3e7 | Michael Hanselmann | # Request attributes
|
82 | a8950eb7 | Michael Hanselmann | self.request_method = method
|
83 | a8950eb7 | Michael Hanselmann | self.request_path = path
|
84 | a8950eb7 | Michael Hanselmann | self.request_headers = headers
|
85 | a8950eb7 | Michael Hanselmann | self.request_body = body
|
86 | 02cab3e7 | Michael Hanselmann | |
87 | 02cab3e7 | Michael Hanselmann | # Response attributes
|
88 | 02cab3e7 | Michael Hanselmann | self.resp_headers = {}
|
89 | 02cab3e7 | Michael Hanselmann | |
90 | 68fa9caf | Michael Hanselmann | # Private data for request handler (useful in combination with
|
91 | 68fa9caf | Michael Hanselmann | # authentication)
|
92 | 68fa9caf | Michael Hanselmann | self.private = None |
93 | 68fa9caf | Michael Hanselmann | |
94 | d44ea6a3 | Michael Hanselmann | def __repr__(self): |
95 | d44ea6a3 | Michael Hanselmann | status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__), |
96 | d44ea6a3 | Michael Hanselmann | self.request_method, self.request_path, |
97 | d44ea6a3 | Michael Hanselmann | "headers=%r" % str(self.request_headers), |
98 | d44ea6a3 | Michael Hanselmann | "body=%r" % (self.request_body, )] |
99 | d44ea6a3 | Michael Hanselmann | |
100 | d44ea6a3 | Michael Hanselmann | return "<%s at %#x>" % (" ".join(status), id(self)) |
101 | d44ea6a3 | Michael Hanselmann | |
102 | 02cab3e7 | Michael Hanselmann | |
103 | 02cab3e7 | Michael Hanselmann | class _HttpServerToClientMessageWriter(http.HttpMessageWriter): |
104 | 02cab3e7 | Michael Hanselmann | """Writes an HTTP response to client.
|
105 | 02cab3e7 | Michael Hanselmann |
|
106 | 02cab3e7 | Michael Hanselmann | """
|
107 | 02cab3e7 | Michael Hanselmann | def __init__(self, sock, request_msg, response_msg, write_timeout): |
108 | 358a8811 | Michael Hanselmann | """Writes the response to the client.
|
109 | 358a8811 | Michael Hanselmann |
|
110 | 358a8811 | Michael Hanselmann | @type sock: socket
|
111 | 358a8811 | Michael Hanselmann | @param sock: Target socket
|
112 | 358a8811 | Michael Hanselmann | @type request_msg: http.HttpMessage
|
113 | 358a8811 | Michael Hanselmann | @param request_msg: Request message, required to determine whether
|
114 | 25e7b43f | Iustin Pop | response may have a message body
|
115 | 358a8811 | Michael Hanselmann | @type response_msg: http.HttpMessage
|
116 | 358a8811 | Michael Hanselmann | @param response_msg: Response message
|
117 | 358a8811 | Michael Hanselmann | @type write_timeout: float
|
118 | 358a8811 | Michael Hanselmann | @param write_timeout: Write timeout for socket
|
119 | 02cab3e7 | Michael Hanselmann |
|
120 | 02cab3e7 | Michael Hanselmann | """
|
121 | 02cab3e7 | Michael Hanselmann | self._request_msg = request_msg
|
122 | 02cab3e7 | Michael Hanselmann | self._response_msg = response_msg
|
123 | 02cab3e7 | Michael Hanselmann | http.HttpMessageWriter.__init__(self, sock, response_msg, write_timeout)
|
124 | 02cab3e7 | Michael Hanselmann | |
125 | 02cab3e7 | Michael Hanselmann | def HasMessageBody(self): |
126 | 02cab3e7 | Michael Hanselmann | """Logic to detect whether response should contain a message body.
|
127 | 02cab3e7 | Michael Hanselmann |
|
128 | 02cab3e7 | Michael Hanselmann | """
|
129 | 02cab3e7 | Michael Hanselmann | if self._request_msg.start_line: |
130 | 02cab3e7 | Michael Hanselmann | request_method = self._request_msg.start_line.method
|
131 | 02cab3e7 | Michael Hanselmann | else:
|
132 | 02cab3e7 | Michael Hanselmann | request_method = None
|
133 | 02cab3e7 | Michael Hanselmann | |
134 | 02cab3e7 | Michael Hanselmann | response_code = self._response_msg.start_line.code
|
135 | 02cab3e7 | Michael Hanselmann | |
136 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 4.3: "A message-body MUST NOT be included in a request
|
137 | 02cab3e7 | Michael Hanselmann | # if the specification of the request method (section 5.1.1) does not allow
|
138 | 02cab3e7 | Michael Hanselmann | # sending an entity-body in requests"
|
139 | 02cab3e7 | Michael Hanselmann | #
|
140 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 9.4: "The HEAD method is identical to GET except that
|
141 | 02cab3e7 | Michael Hanselmann | # the server MUST NOT return a message-body in the response."
|
142 | 02cab3e7 | Michael Hanselmann | #
|
143 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 10.2.5: "The 204 response MUST NOT include a
|
144 | 02cab3e7 | Michael Hanselmann | # message-body [...]"
|
145 | 02cab3e7 | Michael Hanselmann | #
|
146 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 10.3.5: "The 304 response MUST NOT contain a
|
147 | 02cab3e7 | Michael Hanselmann | # message-body, [...]"
|
148 | 02cab3e7 | Michael Hanselmann | |
149 | 02cab3e7 | Michael Hanselmann | return (http.HttpMessageWriter.HasMessageBody(self) and |
150 | 3f3dfc15 | Iustin Pop | (request_method is not None and |
151 | 3f3dfc15 | Iustin Pop | request_method != http.HTTP_HEAD) and
|
152 | 02cab3e7 | Michael Hanselmann | response_code >= http.HTTP_OK and
|
153 | 3f3dfc15 | Iustin Pop | response_code not in (http.HTTP_NO_CONTENT, |
154 | 3f3dfc15 | Iustin Pop | http.HTTP_NOT_MODIFIED)) |
155 | 02cab3e7 | Michael Hanselmann | |
156 | 02cab3e7 | Michael Hanselmann | |
157 | 02cab3e7 | Michael Hanselmann | class _HttpClientToServerMessageReader(http.HttpMessageReader): |
158 | 02cab3e7 | Michael Hanselmann | """Reads an HTTP request sent by client.
|
159 | 02cab3e7 | Michael Hanselmann |
|
160 | 02cab3e7 | Michael Hanselmann | """
|
161 | 02cab3e7 | Michael Hanselmann | # Length limits
|
162 | 02cab3e7 | Michael Hanselmann | START_LINE_LENGTH_MAX = 4096
|
163 | 02cab3e7 | Michael Hanselmann | HEADER_LENGTH_MAX = 4096
|
164 | 02cab3e7 | Michael Hanselmann | |
165 | 02cab3e7 | Michael Hanselmann | def ParseStartLine(self, start_line): |
166 | 02cab3e7 | Michael Hanselmann | """Parses the start line sent by client.
|
167 | 02cab3e7 | Michael Hanselmann |
|
168 | 02cab3e7 | Michael Hanselmann | Example: "GET /index.html HTTP/1.1"
|
169 | 02cab3e7 | Michael Hanselmann |
|
170 | 02cab3e7 | Michael Hanselmann | @type start_line: string
|
171 | 02cab3e7 | Michael Hanselmann | @param start_line: Start line
|
172 | 02cab3e7 | Michael Hanselmann |
|
173 | 02cab3e7 | Michael Hanselmann | """
|
174 | 02cab3e7 | Michael Hanselmann | # Empty lines are skipped when reading
|
175 | 02cab3e7 | Michael Hanselmann | assert start_line
|
176 | 02cab3e7 | Michael Hanselmann | |
177 | 02cab3e7 | Michael Hanselmann | logging.debug("HTTP request: %s", start_line)
|
178 | 02cab3e7 | Michael Hanselmann | |
179 | 02cab3e7 | Michael Hanselmann | words = start_line.split() |
180 | 02cab3e7 | Michael Hanselmann | |
181 | 02cab3e7 | Michael Hanselmann | if len(words) == 3: |
182 | 02cab3e7 | Michael Hanselmann | [method, path, version] = words |
183 | d0c8c01d | Iustin Pop | if version[:5] != "HTTP/": |
184 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request version (%r)" % version) |
185 | 02cab3e7 | Michael Hanselmann | |
186 | 02cab3e7 | Michael Hanselmann | try:
|
187 | 02cab3e7 | Michael Hanselmann | base_version_number = version.split("/", 1)[1] |
188 | 02cab3e7 | Michael Hanselmann | version_number = base_version_number.split(".")
|
189 | 02cab3e7 | Michael Hanselmann | |
190 | 02cab3e7 | Michael Hanselmann | # RFC 2145 section 3.1 says there can be only one "." and
|
191 | 02cab3e7 | Michael Hanselmann | # - major and minor numbers MUST be treated as
|
192 | 02cab3e7 | Michael Hanselmann | # separate integers;
|
193 | 02cab3e7 | Michael Hanselmann | # - HTTP/2.4 is a lower version than HTTP/2.13, which in
|
194 | 02cab3e7 | Michael Hanselmann | # turn is lower than HTTP/12.3;
|
195 | 02cab3e7 | Michael Hanselmann | # - Leading zeros MUST be ignored by recipients.
|
196 | 02cab3e7 | Michael Hanselmann | if len(version_number) != 2: |
197 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request version (%r)" % version) |
198 | 02cab3e7 | Michael Hanselmann | |
199 | 02cab3e7 | Michael Hanselmann | version_number = (int(version_number[0]), int(version_number[1])) |
200 | 02cab3e7 | Michael Hanselmann | except (ValueError, IndexError): |
201 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request version (%r)" % version) |
202 | 02cab3e7 | Michael Hanselmann | |
203 | 02cab3e7 | Michael Hanselmann | if version_number >= (2, 0): |
204 | 02cab3e7 | Michael Hanselmann | raise http.HttpVersionNotSupported("Invalid HTTP Version (%s)" % |
205 | 5ae4945a | Iustin Pop | base_version_number) |
206 | 02cab3e7 | Michael Hanselmann | |
207 | 02cab3e7 | Michael Hanselmann | elif len(words) == 2: |
208 | 02cab3e7 | Michael Hanselmann | version = http.HTTP_0_9 |
209 | 02cab3e7 | Michael Hanselmann | [method, path] = words |
210 | 02cab3e7 | Michael Hanselmann | if method != http.HTTP_GET:
|
211 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad HTTP/0.9 request type (%r)" % method) |
212 | 02cab3e7 | Michael Hanselmann | |
213 | 02cab3e7 | Michael Hanselmann | else:
|
214 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request syntax (%r)" % start_line) |
215 | 02cab3e7 | Michael Hanselmann | |
216 | 02cab3e7 | Michael Hanselmann | return http.HttpClientToServerStartLine(method, path, version)
|
217 | 02cab3e7 | Michael Hanselmann | |
218 | 02cab3e7 | Michael Hanselmann | |
219 | d859a2cf | Michael Hanselmann | def _HandleServerRequestInner(handler, req_msg): |
220 | c81f452f | Michael Hanselmann | """Calls the handler function for the current request.
|
221 | c81f452f | Michael Hanselmann |
|
222 | c81f452f | Michael Hanselmann | """
|
223 | c81f452f | Michael Hanselmann | handler_context = _HttpServerRequest(req_msg.start_line.method, |
224 | c81f452f | Michael Hanselmann | req_msg.start_line.path, |
225 | c81f452f | Michael Hanselmann | req_msg.headers, |
226 | c81f452f | Michael Hanselmann | req_msg.body) |
227 | c81f452f | Michael Hanselmann | |
228 | c81f452f | Michael Hanselmann | logging.debug("Handling request %r", handler_context)
|
229 | c81f452f | Michael Hanselmann | |
230 | c81f452f | Michael Hanselmann | try:
|
231 | c81f452f | Michael Hanselmann | try:
|
232 | c81f452f | Michael Hanselmann | # Authentication, etc.
|
233 | c81f452f | Michael Hanselmann | handler.PreHandleRequest(handler_context) |
234 | c81f452f | Michael Hanselmann | |
235 | c81f452f | Michael Hanselmann | # Call actual request handler
|
236 | c81f452f | Michael Hanselmann | result = handler.HandleRequest(handler_context) |
237 | 352e1a26 | Michael Hanselmann | except (http.HttpException, errors.RapiTestResult,
|
238 | 352e1a26 | Michael Hanselmann | KeyboardInterrupt, SystemExit): |
239 | c81f452f | Michael Hanselmann | raise
|
240 | c81f452f | Michael Hanselmann | except Exception, err: |
241 | c81f452f | Michael Hanselmann | logging.exception("Caught exception")
|
242 | c81f452f | Michael Hanselmann | raise http.HttpInternalServerError(message=str(err)) |
243 | c81f452f | Michael Hanselmann | except:
|
244 | c81f452f | Michael Hanselmann | logging.exception("Unknown exception")
|
245 | c81f452f | Michael Hanselmann | raise http.HttpInternalServerError(message="Unknown error") |
246 | c81f452f | Michael Hanselmann | |
247 | c81f452f | Michael Hanselmann | if not isinstance(result, basestring): |
248 | c81f452f | Michael Hanselmann | raise http.HttpError("Handler function didn't return string type") |
249 | c81f452f | Michael Hanselmann | |
250 | c81f452f | Michael Hanselmann | return (http.HTTP_OK, handler_context.resp_headers, result)
|
251 | c81f452f | Michael Hanselmann | finally:
|
252 | c81f452f | Michael Hanselmann | # No reason to keep this any longer, even for exceptions
|
253 | c81f452f | Michael Hanselmann | handler_context.private = None
|
254 | c81f452f | Michael Hanselmann | |
255 | c81f452f | Michael Hanselmann | |
256 | d859a2cf | Michael Hanselmann | class HttpResponder(object): |
257 | d859a2cf | Michael Hanselmann | # The default request version. This only affects responses up until
|
258 | d859a2cf | Michael Hanselmann | # the point where the request line is parsed, so it mainly decides what
|
259 | d859a2cf | Michael Hanselmann | # the client gets back when sending a malformed request line.
|
260 | d859a2cf | Michael Hanselmann | # Most web servers default to HTTP 0.9, i.e. don't send a status line.
|
261 | d859a2cf | Michael Hanselmann | default_request_version = http.HTTP_0_9 |
262 | d859a2cf | Michael Hanselmann | |
263 | d859a2cf | Michael Hanselmann | responses = BaseHTTPServer.BaseHTTPRequestHandler.responses |
264 | d859a2cf | Michael Hanselmann | |
265 | d859a2cf | Michael Hanselmann | def __init__(self, handler): |
266 | d859a2cf | Michael Hanselmann | """Initializes this class.
|
267 | d859a2cf | Michael Hanselmann |
|
268 | d859a2cf | Michael Hanselmann | """
|
269 | d859a2cf | Michael Hanselmann | self._handler = handler
|
270 | d859a2cf | Michael Hanselmann | |
271 | d859a2cf | Michael Hanselmann | def __call__(self, fn): |
272 | d859a2cf | Michael Hanselmann | """Handles a request.
|
273 | d859a2cf | Michael Hanselmann |
|
274 | d859a2cf | Michael Hanselmann | @type fn: callable
|
275 | d859a2cf | Michael Hanselmann | @param fn: Callback for retrieving HTTP request, must return a tuple
|
276 | d859a2cf | Michael Hanselmann | containing request message (L{http.HttpMessage}) and C{None} or the
|
277 | d859a2cf | Michael Hanselmann | message reader (L{_HttpClientToServerMessageReader})
|
278 | d859a2cf | Michael Hanselmann |
|
279 | d859a2cf | Michael Hanselmann | """
|
280 | d859a2cf | Michael Hanselmann | response_msg = http.HttpMessage() |
281 | d859a2cf | Michael Hanselmann | response_msg.start_line = \ |
282 | d859a2cf | Michael Hanselmann | http.HttpServerToClientStartLine(version=self.default_request_version,
|
283 | d859a2cf | Michael Hanselmann | code=None, reason=None) |
284 | d859a2cf | Michael Hanselmann | |
285 | d859a2cf | Michael Hanselmann | force_close = True
|
286 | d859a2cf | Michael Hanselmann | |
287 | d859a2cf | Michael Hanselmann | try:
|
288 | d859a2cf | Michael Hanselmann | (request_msg, req_msg_reader) = fn() |
289 | d859a2cf | Michael Hanselmann | |
290 | d859a2cf | Michael Hanselmann | response_msg.start_line.version = request_msg.start_line.version |
291 | d859a2cf | Michael Hanselmann | |
292 | d859a2cf | Michael Hanselmann | # RFC2616, 14.23: All Internet-based HTTP/1.1 servers MUST respond
|
293 | d859a2cf | Michael Hanselmann | # with a 400 (Bad Request) status code to any HTTP/1.1 request
|
294 | d859a2cf | Michael Hanselmann | # message which lacks a Host header field.
|
295 | d859a2cf | Michael Hanselmann | if (request_msg.start_line.version == http.HTTP_1_1 and |
296 | d859a2cf | Michael Hanselmann | not (request_msg.headers and |
297 | d859a2cf | Michael Hanselmann | http.HTTP_HOST in request_msg.headers)):
|
298 | d859a2cf | Michael Hanselmann | raise http.HttpBadRequest(message="Missing Host header") |
299 | d859a2cf | Michael Hanselmann | |
300 | d859a2cf | Michael Hanselmann | (response_msg.start_line.code, response_msg.headers, |
301 | d859a2cf | Michael Hanselmann | response_msg.body) = \ |
302 | d859a2cf | Michael Hanselmann | _HandleServerRequestInner(self._handler, request_msg)
|
303 | d859a2cf | Michael Hanselmann | except http.HttpException, err:
|
304 | d859a2cf | Michael Hanselmann | self._SetError(self.responses, self._handler, response_msg, err) |
305 | d859a2cf | Michael Hanselmann | else:
|
306 | d859a2cf | Michael Hanselmann | # Only wait for client to close if we didn't have any exception.
|
307 | d859a2cf | Michael Hanselmann | force_close = False
|
308 | d859a2cf | Michael Hanselmann | |
309 | d859a2cf | Michael Hanselmann | return (request_msg, req_msg_reader, force_close,
|
310 | d859a2cf | Michael Hanselmann | self._Finalize(self.responses, response_msg)) |
311 | d859a2cf | Michael Hanselmann | |
312 | d859a2cf | Michael Hanselmann | @staticmethod
|
313 | d859a2cf | Michael Hanselmann | def _SetError(responses, handler, response_msg, err): |
314 | d859a2cf | Michael Hanselmann | """Sets the response code and body from a HttpException.
|
315 | d859a2cf | Michael Hanselmann |
|
316 | d859a2cf | Michael Hanselmann | @type err: HttpException
|
317 | d859a2cf | Michael Hanselmann | @param err: Exception instance
|
318 | d859a2cf | Michael Hanselmann |
|
319 | d859a2cf | Michael Hanselmann | """
|
320 | d859a2cf | Michael Hanselmann | try:
|
321 | d859a2cf | Michael Hanselmann | (shortmsg, longmsg) = responses[err.code] |
322 | d859a2cf | Michael Hanselmann | except KeyError: |
323 | d859a2cf | Michael Hanselmann | shortmsg = longmsg = "Unknown"
|
324 | d859a2cf | Michael Hanselmann | |
325 | d859a2cf | Michael Hanselmann | if err.message:
|
326 | d859a2cf | Michael Hanselmann | message = err.message |
327 | d859a2cf | Michael Hanselmann | else:
|
328 | d859a2cf | Michael Hanselmann | message = shortmsg |
329 | d859a2cf | Michael Hanselmann | |
330 | d859a2cf | Michael Hanselmann | values = { |
331 | d859a2cf | Michael Hanselmann | "code": err.code,
|
332 | d859a2cf | Michael Hanselmann | "message": cgi.escape(message),
|
333 | d859a2cf | Michael Hanselmann | "explain": longmsg,
|
334 | d859a2cf | Michael Hanselmann | } |
335 | d859a2cf | Michael Hanselmann | |
336 | d859a2cf | Michael Hanselmann | (content_type, body) = handler.FormatErrorMessage(values) |
337 | d859a2cf | Michael Hanselmann | |
338 | d859a2cf | Michael Hanselmann | headers = { |
339 | d859a2cf | Michael Hanselmann | http.HTTP_CONTENT_TYPE: content_type, |
340 | d859a2cf | Michael Hanselmann | } |
341 | d859a2cf | Michael Hanselmann | |
342 | d859a2cf | Michael Hanselmann | if err.headers:
|
343 | d859a2cf | Michael Hanselmann | headers.update(err.headers) |
344 | d859a2cf | Michael Hanselmann | |
345 | d859a2cf | Michael Hanselmann | response_msg.start_line.code = err.code |
346 | d859a2cf | Michael Hanselmann | response_msg.headers = headers |
347 | d859a2cf | Michael Hanselmann | response_msg.body = body |
348 | d859a2cf | Michael Hanselmann | |
349 | d859a2cf | Michael Hanselmann | @staticmethod
|
350 | d859a2cf | Michael Hanselmann | def _Finalize(responses, msg): |
351 | d859a2cf | Michael Hanselmann | assert msg.start_line.reason is None |
352 | d859a2cf | Michael Hanselmann | |
353 | d859a2cf | Michael Hanselmann | if not msg.headers: |
354 | d859a2cf | Michael Hanselmann | msg.headers = {} |
355 | d859a2cf | Michael Hanselmann | |
356 | d859a2cf | Michael Hanselmann | msg.headers.update({ |
357 | d859a2cf | Michael Hanselmann | # TODO: Keep-alive is not supported
|
358 | d859a2cf | Michael Hanselmann | http.HTTP_CONNECTION: "close",
|
359 | d859a2cf | Michael Hanselmann | http.HTTP_DATE: _DateTimeHeader(), |
360 | d859a2cf | Michael Hanselmann | http.HTTP_SERVER: http.HTTP_GANETI_VERSION, |
361 | d859a2cf | Michael Hanselmann | }) |
362 | d859a2cf | Michael Hanselmann | |
363 | d859a2cf | Michael Hanselmann | # Get response reason based on code
|
364 | d859a2cf | Michael Hanselmann | try:
|
365 | d859a2cf | Michael Hanselmann | code_desc = responses[msg.start_line.code] |
366 | d859a2cf | Michael Hanselmann | except KeyError: |
367 | d859a2cf | Michael Hanselmann | reason = ""
|
368 | d859a2cf | Michael Hanselmann | else:
|
369 | d859a2cf | Michael Hanselmann | (reason, _) = code_desc |
370 | d859a2cf | Michael Hanselmann | |
371 | d859a2cf | Michael Hanselmann | msg.start_line.reason = reason |
372 | d859a2cf | Michael Hanselmann | |
373 | d859a2cf | Michael Hanselmann | return msg
|
374 | d859a2cf | Michael Hanselmann | |
375 | d859a2cf | Michael Hanselmann | |
376 | 1f8588f6 | Iustin Pop | class HttpServerRequestExecutor(object): |
377 | 02cab3e7 | Michael Hanselmann | """Implements server side of HTTP.
|
378 | 02cab3e7 | Michael Hanselmann |
|
379 | 25e7b43f | Iustin Pop | This class implements the server side of HTTP. It's based on code of
|
380 | 25e7b43f | Iustin Pop | Python's BaseHTTPServer, from both version 2.4 and 3k. It does not
|
381 | 25e7b43f | Iustin Pop | support non-ASCII character encodings. Keep-alive connections are
|
382 | 25e7b43f | Iustin Pop | not supported.
|
383 | 02cab3e7 | Michael Hanselmann |
|
384 | 02cab3e7 | Michael Hanselmann | """
|
385 | 02cab3e7 | Michael Hanselmann | # Timeouts in seconds for socket layer
|
386 | 02cab3e7 | Michael Hanselmann | WRITE_TIMEOUT = 10
|
387 | 02cab3e7 | Michael Hanselmann | READ_TIMEOUT = 10
|
388 | 02cab3e7 | Michael Hanselmann | CLOSE_TIMEOUT = 1
|
389 | 02cab3e7 | Michael Hanselmann | |
390 | e0003509 | Michael Hanselmann | def __init__(self, server, handler, sock, client_addr): |
391 | 02cab3e7 | Michael Hanselmann | """Initializes this class.
|
392 | 02cab3e7 | Michael Hanselmann |
|
393 | 02cab3e7 | Michael Hanselmann | """
|
394 | d859a2cf | Michael Hanselmann | responder = HttpResponder(handler) |
395 | 02cab3e7 | Michael Hanselmann | |
396 | 02cab3e7 | Michael Hanselmann | # Disable Python's timeout
|
397 | d859a2cf | Michael Hanselmann | sock.settimeout(None)
|
398 | 02cab3e7 | Michael Hanselmann | |
399 | 02cab3e7 | Michael Hanselmann | # Operate in non-blocking mode
|
400 | d859a2cf | Michael Hanselmann | sock.setblocking(0)
|
401 | d859a2cf | Michael Hanselmann | |
402 | d859a2cf | Michael Hanselmann | request_msg_reader = None
|
403 | d859a2cf | Michael Hanselmann | force_close = True
|
404 | 02cab3e7 | Michael Hanselmann | |
405 | 14d57a8b | Iustin Pop | logging.debug("Connection from %s:%s", client_addr[0], client_addr[1]) |
406 | 02cab3e7 | Michael Hanselmann | try:
|
407 | d859a2cf | Michael Hanselmann | # Block for closing connection
|
408 | 02cab3e7 | Michael Hanselmann | try:
|
409 | f2e13d55 | Michael Hanselmann | # Do the secret SSL handshake
|
410 | d859a2cf | Michael Hanselmann | if server.using_ssl:
|
411 | d859a2cf | Michael Hanselmann | sock.set_accept_state() |
412 | f2e13d55 | Michael Hanselmann | try:
|
413 | d859a2cf | Michael Hanselmann | http.Handshake(sock, self.WRITE_TIMEOUT)
|
414 | f2e13d55 | Michael Hanselmann | except http.HttpSessionHandshakeUnexpectedEOF:
|
415 | f2e13d55 | Michael Hanselmann | # Ignore rest
|
416 | f2e13d55 | Michael Hanselmann | return
|
417 | f2e13d55 | Michael Hanselmann | |
418 | d859a2cf | Michael Hanselmann | (request_msg, request_msg_reader, force_close, response_msg) = \ |
419 | d859a2cf | Michael Hanselmann | responder(compat.partial(self._ReadRequest, sock, self.READ_TIMEOUT)) |
420 | d859a2cf | Michael Hanselmann | if response_msg:
|
421 | d859a2cf | Michael Hanselmann | # HttpMessage.start_line can be of different types
|
422 | 57a6042e | Guido Trotter | # Instance of 'HttpClientToServerStartLine' has no 'code' member
|
423 | 57a6042e | Guido Trotter | # pylint: disable=E1103,E1101
|
424 | d859a2cf | Michael Hanselmann | logging.info("%s:%s %s %s", client_addr[0], client_addr[1], |
425 | d859a2cf | Michael Hanselmann | request_msg.start_line, response_msg.start_line.code) |
426 | d859a2cf | Michael Hanselmann | self._SendResponse(sock, request_msg, response_msg,
|
427 | d859a2cf | Michael Hanselmann | self.WRITE_TIMEOUT)
|
428 | 02cab3e7 | Michael Hanselmann | finally:
|
429 | aea0ed67 | Michael Hanselmann | http.ShutdownConnection(sock, self.CLOSE_TIMEOUT, self.WRITE_TIMEOUT, |
430 | 02cab3e7 | Michael Hanselmann | request_msg_reader, force_close) |
431 | 02cab3e7 | Michael Hanselmann | |
432 | d859a2cf | Michael Hanselmann | sock.close() |
433 | 02cab3e7 | Michael Hanselmann | finally:
|
434 | 14d57a8b | Iustin Pop | logging.debug("Disconnected %s:%s", client_addr[0], client_addr[1]) |
435 | 02cab3e7 | Michael Hanselmann | |
436 | d859a2cf | Michael Hanselmann | @staticmethod
|
437 | d859a2cf | Michael Hanselmann | def _ReadRequest(sock, timeout): |
438 | 02cab3e7 | Michael Hanselmann | """Reads a request sent by client.
|
439 | 02cab3e7 | Michael Hanselmann |
|
440 | 02cab3e7 | Michael Hanselmann | """
|
441 | d859a2cf | Michael Hanselmann | msg = http.HttpMessage() |
442 | d859a2cf | Michael Hanselmann | |
443 | 02cab3e7 | Michael Hanselmann | try:
|
444 | d859a2cf | Michael Hanselmann | reader = _HttpClientToServerMessageReader(sock, msg, timeout) |
445 | 02cab3e7 | Michael Hanselmann | except http.HttpSocketTimeout:
|
446 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Timeout while reading request") |
447 | 02cab3e7 | Michael Hanselmann | except socket.error, err:
|
448 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Error reading request: %s" % err) |
449 | 02cab3e7 | Michael Hanselmann | |
450 | d859a2cf | Michael Hanselmann | return (msg, reader)
|
451 | 02cab3e7 | Michael Hanselmann | |
452 | d859a2cf | Michael Hanselmann | @staticmethod
|
453 | d859a2cf | Michael Hanselmann | def _SendResponse(sock, req_msg, msg, timeout): |
454 | 02cab3e7 | Michael Hanselmann | """Sends the response to the client.
|
455 | 02cab3e7 | Michael Hanselmann |
|
456 | 02cab3e7 | Michael Hanselmann | """
|
457 | 02cab3e7 | Michael Hanselmann | try:
|
458 | d859a2cf | Michael Hanselmann | _HttpServerToClientMessageWriter(sock, req_msg, msg, timeout) |
459 | 02cab3e7 | Michael Hanselmann | except http.HttpSocketTimeout:
|
460 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Timeout while sending response") |
461 | 02cab3e7 | Michael Hanselmann | except socket.error, err:
|
462 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Error sending response: %s" % err) |
463 | 02cab3e7 | Michael Hanselmann | |
464 | 57fd6d0b | Michael Hanselmann | |
465 | 112d240d | Guido Trotter | class HttpServer(http.HttpBase, asyncore.dispatcher): |
466 | 02cab3e7 | Michael Hanselmann | """Generic HTTP server class
|
467 | 02cab3e7 | Michael Hanselmann |
|
468 | 02cab3e7 | Michael Hanselmann | """
|
469 | 02cab3e7 | Michael Hanselmann | MAX_CHILDREN = 20
|
470 | 02cab3e7 | Michael Hanselmann | |
471 | e0003509 | Michael Hanselmann | def __init__(self, mainloop, local_address, port, handler, |
472 | 1f8588f6 | Iustin Pop | ssl_params=None, ssl_verify_peer=False, |
473 | 1f8588f6 | Iustin Pop | request_executor_class=None):
|
474 | 02cab3e7 | Michael Hanselmann | """Initializes the HTTP server
|
475 | 02cab3e7 | Michael Hanselmann |
|
476 | 02cab3e7 | Michael Hanselmann | @type mainloop: ganeti.daemon.Mainloop
|
477 | 02cab3e7 | Michael Hanselmann | @param mainloop: Mainloop used to poll for I/O events
|
478 | c41eea6e | Iustin Pop | @type local_address: string
|
479 | 02cab3e7 | Michael Hanselmann | @param local_address: Local IP address to bind to
|
480 | 02cab3e7 | Michael Hanselmann | @type port: int
|
481 | 02cab3e7 | Michael Hanselmann | @param port: TCP port to listen on
|
482 | 02cab3e7 | Michael Hanselmann | @type ssl_params: HttpSslParams
|
483 | 02cab3e7 | Michael Hanselmann | @param ssl_params: SSL key and certificate
|
484 | 02cab3e7 | Michael Hanselmann | @type ssl_verify_peer: bool
|
485 | 25e7b43f | Iustin Pop | @param ssl_verify_peer: Whether to require client certificate
|
486 | 25e7b43f | Iustin Pop | and compare it with our certificate
|
487 | 1f8588f6 | Iustin Pop | @type request_executor_class: class
|
488 | 1f8588f6 | Iustin Pop | @param request_executor_class: an class derived from the
|
489 | 1f8588f6 | Iustin Pop | HttpServerRequestExecutor class
|
490 | 02cab3e7 | Michael Hanselmann |
|
491 | 02cab3e7 | Michael Hanselmann | """
|
492 | f4322a1e | Michael Hanselmann | http.HttpBase.__init__(self)
|
493 | 112d240d | Guido Trotter | asyncore.dispatcher.__init__(self)
|
494 | 02cab3e7 | Michael Hanselmann | |
495 | 1f8588f6 | Iustin Pop | if request_executor_class is None: |
496 | 1f8588f6 | Iustin Pop | self.request_executor = HttpServerRequestExecutor
|
497 | 1f8588f6 | Iustin Pop | else:
|
498 | 1f8588f6 | Iustin Pop | self.request_executor = request_executor_class
|
499 | 1f8588f6 | Iustin Pop | |
500 | 02cab3e7 | Michael Hanselmann | self.mainloop = mainloop
|
501 | 02cab3e7 | Michael Hanselmann | self.local_address = local_address
|
502 | 02cab3e7 | Michael Hanselmann | self.port = port
|
503 | e0003509 | Michael Hanselmann | self.handler = handler
|
504 | db4e138b | Manuel Franceschini | family = netutils.IPAddress.GetAddressFamily(local_address) |
505 | db4e138b | Manuel Franceschini | self.socket = self._CreateSocket(ssl_params, ssl_verify_peer, family) |
506 | 02cab3e7 | Michael Hanselmann | |
507 | 02cab3e7 | Michael Hanselmann | # Allow port to be reused
|
508 | 02cab3e7 | Michael Hanselmann | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
509 | 02cab3e7 | Michael Hanselmann | |
510 | 02cab3e7 | Michael Hanselmann | self._children = []
|
511 | 112d240d | Guido Trotter | self.set_socket(self.socket) |
512 | 112d240d | Guido Trotter | self.accepting = True |
513 | 02cab3e7 | Michael Hanselmann | mainloop.RegisterSignal(self)
|
514 | 02cab3e7 | Michael Hanselmann | |
515 | 02cab3e7 | Michael Hanselmann | def Start(self): |
516 | 02cab3e7 | Michael Hanselmann | self.socket.bind((self.local_address, self.port)) |
517 | 59305197 | Michael Hanselmann | self.socket.listen(1024) |
518 | 02cab3e7 | Michael Hanselmann | |
519 | 02cab3e7 | Michael Hanselmann | def Stop(self): |
520 | 02cab3e7 | Michael Hanselmann | self.socket.close()
|
521 | 02cab3e7 | Michael Hanselmann | |
522 | 112d240d | Guido Trotter | def handle_accept(self): |
523 | 112d240d | Guido Trotter | self._IncomingConnection()
|
524 | 02cab3e7 | Michael Hanselmann | |
525 | 02cab3e7 | Michael Hanselmann | def OnSignal(self, signum): |
526 | 02cab3e7 | Michael Hanselmann | if signum == signal.SIGCHLD:
|
527 | 02cab3e7 | Michael Hanselmann | self._CollectChildren(True) |
528 | 02cab3e7 | Michael Hanselmann | |
529 | 02cab3e7 | Michael Hanselmann | def _CollectChildren(self, quick): |
530 | 02cab3e7 | Michael Hanselmann | """Checks whether any child processes are done
|
531 | 02cab3e7 | Michael Hanselmann |
|
532 | 02cab3e7 | Michael Hanselmann | @type quick: bool
|
533 | 02cab3e7 | Michael Hanselmann | @param quick: Whether to only use non-blocking functions
|
534 | 02cab3e7 | Michael Hanselmann |
|
535 | 02cab3e7 | Michael Hanselmann | """
|
536 | 02cab3e7 | Michael Hanselmann | if not quick: |
537 | 02cab3e7 | Michael Hanselmann | # Don't wait for other processes if it should be a quick check
|
538 | 02cab3e7 | Michael Hanselmann | while len(self._children) > self.MAX_CHILDREN: |
539 | 02cab3e7 | Michael Hanselmann | try:
|
540 | 02cab3e7 | Michael Hanselmann | # Waiting without a timeout brings us into a potential DoS situation.
|
541 | 02cab3e7 | Michael Hanselmann | # As soon as too many children run, we'll not respond to new
|
542 | 02cab3e7 | Michael Hanselmann | # requests. The real solution would be to add a timeout for children
|
543 | 02cab3e7 | Michael Hanselmann | # and killing them after some time.
|
544 | 7c4d6c7b | Michael Hanselmann | pid, _ = os.waitpid(0, 0) |
545 | 02cab3e7 | Michael Hanselmann | except os.error:
|
546 | 02cab3e7 | Michael Hanselmann | pid = None
|
547 | 02cab3e7 | Michael Hanselmann | if pid and pid in self._children: |
548 | 02cab3e7 | Michael Hanselmann | self._children.remove(pid)
|
549 | 02cab3e7 | Michael Hanselmann | |
550 | 02cab3e7 | Michael Hanselmann | for child in self._children: |
551 | 02cab3e7 | Michael Hanselmann | try:
|
552 | 1122eb25 | Iustin Pop | pid, _ = os.waitpid(child, os.WNOHANG) |
553 | 02cab3e7 | Michael Hanselmann | except os.error:
|
554 | 02cab3e7 | Michael Hanselmann | pid = None
|
555 | 02cab3e7 | Michael Hanselmann | if pid and pid in self._children: |
556 | 02cab3e7 | Michael Hanselmann | self._children.remove(pid)
|
557 | 02cab3e7 | Michael Hanselmann | |
558 | 02cab3e7 | Michael Hanselmann | def _IncomingConnection(self): |
559 | 02cab3e7 | Michael Hanselmann | """Called for each incoming connection
|
560 | 02cab3e7 | Michael Hanselmann |
|
561 | 02cab3e7 | Michael Hanselmann | """
|
562 | b459a848 | Andrea Spadaccini | # pylint: disable=W0212
|
563 | 02cab3e7 | Michael Hanselmann | (connection, client_addr) = self.socket.accept()
|
564 | 02cab3e7 | Michael Hanselmann | |
565 | 02cab3e7 | Michael Hanselmann | self._CollectChildren(False) |
566 | 02cab3e7 | Michael Hanselmann | |
567 | 02cab3e7 | Michael Hanselmann | pid = os.fork() |
568 | 02cab3e7 | Michael Hanselmann | if pid == 0: |
569 | 02cab3e7 | Michael Hanselmann | # Child process
|
570 | 02cab3e7 | Michael Hanselmann | try:
|
571 | bcb1a39e | Michael Hanselmann | # The client shouldn't keep the listening socket open. If the parent
|
572 | bcb1a39e | Michael Hanselmann | # process is restarted, it would fail when there's already something
|
573 | bcb1a39e | Michael Hanselmann | # listening (in this case its own child from a previous run) on the
|
574 | bcb1a39e | Michael Hanselmann | # same port.
|
575 | bcb1a39e | Michael Hanselmann | try:
|
576 | bcb1a39e | Michael Hanselmann | self.socket.close()
|
577 | bcb1a39e | Michael Hanselmann | except socket.error:
|
578 | bcb1a39e | Michael Hanselmann | pass
|
579 | bcb1a39e | Michael Hanselmann | self.socket = None |
580 | bcb1a39e | Michael Hanselmann | |
581 | 82869978 | Michael Hanselmann | # In case the handler code uses temporary files
|
582 | 82869978 | Michael Hanselmann | utils.ResetTempfileModule() |
583 | 82869978 | Michael Hanselmann | |
584 | e0003509 | Michael Hanselmann | self.request_executor(self, self.handler, connection, client_addr) |
585 | b459a848 | Andrea Spadaccini | except Exception: # pylint: disable=W0703 |
586 | 02cab3e7 | Michael Hanselmann | logging.exception("Error while handling request from %s:%s",
|
587 | 02cab3e7 | Michael Hanselmann | client_addr[0], client_addr[1]) |
588 | 02cab3e7 | Michael Hanselmann | os._exit(1)
|
589 | 02cab3e7 | Michael Hanselmann | os._exit(0)
|
590 | 02cab3e7 | Michael Hanselmann | else:
|
591 | 02cab3e7 | Michael Hanselmann | self._children.append(pid)
|
592 | 02cab3e7 | Michael Hanselmann | |
593 | e0003509 | Michael Hanselmann | |
594 | e0003509 | Michael Hanselmann | class HttpServerHandler(object): |
595 | e0003509 | Michael Hanselmann | """Base class for handling HTTP server requests.
|
596 | e0003509 | Michael Hanselmann |
|
597 | e0003509 | Michael Hanselmann | Users of this class must subclass it and override the L{HandleRequest}
|
598 | e0003509 | Michael Hanselmann | function.
|
599 | e0003509 | Michael Hanselmann |
|
600 | e0003509 | Michael Hanselmann | """
|
601 | f8bd7df3 | Michael Hanselmann | def PreHandleRequest(self, req): |
602 | f8bd7df3 | Michael Hanselmann | """Called before handling a request.
|
603 | f8bd7df3 | Michael Hanselmann |
|
604 | 5bbd3f7f | Michael Hanselmann | Can be overridden by a subclass.
|
605 | f8bd7df3 | Michael Hanselmann |
|
606 | f8bd7df3 | Michael Hanselmann | """
|
607 | f8bd7df3 | Michael Hanselmann | |
608 | 02cab3e7 | Michael Hanselmann | def HandleRequest(self, req): |
609 | 02cab3e7 | Michael Hanselmann | """Handles a request.
|
610 | 02cab3e7 | Michael Hanselmann |
|
611 | 5bbd3f7f | Michael Hanselmann | Must be overridden by subclass.
|
612 | 02cab3e7 | Michael Hanselmann |
|
613 | 02cab3e7 | Michael Hanselmann | """
|
614 | 02cab3e7 | Michael Hanselmann | raise NotImplementedError() |
615 | 377ae13e | Michael Hanselmann | |
616 | 377ae13e | Michael Hanselmann | @staticmethod
|
617 | 377ae13e | Michael Hanselmann | def FormatErrorMessage(values): |
618 | 377ae13e | Michael Hanselmann | """Formats the body of an error message.
|
619 | 377ae13e | Michael Hanselmann |
|
620 | 377ae13e | Michael Hanselmann | @type values: dict
|
621 | 377ae13e | Michael Hanselmann | @param values: dictionary with keys C{code}, C{message} and C{explain}.
|
622 | 377ae13e | Michael Hanselmann | @rtype: tuple; (string, string)
|
623 | 377ae13e | Michael Hanselmann | @return: Content-type and response body
|
624 | 377ae13e | Michael Hanselmann |
|
625 | 377ae13e | Michael Hanselmann | """
|
626 | 377ae13e | Michael Hanselmann | return (DEFAULT_ERROR_CONTENT_TYPE, DEFAULT_ERROR_MESSAGE % values) |