root / lib / http / server.py @ f2e13d55
History | View | Annotate | Download (15 kB)
1 | 02cab3e7 | Michael Hanselmann | #
|
---|---|---|---|
2 | 02cab3e7 | Michael Hanselmann | #
|
3 | 02cab3e7 | Michael Hanselmann | |
4 | 02cab3e7 | Michael Hanselmann | # Copyright (C) 2007, 2008 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 select |
30 | 02cab3e7 | Michael Hanselmann | import socket |
31 | 02cab3e7 | Michael Hanselmann | import time |
32 | 02cab3e7 | Michael Hanselmann | import signal |
33 | 02cab3e7 | Michael Hanselmann | |
34 | 02cab3e7 | Michael Hanselmann | from ganeti import constants |
35 | 02cab3e7 | Michael Hanselmann | from ganeti import serializer |
36 | 02cab3e7 | Michael Hanselmann | from ganeti import utils |
37 | 02cab3e7 | Michael Hanselmann | from ganeti import http |
38 | 02cab3e7 | Michael Hanselmann | |
39 | 02cab3e7 | Michael Hanselmann | |
40 | 02cab3e7 | Michael Hanselmann | WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] |
41 | 02cab3e7 | Michael Hanselmann | MONTHNAME = [None,
|
42 | 02cab3e7 | Michael Hanselmann | 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', |
43 | 02cab3e7 | Michael Hanselmann | 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] |
44 | 02cab3e7 | Michael Hanselmann | |
45 | 02cab3e7 | Michael Hanselmann | # Default error message
|
46 | 02cab3e7 | Michael Hanselmann | DEFAULT_ERROR_CONTENT_TYPE = "text/html"
|
47 | 02cab3e7 | Michael Hanselmann | DEFAULT_ERROR_MESSAGE = """\
|
48 | 02cab3e7 | Michael Hanselmann | <html>
|
49 | 02cab3e7 | Michael Hanselmann | <head>
|
50 | 02cab3e7 | Michael Hanselmann | <title>Error response</title>
|
51 | 02cab3e7 | Michael Hanselmann | </head>
|
52 | 02cab3e7 | Michael Hanselmann | <body>
|
53 | 02cab3e7 | Michael Hanselmann | <h1>Error response</h1>
|
54 | 02cab3e7 | Michael Hanselmann | <p>Error code %(code)d.
|
55 | 02cab3e7 | Michael Hanselmann | <p>Message: %(message)s.
|
56 | 02cab3e7 | Michael Hanselmann | <p>Error code explanation: %(code)s = %(explain)s.
|
57 | 02cab3e7 | Michael Hanselmann | </body>
|
58 | 02cab3e7 | Michael Hanselmann | </html>
|
59 | 02cab3e7 | Michael Hanselmann | """
|
60 | 02cab3e7 | Michael Hanselmann | |
61 | 02cab3e7 | Michael Hanselmann | |
62 | 02cab3e7 | Michael Hanselmann | def _DateTimeHeader(): |
63 | 02cab3e7 | Michael Hanselmann | """Return the current date and time formatted for a message header.
|
64 | 02cab3e7 | Michael Hanselmann |
|
65 | 02cab3e7 | Michael Hanselmann | """
|
66 | 02cab3e7 | Michael Hanselmann | (year, month, day, hh, mm, ss, wd, _, _) = time.gmtime() |
67 | 02cab3e7 | Michael Hanselmann | return ("%s, %02d %3s %4d %02d:%02d:%02d GMT" % |
68 | 02cab3e7 | Michael Hanselmann | (WEEKDAYNAME[wd], day, MONTHNAME[month], year, hh, mm, ss)) |
69 | 02cab3e7 | Michael Hanselmann | |
70 | 02cab3e7 | Michael Hanselmann | |
71 | 02cab3e7 | Michael Hanselmann | class _HttpServerRequest(object): |
72 | 02cab3e7 | Michael Hanselmann | """Data structure for HTTP request on server side.
|
73 | 02cab3e7 | Michael Hanselmann |
|
74 | 02cab3e7 | Michael Hanselmann | """
|
75 | 02cab3e7 | Michael Hanselmann | def __init__(self, request_msg): |
76 | 02cab3e7 | Michael Hanselmann | # Request attributes
|
77 | 02cab3e7 | Michael Hanselmann | self.request_method = request_msg.start_line.method
|
78 | 02cab3e7 | Michael Hanselmann | self.request_path = request_msg.start_line.path
|
79 | 02cab3e7 | Michael Hanselmann | self.request_headers = request_msg.headers
|
80 | 02cab3e7 | Michael Hanselmann | self.request_body = request_msg.decoded_body
|
81 | 02cab3e7 | Michael Hanselmann | |
82 | 02cab3e7 | Michael Hanselmann | # Response attributes
|
83 | 02cab3e7 | Michael Hanselmann | self.resp_headers = {}
|
84 | 02cab3e7 | Michael Hanselmann | |
85 | 02cab3e7 | Michael Hanselmann | |
86 | 02cab3e7 | Michael Hanselmann | class _HttpServerToClientMessageWriter(http.HttpMessageWriter): |
87 | 02cab3e7 | Michael Hanselmann | """Writes an HTTP response to client.
|
88 | 02cab3e7 | Michael Hanselmann |
|
89 | 02cab3e7 | Michael Hanselmann | """
|
90 | 02cab3e7 | Michael Hanselmann | def __init__(self, sock, request_msg, response_msg, write_timeout): |
91 | 358a8811 | Michael Hanselmann | """Writes the response to the client.
|
92 | 358a8811 | Michael Hanselmann |
|
93 | 358a8811 | Michael Hanselmann | @type sock: socket
|
94 | 358a8811 | Michael Hanselmann | @param sock: Target socket
|
95 | 358a8811 | Michael Hanselmann | @type request_msg: http.HttpMessage
|
96 | 358a8811 | Michael Hanselmann | @param request_msg: Request message, required to determine whether
|
97 | 358a8811 | Michael Hanselmann | response may have a message body
|
98 | 358a8811 | Michael Hanselmann | @type response_msg: http.HttpMessage
|
99 | 358a8811 | Michael Hanselmann | @param response_msg: Response message
|
100 | 358a8811 | Michael Hanselmann | @type write_timeout: float
|
101 | 358a8811 | Michael Hanselmann | @param write_timeout: Write timeout for socket
|
102 | 02cab3e7 | Michael Hanselmann |
|
103 | 02cab3e7 | Michael Hanselmann | """
|
104 | 02cab3e7 | Michael Hanselmann | self._request_msg = request_msg
|
105 | 02cab3e7 | Michael Hanselmann | self._response_msg = response_msg
|
106 | 02cab3e7 | Michael Hanselmann | http.HttpMessageWriter.__init__(self, sock, response_msg, write_timeout)
|
107 | 02cab3e7 | Michael Hanselmann | |
108 | 02cab3e7 | Michael Hanselmann | def HasMessageBody(self): |
109 | 02cab3e7 | Michael Hanselmann | """Logic to detect whether response should contain a message body.
|
110 | 02cab3e7 | Michael Hanselmann |
|
111 | 02cab3e7 | Michael Hanselmann | """
|
112 | 02cab3e7 | Michael Hanselmann | if self._request_msg.start_line: |
113 | 02cab3e7 | Michael Hanselmann | request_method = self._request_msg.start_line.method
|
114 | 02cab3e7 | Michael Hanselmann | else:
|
115 | 02cab3e7 | Michael Hanselmann | request_method = None
|
116 | 02cab3e7 | Michael Hanselmann | |
117 | 02cab3e7 | Michael Hanselmann | response_code = self._response_msg.start_line.code
|
118 | 02cab3e7 | Michael Hanselmann | |
119 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 4.3: "A message-body MUST NOT be included in a request
|
120 | 02cab3e7 | Michael Hanselmann | # if the specification of the request method (section 5.1.1) does not allow
|
121 | 02cab3e7 | Michael Hanselmann | # sending an entity-body in requests"
|
122 | 02cab3e7 | Michael Hanselmann | #
|
123 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 9.4: "The HEAD method is identical to GET except that
|
124 | 02cab3e7 | Michael Hanselmann | # the server MUST NOT return a message-body in the response."
|
125 | 02cab3e7 | Michael Hanselmann | #
|
126 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 10.2.5: "The 204 response MUST NOT include a
|
127 | 02cab3e7 | Michael Hanselmann | # message-body [...]"
|
128 | 02cab3e7 | Michael Hanselmann | #
|
129 | 02cab3e7 | Michael Hanselmann | # RFC2616, section 10.3.5: "The 304 response MUST NOT contain a
|
130 | 02cab3e7 | Michael Hanselmann | # message-body, [...]"
|
131 | 02cab3e7 | Michael Hanselmann | |
132 | 02cab3e7 | Michael Hanselmann | return (http.HttpMessageWriter.HasMessageBody(self) and |
133 | 3f3dfc15 | Iustin Pop | (request_method is not None and |
134 | 3f3dfc15 | Iustin Pop | request_method != http.HTTP_HEAD) and
|
135 | 02cab3e7 | Michael Hanselmann | response_code >= http.HTTP_OK and
|
136 | 3f3dfc15 | Iustin Pop | response_code not in (http.HTTP_NO_CONTENT, |
137 | 3f3dfc15 | Iustin Pop | http.HTTP_NOT_MODIFIED)) |
138 | 02cab3e7 | Michael Hanselmann | |
139 | 02cab3e7 | Michael Hanselmann | |
140 | 02cab3e7 | Michael Hanselmann | class _HttpClientToServerMessageReader(http.HttpMessageReader): |
141 | 02cab3e7 | Michael Hanselmann | """Reads an HTTP request sent by client.
|
142 | 02cab3e7 | Michael Hanselmann |
|
143 | 02cab3e7 | Michael Hanselmann | """
|
144 | 02cab3e7 | Michael Hanselmann | # Length limits
|
145 | 02cab3e7 | Michael Hanselmann | START_LINE_LENGTH_MAX = 4096
|
146 | 02cab3e7 | Michael Hanselmann | HEADER_LENGTH_MAX = 4096
|
147 | 02cab3e7 | Michael Hanselmann | |
148 | 02cab3e7 | Michael Hanselmann | def ParseStartLine(self, start_line): |
149 | 02cab3e7 | Michael Hanselmann | """Parses the start line sent by client.
|
150 | 02cab3e7 | Michael Hanselmann |
|
151 | 02cab3e7 | Michael Hanselmann | Example: "GET /index.html HTTP/1.1"
|
152 | 02cab3e7 | Michael Hanselmann |
|
153 | 02cab3e7 | Michael Hanselmann | @type start_line: string
|
154 | 02cab3e7 | Michael Hanselmann | @param start_line: Start line
|
155 | 02cab3e7 | Michael Hanselmann |
|
156 | 02cab3e7 | Michael Hanselmann | """
|
157 | 02cab3e7 | Michael Hanselmann | # Empty lines are skipped when reading
|
158 | 02cab3e7 | Michael Hanselmann | assert start_line
|
159 | 02cab3e7 | Michael Hanselmann | |
160 | 02cab3e7 | Michael Hanselmann | logging.debug("HTTP request: %s", start_line)
|
161 | 02cab3e7 | Michael Hanselmann | |
162 | 02cab3e7 | Michael Hanselmann | words = start_line.split() |
163 | 02cab3e7 | Michael Hanselmann | |
164 | 02cab3e7 | Michael Hanselmann | if len(words) == 3: |
165 | 02cab3e7 | Michael Hanselmann | [method, path, version] = words |
166 | 02cab3e7 | Michael Hanselmann | if version[:5] != 'HTTP/': |
167 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request version (%r)" % version) |
168 | 02cab3e7 | Michael Hanselmann | |
169 | 02cab3e7 | Michael Hanselmann | try:
|
170 | 02cab3e7 | Michael Hanselmann | base_version_number = version.split("/", 1)[1] |
171 | 02cab3e7 | Michael Hanselmann | version_number = base_version_number.split(".")
|
172 | 02cab3e7 | Michael Hanselmann | |
173 | 02cab3e7 | Michael Hanselmann | # RFC 2145 section 3.1 says there can be only one "." and
|
174 | 02cab3e7 | Michael Hanselmann | # - major and minor numbers MUST be treated as
|
175 | 02cab3e7 | Michael Hanselmann | # separate integers;
|
176 | 02cab3e7 | Michael Hanselmann | # - HTTP/2.4 is a lower version than HTTP/2.13, which in
|
177 | 02cab3e7 | Michael Hanselmann | # turn is lower than HTTP/12.3;
|
178 | 02cab3e7 | Michael Hanselmann | # - Leading zeros MUST be ignored by recipients.
|
179 | 02cab3e7 | Michael Hanselmann | if len(version_number) != 2: |
180 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request version (%r)" % version) |
181 | 02cab3e7 | Michael Hanselmann | |
182 | 02cab3e7 | Michael Hanselmann | version_number = (int(version_number[0]), int(version_number[1])) |
183 | 02cab3e7 | Michael Hanselmann | except (ValueError, IndexError): |
184 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request version (%r)" % version) |
185 | 02cab3e7 | Michael Hanselmann | |
186 | 02cab3e7 | Michael Hanselmann | if version_number >= (2, 0): |
187 | 02cab3e7 | Michael Hanselmann | raise http.HttpVersionNotSupported("Invalid HTTP Version (%s)" % |
188 | 02cab3e7 | Michael Hanselmann | base_version_number) |
189 | 02cab3e7 | Michael Hanselmann | |
190 | 02cab3e7 | Michael Hanselmann | elif len(words) == 2: |
191 | 02cab3e7 | Michael Hanselmann | version = http.HTTP_0_9 |
192 | 02cab3e7 | Michael Hanselmann | [method, path] = words |
193 | 02cab3e7 | Michael Hanselmann | if method != http.HTTP_GET:
|
194 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad HTTP/0.9 request type (%r)" % method) |
195 | 02cab3e7 | Michael Hanselmann | |
196 | 02cab3e7 | Michael Hanselmann | else:
|
197 | 02cab3e7 | Michael Hanselmann | raise http.HttpBadRequest("Bad request syntax (%r)" % start_line) |
198 | 02cab3e7 | Michael Hanselmann | |
199 | 02cab3e7 | Michael Hanselmann | return http.HttpClientToServerStartLine(method, path, version)
|
200 | 02cab3e7 | Michael Hanselmann | |
201 | 02cab3e7 | Michael Hanselmann | |
202 | 02cab3e7 | Michael Hanselmann | class _HttpServerRequestExecutor(object): |
203 | 02cab3e7 | Michael Hanselmann | """Implements server side of HTTP.
|
204 | 02cab3e7 | Michael Hanselmann |
|
205 | 02cab3e7 | Michael Hanselmann | This class implements the server side of HTTP. It's based on code of Python's
|
206 | 02cab3e7 | Michael Hanselmann | BaseHTTPServer, from both version 2.4 and 3k. It does not support non-ASCII
|
207 | 02cab3e7 | Michael Hanselmann | character encodings. Keep-alive connections are not supported.
|
208 | 02cab3e7 | Michael Hanselmann |
|
209 | 02cab3e7 | Michael Hanselmann | """
|
210 | 02cab3e7 | Michael Hanselmann | # The default request version. This only affects responses up until
|
211 | 02cab3e7 | Michael Hanselmann | # the point where the request line is parsed, so it mainly decides what
|
212 | 02cab3e7 | Michael Hanselmann | # the client gets back when sending a malformed request line.
|
213 | 02cab3e7 | Michael Hanselmann | # Most web servers default to HTTP 0.9, i.e. don't send a status line.
|
214 | 02cab3e7 | Michael Hanselmann | default_request_version = http.HTTP_0_9 |
215 | 02cab3e7 | Michael Hanselmann | |
216 | 02cab3e7 | Michael Hanselmann | # Error message settings
|
217 | 02cab3e7 | Michael Hanselmann | error_message_format = DEFAULT_ERROR_MESSAGE |
218 | 02cab3e7 | Michael Hanselmann | error_content_type = DEFAULT_ERROR_CONTENT_TYPE |
219 | 02cab3e7 | Michael Hanselmann | |
220 | 02cab3e7 | Michael Hanselmann | responses = BaseHTTPServer.BaseHTTPRequestHandler.responses |
221 | 02cab3e7 | Michael Hanselmann | |
222 | 02cab3e7 | Michael Hanselmann | # Timeouts in seconds for socket layer
|
223 | 02cab3e7 | Michael Hanselmann | WRITE_TIMEOUT = 10
|
224 | 02cab3e7 | Michael Hanselmann | READ_TIMEOUT = 10
|
225 | 02cab3e7 | Michael Hanselmann | CLOSE_TIMEOUT = 1
|
226 | 02cab3e7 | Michael Hanselmann | |
227 | 02cab3e7 | Michael Hanselmann | def __init__(self, server, sock, client_addr): |
228 | 02cab3e7 | Michael Hanselmann | """Initializes this class.
|
229 | 02cab3e7 | Michael Hanselmann |
|
230 | 02cab3e7 | Michael Hanselmann | """
|
231 | 02cab3e7 | Michael Hanselmann | self.server = server
|
232 | 02cab3e7 | Michael Hanselmann | self.sock = sock
|
233 | 02cab3e7 | Michael Hanselmann | self.client_addr = client_addr
|
234 | 02cab3e7 | Michael Hanselmann | |
235 | 02cab3e7 | Michael Hanselmann | self.poller = select.poll()
|
236 | 02cab3e7 | Michael Hanselmann | |
237 | 02cab3e7 | Michael Hanselmann | self.request_msg = http.HttpMessage()
|
238 | 02cab3e7 | Michael Hanselmann | self.response_msg = http.HttpMessage()
|
239 | 02cab3e7 | Michael Hanselmann | |
240 | 02cab3e7 | Michael Hanselmann | self.response_msg.start_line = \
|
241 | 02cab3e7 | Michael Hanselmann | http.HttpServerToClientStartLine(version=self.default_request_version,
|
242 | 02cab3e7 | Michael Hanselmann | code=None, reason=None) |
243 | 02cab3e7 | Michael Hanselmann | |
244 | 02cab3e7 | Michael Hanselmann | # Disable Python's timeout
|
245 | 02cab3e7 | Michael Hanselmann | self.sock.settimeout(None) |
246 | 02cab3e7 | Michael Hanselmann | |
247 | 02cab3e7 | Michael Hanselmann | # Operate in non-blocking mode
|
248 | 02cab3e7 | Michael Hanselmann | self.sock.setblocking(0) |
249 | 02cab3e7 | Michael Hanselmann | |
250 | 02cab3e7 | Michael Hanselmann | logging.info("Connection from %s:%s", client_addr[0], client_addr[1]) |
251 | 02cab3e7 | Michael Hanselmann | try:
|
252 | 02cab3e7 | Michael Hanselmann | request_msg_reader = None
|
253 | 02cab3e7 | Michael Hanselmann | force_close = True
|
254 | 02cab3e7 | Michael Hanselmann | try:
|
255 | f2e13d55 | Michael Hanselmann | # Do the secret SSL handshake
|
256 | f2e13d55 | Michael Hanselmann | if self.server.using_ssl: |
257 | f2e13d55 | Michael Hanselmann | self.sock.set_accept_state()
|
258 | f2e13d55 | Michael Hanselmann | try:
|
259 | f2e13d55 | Michael Hanselmann | http.Handshake(self.poller, self.sock, self.WRITE_TIMEOUT) |
260 | f2e13d55 | Michael Hanselmann | except http.HttpSessionHandshakeUnexpectedEOF:
|
261 | f2e13d55 | Michael Hanselmann | # Ignore rest
|
262 | f2e13d55 | Michael Hanselmann | return
|
263 | f2e13d55 | Michael Hanselmann | |
264 | 02cab3e7 | Michael Hanselmann | try:
|
265 | 02cab3e7 | Michael Hanselmann | try:
|
266 | 02cab3e7 | Michael Hanselmann | request_msg_reader = self._ReadRequest()
|
267 | 02cab3e7 | Michael Hanselmann | self._HandleRequest()
|
268 | 02cab3e7 | Michael Hanselmann | |
269 | 02cab3e7 | Michael Hanselmann | # Only wait for client to close if we didn't have any exception.
|
270 | 02cab3e7 | Michael Hanselmann | force_close = False
|
271 | 02cab3e7 | Michael Hanselmann | except http.HttpException, err:
|
272 | 02cab3e7 | Michael Hanselmann | self._SetErrorStatus(err)
|
273 | 02cab3e7 | Michael Hanselmann | finally:
|
274 | 02cab3e7 | Michael Hanselmann | # Try to send a response
|
275 | 02cab3e7 | Michael Hanselmann | self._SendResponse()
|
276 | 02cab3e7 | Michael Hanselmann | finally:
|
277 | 02cab3e7 | Michael Hanselmann | http.ShutdownConnection(self.poller, sock,
|
278 | 02cab3e7 | Michael Hanselmann | self.CLOSE_TIMEOUT, self.WRITE_TIMEOUT, |
279 | 02cab3e7 | Michael Hanselmann | request_msg_reader, force_close) |
280 | 02cab3e7 | Michael Hanselmann | |
281 | 02cab3e7 | Michael Hanselmann | self.sock.close()
|
282 | 02cab3e7 | Michael Hanselmann | self.sock = None |
283 | 02cab3e7 | Michael Hanselmann | finally:
|
284 | 02cab3e7 | Michael Hanselmann | logging.info("Disconnected %s:%s", client_addr[0], client_addr[1]) |
285 | 02cab3e7 | Michael Hanselmann | |
286 | 02cab3e7 | Michael Hanselmann | def _ReadRequest(self): |
287 | 02cab3e7 | Michael Hanselmann | """Reads a request sent by client.
|
288 | 02cab3e7 | Michael Hanselmann |
|
289 | 02cab3e7 | Michael Hanselmann | """
|
290 | 02cab3e7 | Michael Hanselmann | try:
|
291 | 02cab3e7 | Michael Hanselmann | request_msg_reader = \ |
292 | 02cab3e7 | Michael Hanselmann | _HttpClientToServerMessageReader(self.sock, self.request_msg, |
293 | 02cab3e7 | Michael Hanselmann | self.READ_TIMEOUT)
|
294 | 02cab3e7 | Michael Hanselmann | except http.HttpSocketTimeout:
|
295 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Timeout while reading request") |
296 | 02cab3e7 | Michael Hanselmann | except socket.error, err:
|
297 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Error reading request: %s" % err) |
298 | 02cab3e7 | Michael Hanselmann | |
299 | 02cab3e7 | Michael Hanselmann | self.response_msg.start_line.version = self.request_msg.start_line.version |
300 | 02cab3e7 | Michael Hanselmann | |
301 | 02cab3e7 | Michael Hanselmann | return request_msg_reader
|
302 | 02cab3e7 | Michael Hanselmann | |
303 | 02cab3e7 | Michael Hanselmann | def _HandleRequest(self): |
304 | 02cab3e7 | Michael Hanselmann | """Calls the handler function for the current request.
|
305 | 02cab3e7 | Michael Hanselmann |
|
306 | 02cab3e7 | Michael Hanselmann | """
|
307 | 02cab3e7 | Michael Hanselmann | handler_context = _HttpServerRequest(self.request_msg)
|
308 | 02cab3e7 | Michael Hanselmann | |
309 | 02cab3e7 | Michael Hanselmann | try:
|
310 | 02cab3e7 | Michael Hanselmann | result = self.server.HandleRequest(handler_context)
|
311 | 02cab3e7 | Michael Hanselmann | except (http.HttpException, KeyboardInterrupt, SystemExit): |
312 | 02cab3e7 | Michael Hanselmann | raise
|
313 | 02cab3e7 | Michael Hanselmann | except Exception, err: |
314 | 02cab3e7 | Michael Hanselmann | logging.exception("Caught exception")
|
315 | 02cab3e7 | Michael Hanselmann | raise http.HttpInternalError(message=str(err)) |
316 | 02cab3e7 | Michael Hanselmann | except:
|
317 | 02cab3e7 | Michael Hanselmann | logging.exception("Unknown exception")
|
318 | 02cab3e7 | Michael Hanselmann | raise http.HttpInternalError(message="Unknown error") |
319 | 02cab3e7 | Michael Hanselmann | |
320 | 02cab3e7 | Michael Hanselmann | # TODO: Content-type
|
321 | 02cab3e7 | Michael Hanselmann | encoder = http.HttpJsonConverter() |
322 | 02cab3e7 | Michael Hanselmann | self.response_msg.start_line.code = http.HTTP_OK
|
323 | 02cab3e7 | Michael Hanselmann | self.response_msg.body = encoder.Encode(result)
|
324 | 02cab3e7 | Michael Hanselmann | self.response_msg.headers = handler_context.resp_headers
|
325 | 02cab3e7 | Michael Hanselmann | self.response_msg.headers[http.HTTP_CONTENT_TYPE] = encoder.CONTENT_TYPE
|
326 | 02cab3e7 | Michael Hanselmann | |
327 | 02cab3e7 | Michael Hanselmann | def _SendResponse(self): |
328 | 02cab3e7 | Michael Hanselmann | """Sends the response to the client.
|
329 | 02cab3e7 | Michael Hanselmann |
|
330 | 02cab3e7 | Michael Hanselmann | """
|
331 | 02cab3e7 | Michael Hanselmann | if self.response_msg.start_line.code is None: |
332 | 02cab3e7 | Michael Hanselmann | return
|
333 | 02cab3e7 | Michael Hanselmann | |
334 | 02cab3e7 | Michael Hanselmann | if not self.response_msg.headers: |
335 | 02cab3e7 | Michael Hanselmann | self.response_msg.headers = {}
|
336 | 02cab3e7 | Michael Hanselmann | |
337 | 02cab3e7 | Michael Hanselmann | self.response_msg.headers.update({
|
338 | 02cab3e7 | Michael Hanselmann | # TODO: Keep-alive is not supported
|
339 | 02cab3e7 | Michael Hanselmann | http.HTTP_CONNECTION: "close",
|
340 | 02cab3e7 | Michael Hanselmann | http.HTTP_DATE: _DateTimeHeader(), |
341 | 02cab3e7 | Michael Hanselmann | http.HTTP_SERVER: http.HTTP_GANETI_VERSION, |
342 | 02cab3e7 | Michael Hanselmann | }) |
343 | 02cab3e7 | Michael Hanselmann | |
344 | 02cab3e7 | Michael Hanselmann | # Get response reason based on code
|
345 | 02cab3e7 | Michael Hanselmann | response_code = self.response_msg.start_line.code
|
346 | 02cab3e7 | Michael Hanselmann | if response_code in self.responses: |
347 | 02cab3e7 | Michael Hanselmann | response_reason = self.responses[response_code][0] |
348 | 02cab3e7 | Michael Hanselmann | else:
|
349 | 02cab3e7 | Michael Hanselmann | response_reason = ""
|
350 | 02cab3e7 | Michael Hanselmann | self.response_msg.start_line.reason = response_reason
|
351 | 02cab3e7 | Michael Hanselmann | |
352 | 02cab3e7 | Michael Hanselmann | logging.info("%s:%s %s %s", self.client_addr[0], self.client_addr[1], |
353 | 02cab3e7 | Michael Hanselmann | self.request_msg.start_line, response_code)
|
354 | 02cab3e7 | Michael Hanselmann | |
355 | 02cab3e7 | Michael Hanselmann | try:
|
356 | 02cab3e7 | Michael Hanselmann | _HttpServerToClientMessageWriter(self.sock, self.request_msg, |
357 | 02cab3e7 | Michael Hanselmann | self.response_msg, self.WRITE_TIMEOUT) |
358 | 02cab3e7 | Michael Hanselmann | except http.HttpSocketTimeout:
|
359 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Timeout while sending response") |
360 | 02cab3e7 | Michael Hanselmann | except socket.error, err:
|
361 | 02cab3e7 | Michael Hanselmann | raise http.HttpError("Error sending response: %s" % err) |
362 | 02cab3e7 | Michael Hanselmann | |
363 | 02cab3e7 | Michael Hanselmann | def _SetErrorStatus(self, err): |
364 | 02cab3e7 | Michael Hanselmann | """Sets the response code and body from a HttpException.
|
365 | 02cab3e7 | Michael Hanselmann |
|
366 | 02cab3e7 | Michael Hanselmann | @type err: HttpException
|
367 | 02cab3e7 | Michael Hanselmann | @param err: Exception instance
|
368 | 02cab3e7 | Michael Hanselmann |
|
369 | 02cab3e7 | Michael Hanselmann | """
|
370 | 02cab3e7 | Michael Hanselmann | try:
|
371 | 02cab3e7 | Michael Hanselmann | (shortmsg, longmsg) = self.responses[err.code]
|
372 | 02cab3e7 | Michael Hanselmann | except KeyError: |
373 | 02cab3e7 | Michael Hanselmann | shortmsg = longmsg = "Unknown"
|
374 | 02cab3e7 | Michael Hanselmann | |
375 | 02cab3e7 | Michael Hanselmann | if err.message:
|
376 | 02cab3e7 | Michael Hanselmann | message = err.message |
377 | 02cab3e7 | Michael Hanselmann | else:
|
378 | 02cab3e7 | Michael Hanselmann | message = shortmsg |
379 | 02cab3e7 | Michael Hanselmann | |
380 | 02cab3e7 | Michael Hanselmann | values = { |
381 | 02cab3e7 | Michael Hanselmann | "code": err.code,
|
382 | 02cab3e7 | Michael Hanselmann | "message": cgi.escape(message),
|
383 | 02cab3e7 | Michael Hanselmann | "explain": longmsg,
|
384 | 02cab3e7 | Michael Hanselmann | } |
385 | 02cab3e7 | Michael Hanselmann | |
386 | 02cab3e7 | Michael Hanselmann | self.response_msg.start_line.code = err.code
|
387 | 02cab3e7 | Michael Hanselmann | self.response_msg.headers = {
|
388 | 02cab3e7 | Michael Hanselmann | http.HTTP_CONTENT_TYPE: self.error_content_type,
|
389 | 02cab3e7 | Michael Hanselmann | } |
390 | 02cab3e7 | Michael Hanselmann | self.response_msg.body = self.error_message_format % values |
391 | 02cab3e7 | Michael Hanselmann | |
392 | 02cab3e7 | Michael Hanselmann | |
393 | f4322a1e | Michael Hanselmann | class HttpServer(http.HttpBase): |
394 | 02cab3e7 | Michael Hanselmann | """Generic HTTP server class
|
395 | 02cab3e7 | Michael Hanselmann |
|
396 | 02cab3e7 | Michael Hanselmann | Users of this class must subclass it and override the HandleRequest function.
|
397 | 02cab3e7 | Michael Hanselmann |
|
398 | 02cab3e7 | Michael Hanselmann | """
|
399 | 02cab3e7 | Michael Hanselmann | MAX_CHILDREN = 20
|
400 | 02cab3e7 | Michael Hanselmann | |
401 | 02cab3e7 | Michael Hanselmann | def __init__(self, mainloop, local_address, port, |
402 | 02cab3e7 | Michael Hanselmann | ssl_params=None, ssl_verify_peer=False): |
403 | 02cab3e7 | Michael Hanselmann | """Initializes the HTTP server
|
404 | 02cab3e7 | Michael Hanselmann |
|
405 | 02cab3e7 | Michael Hanselmann | @type mainloop: ganeti.daemon.Mainloop
|
406 | 02cab3e7 | Michael Hanselmann | @param mainloop: Mainloop used to poll for I/O events
|
407 | c41eea6e | Iustin Pop | @type local_address: string
|
408 | 02cab3e7 | Michael Hanselmann | @param local_address: Local IP address to bind to
|
409 | 02cab3e7 | Michael Hanselmann | @type port: int
|
410 | 02cab3e7 | Michael Hanselmann | @param port: TCP port to listen on
|
411 | 02cab3e7 | Michael Hanselmann | @type ssl_params: HttpSslParams
|
412 | 02cab3e7 | Michael Hanselmann | @param ssl_params: SSL key and certificate
|
413 | 02cab3e7 | Michael Hanselmann | @type ssl_verify_peer: bool
|
414 | 02cab3e7 | Michael Hanselmann | @param ssl_verify_peer: Whether to require client certificate and compare
|
415 | 02cab3e7 | Michael Hanselmann | it with our certificate
|
416 | 02cab3e7 | Michael Hanselmann |
|
417 | 02cab3e7 | Michael Hanselmann | """
|
418 | f4322a1e | Michael Hanselmann | http.HttpBase.__init__(self)
|
419 | 02cab3e7 | Michael Hanselmann | |
420 | 02cab3e7 | Michael Hanselmann | self.mainloop = mainloop
|
421 | 02cab3e7 | Michael Hanselmann | self.local_address = local_address
|
422 | 02cab3e7 | Michael Hanselmann | self.port = port
|
423 | 02cab3e7 | Michael Hanselmann | |
424 | 02cab3e7 | Michael Hanselmann | self.socket = self._CreateSocket(ssl_params, ssl_verify_peer) |
425 | 02cab3e7 | Michael Hanselmann | |
426 | 02cab3e7 | Michael Hanselmann | # Allow port to be reused
|
427 | 02cab3e7 | Michael Hanselmann | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
428 | 02cab3e7 | Michael Hanselmann | |
429 | 02cab3e7 | Michael Hanselmann | self._children = []
|
430 | 02cab3e7 | Michael Hanselmann | |
431 | 02cab3e7 | Michael Hanselmann | mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN) |
432 | 02cab3e7 | Michael Hanselmann | mainloop.RegisterSignal(self)
|
433 | 02cab3e7 | Michael Hanselmann | |
434 | 02cab3e7 | Michael Hanselmann | def Start(self): |
435 | 02cab3e7 | Michael Hanselmann | self.socket.bind((self.local_address, self.port)) |
436 | 59305197 | Michael Hanselmann | self.socket.listen(1024) |
437 | 02cab3e7 | Michael Hanselmann | |
438 | 02cab3e7 | Michael Hanselmann | def Stop(self): |
439 | 02cab3e7 | Michael Hanselmann | self.socket.close()
|
440 | 02cab3e7 | Michael Hanselmann | |
441 | 02cab3e7 | Michael Hanselmann | def OnIO(self, fd, condition): |
442 | 02cab3e7 | Michael Hanselmann | if condition & select.POLLIN:
|
443 | 02cab3e7 | Michael Hanselmann | self._IncomingConnection()
|
444 | 02cab3e7 | Michael Hanselmann | |
445 | 02cab3e7 | Michael Hanselmann | def OnSignal(self, signum): |
446 | 02cab3e7 | Michael Hanselmann | if signum == signal.SIGCHLD:
|
447 | 02cab3e7 | Michael Hanselmann | self._CollectChildren(True) |
448 | 02cab3e7 | Michael Hanselmann | |
449 | 02cab3e7 | Michael Hanselmann | def _CollectChildren(self, quick): |
450 | 02cab3e7 | Michael Hanselmann | """Checks whether any child processes are done
|
451 | 02cab3e7 | Michael Hanselmann |
|
452 | 02cab3e7 | Michael Hanselmann | @type quick: bool
|
453 | 02cab3e7 | Michael Hanselmann | @param quick: Whether to only use non-blocking functions
|
454 | 02cab3e7 | Michael Hanselmann |
|
455 | 02cab3e7 | Michael Hanselmann | """
|
456 | 02cab3e7 | Michael Hanselmann | if not quick: |
457 | 02cab3e7 | Michael Hanselmann | # Don't wait for other processes if it should be a quick check
|
458 | 02cab3e7 | Michael Hanselmann | while len(self._children) > self.MAX_CHILDREN: |
459 | 02cab3e7 | Michael Hanselmann | try:
|
460 | 02cab3e7 | Michael Hanselmann | # Waiting without a timeout brings us into a potential DoS situation.
|
461 | 02cab3e7 | Michael Hanselmann | # As soon as too many children run, we'll not respond to new
|
462 | 02cab3e7 | Michael Hanselmann | # requests. The real solution would be to add a timeout for children
|
463 | 02cab3e7 | Michael Hanselmann | # and killing them after some time.
|
464 | 02cab3e7 | Michael Hanselmann | pid, status = os.waitpid(0, 0) |
465 | 02cab3e7 | Michael Hanselmann | except os.error:
|
466 | 02cab3e7 | Michael Hanselmann | pid = None
|
467 | 02cab3e7 | Michael Hanselmann | if pid and pid in self._children: |
468 | 02cab3e7 | Michael Hanselmann | self._children.remove(pid)
|
469 | 02cab3e7 | Michael Hanselmann | |
470 | 02cab3e7 | Michael Hanselmann | for child in self._children: |
471 | 02cab3e7 | Michael Hanselmann | try:
|
472 | 02cab3e7 | Michael Hanselmann | pid, status = os.waitpid(child, os.WNOHANG) |
473 | 02cab3e7 | Michael Hanselmann | except os.error:
|
474 | 02cab3e7 | Michael Hanselmann | pid = None
|
475 | 02cab3e7 | Michael Hanselmann | if pid and pid in self._children: |
476 | 02cab3e7 | Michael Hanselmann | self._children.remove(pid)
|
477 | 02cab3e7 | Michael Hanselmann | |
478 | 02cab3e7 | Michael Hanselmann | def _IncomingConnection(self): |
479 | 02cab3e7 | Michael Hanselmann | """Called for each incoming connection
|
480 | 02cab3e7 | Michael Hanselmann |
|
481 | 02cab3e7 | Michael Hanselmann | """
|
482 | 02cab3e7 | Michael Hanselmann | (connection, client_addr) = self.socket.accept()
|
483 | 02cab3e7 | Michael Hanselmann | |
484 | 02cab3e7 | Michael Hanselmann | self._CollectChildren(False) |
485 | 02cab3e7 | Michael Hanselmann | |
486 | 02cab3e7 | Michael Hanselmann | pid = os.fork() |
487 | 02cab3e7 | Michael Hanselmann | if pid == 0: |
488 | 02cab3e7 | Michael Hanselmann | # Child process
|
489 | 02cab3e7 | Michael Hanselmann | try:
|
490 | 02cab3e7 | Michael Hanselmann | _HttpServerRequestExecutor(self, connection, client_addr)
|
491 | 02cab3e7 | Michael Hanselmann | except Exception: |
492 | 02cab3e7 | Michael Hanselmann | logging.exception("Error while handling request from %s:%s",
|
493 | 02cab3e7 | Michael Hanselmann | client_addr[0], client_addr[1]) |
494 | 02cab3e7 | Michael Hanselmann | os._exit(1)
|
495 | 02cab3e7 | Michael Hanselmann | os._exit(0)
|
496 | 02cab3e7 | Michael Hanselmann | else:
|
497 | 02cab3e7 | Michael Hanselmann | self._children.append(pid)
|
498 | 02cab3e7 | Michael Hanselmann | |
499 | 02cab3e7 | Michael Hanselmann | def HandleRequest(self, req): |
500 | 02cab3e7 | Michael Hanselmann | """Handles a request.
|
501 | 02cab3e7 | Michael Hanselmann |
|
502 | 02cab3e7 | Michael Hanselmann | Must be overriden by subclass.
|
503 | 02cab3e7 | Michael Hanselmann |
|
504 | 02cab3e7 | Michael Hanselmann | """
|
505 | 02cab3e7 | Michael Hanselmann | raise NotImplementedError() |