Statistics
| Branch: | Tag: | Revision:

root / lib / http / server.py @ a6db1af2

History | View | Annotate | Download (16.5 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 112d240d Guido Trotter
import asyncore
34 02cab3e7 Michael Hanselmann
35 02cab3e7 Michael Hanselmann
from ganeti import http
36 02cab3e7 Michael Hanselmann
37 02cab3e7 Michael Hanselmann
38 02cab3e7 Michael Hanselmann
WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
39 02cab3e7 Michael Hanselmann
MONTHNAME = [None,
40 02cab3e7 Michael Hanselmann
             'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
41 02cab3e7 Michael Hanselmann
             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
42 02cab3e7 Michael Hanselmann
43 02cab3e7 Michael Hanselmann
# Default error message
44 02cab3e7 Michael Hanselmann
DEFAULT_ERROR_CONTENT_TYPE = "text/html"
45 02cab3e7 Michael Hanselmann
DEFAULT_ERROR_MESSAGE = """\
46 02cab3e7 Michael Hanselmann
<html>
47 02cab3e7 Michael Hanselmann
<head>
48 02cab3e7 Michael Hanselmann
<title>Error response</title>
49 02cab3e7 Michael Hanselmann
</head>
50 02cab3e7 Michael Hanselmann
<body>
51 02cab3e7 Michael Hanselmann
<h1>Error response</h1>
52 02cab3e7 Michael Hanselmann
<p>Error code %(code)d.
53 02cab3e7 Michael Hanselmann
<p>Message: %(message)s.
54 02cab3e7 Michael Hanselmann
<p>Error code explanation: %(code)s = %(explain)s.
55 02cab3e7 Michael Hanselmann
</body>
56 02cab3e7 Michael Hanselmann
</html>
57 02cab3e7 Michael Hanselmann
"""
58 02cab3e7 Michael Hanselmann
59 02cab3e7 Michael Hanselmann
60 f30ca1e6 Michael Hanselmann
def _DateTimeHeader(gmnow=None):
61 02cab3e7 Michael Hanselmann
  """Return the current date and time formatted for a message header.
62 02cab3e7 Michael Hanselmann

63 f30ca1e6 Michael Hanselmann
  The time MUST be in the GMT timezone.
64 f30ca1e6 Michael Hanselmann

65 02cab3e7 Michael Hanselmann
  """
66 f30ca1e6 Michael Hanselmann
  if gmnow is None:
67 f30ca1e6 Michael Hanselmann
    gmnow = time.gmtime()
68 f30ca1e6 Michael Hanselmann
  (year, month, day, hh, mm, ss, wd, _, _) = gmnow
69 02cab3e7 Michael Hanselmann
  return ("%s, %02d %3s %4d %02d:%02d:%02d GMT" %
70 02cab3e7 Michael Hanselmann
          (WEEKDAYNAME[wd], day, MONTHNAME[month], year, hh, mm, ss))
71 02cab3e7 Michael Hanselmann
72 02cab3e7 Michael Hanselmann
73 02cab3e7 Michael Hanselmann
class _HttpServerRequest(object):
74 02cab3e7 Michael Hanselmann
  """Data structure for HTTP request on server side.
75 02cab3e7 Michael Hanselmann

76 02cab3e7 Michael Hanselmann
  """
77 02cab3e7 Michael Hanselmann
  def __init__(self, request_msg):
78 02cab3e7 Michael Hanselmann
    # Request attributes
79 02cab3e7 Michael Hanselmann
    self.request_method = request_msg.start_line.method
80 02cab3e7 Michael Hanselmann
    self.request_path = request_msg.start_line.path
81 02cab3e7 Michael Hanselmann
    self.request_headers = request_msg.headers
82 02cab3e7 Michael Hanselmann
    self.request_body = request_msg.decoded_body
83 02cab3e7 Michael Hanselmann
84 02cab3e7 Michael Hanselmann
    # Response attributes
85 02cab3e7 Michael Hanselmann
    self.resp_headers = {}
86 02cab3e7 Michael Hanselmann
87 68fa9caf Michael Hanselmann
    # Private data for request handler (useful in combination with
88 68fa9caf Michael Hanselmann
    # authentication)
89 68fa9caf Michael Hanselmann
    self.private = None
90 68fa9caf Michael Hanselmann
91 02cab3e7 Michael Hanselmann
92 02cab3e7 Michael Hanselmann
class _HttpServerToClientMessageWriter(http.HttpMessageWriter):
93 02cab3e7 Michael Hanselmann
  """Writes an HTTP response to client.
94 02cab3e7 Michael Hanselmann

95 02cab3e7 Michael Hanselmann
  """
96 02cab3e7 Michael Hanselmann
  def __init__(self, sock, request_msg, response_msg, write_timeout):
97 358a8811 Michael Hanselmann
    """Writes the response to the client.
98 358a8811 Michael Hanselmann

99 358a8811 Michael Hanselmann
    @type sock: socket
100 358a8811 Michael Hanselmann
    @param sock: Target socket
101 358a8811 Michael Hanselmann
    @type request_msg: http.HttpMessage
102 358a8811 Michael Hanselmann
    @param request_msg: Request message, required to determine whether
103 25e7b43f Iustin Pop
        response may have a message body
104 358a8811 Michael Hanselmann
    @type response_msg: http.HttpMessage
105 358a8811 Michael Hanselmann
    @param response_msg: Response message
106 358a8811 Michael Hanselmann
    @type write_timeout: float
107 358a8811 Michael Hanselmann
    @param write_timeout: Write timeout for socket
108 02cab3e7 Michael Hanselmann

109 02cab3e7 Michael Hanselmann
    """
110 02cab3e7 Michael Hanselmann
    self._request_msg = request_msg
111 02cab3e7 Michael Hanselmann
    self._response_msg = response_msg
112 02cab3e7 Michael Hanselmann
    http.HttpMessageWriter.__init__(self, sock, response_msg, write_timeout)
113 02cab3e7 Michael Hanselmann
114 02cab3e7 Michael Hanselmann
  def HasMessageBody(self):
115 02cab3e7 Michael Hanselmann
    """Logic to detect whether response should contain a message body.
116 02cab3e7 Michael Hanselmann

117 02cab3e7 Michael Hanselmann
    """
118 02cab3e7 Michael Hanselmann
    if self._request_msg.start_line:
119 02cab3e7 Michael Hanselmann
      request_method = self._request_msg.start_line.method
120 02cab3e7 Michael Hanselmann
    else:
121 02cab3e7 Michael Hanselmann
      request_method = None
122 02cab3e7 Michael Hanselmann
123 02cab3e7 Michael Hanselmann
    response_code = self._response_msg.start_line.code
124 02cab3e7 Michael Hanselmann
125 02cab3e7 Michael Hanselmann
    # RFC2616, section 4.3: "A message-body MUST NOT be included in a request
126 02cab3e7 Michael Hanselmann
    # if the specification of the request method (section 5.1.1) does not allow
127 02cab3e7 Michael Hanselmann
    # sending an entity-body in requests"
128 02cab3e7 Michael Hanselmann
    #
129 02cab3e7 Michael Hanselmann
    # RFC2616, section 9.4: "The HEAD method is identical to GET except that
130 02cab3e7 Michael Hanselmann
    # the server MUST NOT return a message-body in the response."
131 02cab3e7 Michael Hanselmann
    #
132 02cab3e7 Michael Hanselmann
    # RFC2616, section 10.2.5: "The 204 response MUST NOT include a
133 02cab3e7 Michael Hanselmann
    # message-body [...]"
134 02cab3e7 Michael Hanselmann
    #
135 02cab3e7 Michael Hanselmann
    # RFC2616, section 10.3.5: "The 304 response MUST NOT contain a
136 02cab3e7 Michael Hanselmann
    # message-body, [...]"
137 02cab3e7 Michael Hanselmann
138 02cab3e7 Michael Hanselmann
    return (http.HttpMessageWriter.HasMessageBody(self) and
139 3f3dfc15 Iustin Pop
            (request_method is not None and
140 3f3dfc15 Iustin Pop
             request_method != http.HTTP_HEAD) and
141 02cab3e7 Michael Hanselmann
            response_code >= http.HTTP_OK and
142 3f3dfc15 Iustin Pop
            response_code not in (http.HTTP_NO_CONTENT,
143 3f3dfc15 Iustin Pop
                                  http.HTTP_NOT_MODIFIED))
144 02cab3e7 Michael Hanselmann
145 02cab3e7 Michael Hanselmann
146 02cab3e7 Michael Hanselmann
class _HttpClientToServerMessageReader(http.HttpMessageReader):
147 02cab3e7 Michael Hanselmann
  """Reads an HTTP request sent by client.
148 02cab3e7 Michael Hanselmann

149 02cab3e7 Michael Hanselmann
  """
150 02cab3e7 Michael Hanselmann
  # Length limits
151 02cab3e7 Michael Hanselmann
  START_LINE_LENGTH_MAX = 4096
152 02cab3e7 Michael Hanselmann
  HEADER_LENGTH_MAX = 4096
153 02cab3e7 Michael Hanselmann
154 02cab3e7 Michael Hanselmann
  def ParseStartLine(self, start_line):
155 02cab3e7 Michael Hanselmann
    """Parses the start line sent by client.
156 02cab3e7 Michael Hanselmann

157 02cab3e7 Michael Hanselmann
    Example: "GET /index.html HTTP/1.1"
158 02cab3e7 Michael Hanselmann

159 02cab3e7 Michael Hanselmann
    @type start_line: string
160 02cab3e7 Michael Hanselmann
    @param start_line: Start line
161 02cab3e7 Michael Hanselmann

162 02cab3e7 Michael Hanselmann
    """
163 02cab3e7 Michael Hanselmann
    # Empty lines are skipped when reading
164 02cab3e7 Michael Hanselmann
    assert start_line
165 02cab3e7 Michael Hanselmann
166 02cab3e7 Michael Hanselmann
    logging.debug("HTTP request: %s", start_line)
167 02cab3e7 Michael Hanselmann
168 02cab3e7 Michael Hanselmann
    words = start_line.split()
169 02cab3e7 Michael Hanselmann
170 02cab3e7 Michael Hanselmann
    if len(words) == 3:
171 02cab3e7 Michael Hanselmann
      [method, path, version] = words
172 02cab3e7 Michael Hanselmann
      if version[:5] != 'HTTP/':
173 02cab3e7 Michael Hanselmann
        raise http.HttpBadRequest("Bad request version (%r)" % version)
174 02cab3e7 Michael Hanselmann
175 02cab3e7 Michael Hanselmann
      try:
176 02cab3e7 Michael Hanselmann
        base_version_number = version.split("/", 1)[1]
177 02cab3e7 Michael Hanselmann
        version_number = base_version_number.split(".")
178 02cab3e7 Michael Hanselmann
179 02cab3e7 Michael Hanselmann
        # RFC 2145 section 3.1 says there can be only one "." and
180 02cab3e7 Michael Hanselmann
        #   - major and minor numbers MUST be treated as
181 02cab3e7 Michael Hanselmann
        #      separate integers;
182 02cab3e7 Michael Hanselmann
        #   - HTTP/2.4 is a lower version than HTTP/2.13, which in
183 02cab3e7 Michael Hanselmann
        #      turn is lower than HTTP/12.3;
184 02cab3e7 Michael Hanselmann
        #   - Leading zeros MUST be ignored by recipients.
185 02cab3e7 Michael Hanselmann
        if len(version_number) != 2:
186 02cab3e7 Michael Hanselmann
          raise http.HttpBadRequest("Bad request version (%r)" % version)
187 02cab3e7 Michael Hanselmann
188 02cab3e7 Michael Hanselmann
        version_number = (int(version_number[0]), int(version_number[1]))
189 02cab3e7 Michael Hanselmann
      except (ValueError, IndexError):
190 02cab3e7 Michael Hanselmann
        raise http.HttpBadRequest("Bad request version (%r)" % version)
191 02cab3e7 Michael Hanselmann
192 02cab3e7 Michael Hanselmann
      if version_number >= (2, 0):
193 02cab3e7 Michael Hanselmann
        raise http.HttpVersionNotSupported("Invalid HTTP Version (%s)" %
194 02cab3e7 Michael Hanselmann
                                      base_version_number)
195 02cab3e7 Michael Hanselmann
196 02cab3e7 Michael Hanselmann
    elif len(words) == 2:
197 02cab3e7 Michael Hanselmann
      version = http.HTTP_0_9
198 02cab3e7 Michael Hanselmann
      [method, path] = words
199 02cab3e7 Michael Hanselmann
      if method != http.HTTP_GET:
200 02cab3e7 Michael Hanselmann
        raise http.HttpBadRequest("Bad HTTP/0.9 request type (%r)" % method)
201 02cab3e7 Michael Hanselmann
202 02cab3e7 Michael Hanselmann
    else:
203 02cab3e7 Michael Hanselmann
      raise http.HttpBadRequest("Bad request syntax (%r)" % start_line)
204 02cab3e7 Michael Hanselmann
205 02cab3e7 Michael Hanselmann
    return http.HttpClientToServerStartLine(method, path, version)
206 02cab3e7 Michael Hanselmann
207 02cab3e7 Michael Hanselmann
208 1f8588f6 Iustin Pop
class HttpServerRequestExecutor(object):
209 02cab3e7 Michael Hanselmann
  """Implements server side of HTTP.
210 02cab3e7 Michael Hanselmann

211 25e7b43f Iustin Pop
  This class implements the server side of HTTP. It's based on code of
212 25e7b43f Iustin Pop
  Python's BaseHTTPServer, from both version 2.4 and 3k. It does not
213 25e7b43f Iustin Pop
  support non-ASCII character encodings. Keep-alive connections are
214 25e7b43f Iustin Pop
  not supported.
215 02cab3e7 Michael Hanselmann

216 02cab3e7 Michael Hanselmann
  """
217 02cab3e7 Michael Hanselmann
  # The default request version.  This only affects responses up until
218 02cab3e7 Michael Hanselmann
  # the point where the request line is parsed, so it mainly decides what
219 02cab3e7 Michael Hanselmann
  # the client gets back when sending a malformed request line.
220 02cab3e7 Michael Hanselmann
  # Most web servers default to HTTP 0.9, i.e. don't send a status line.
221 02cab3e7 Michael Hanselmann
  default_request_version = http.HTTP_0_9
222 02cab3e7 Michael Hanselmann
223 02cab3e7 Michael Hanselmann
  # Error message settings
224 02cab3e7 Michael Hanselmann
  error_message_format = DEFAULT_ERROR_MESSAGE
225 02cab3e7 Michael Hanselmann
  error_content_type = DEFAULT_ERROR_CONTENT_TYPE
226 02cab3e7 Michael Hanselmann
227 02cab3e7 Michael Hanselmann
  responses = BaseHTTPServer.BaseHTTPRequestHandler.responses
228 02cab3e7 Michael Hanselmann
229 02cab3e7 Michael Hanselmann
  # Timeouts in seconds for socket layer
230 02cab3e7 Michael Hanselmann
  WRITE_TIMEOUT = 10
231 02cab3e7 Michael Hanselmann
  READ_TIMEOUT = 10
232 02cab3e7 Michael Hanselmann
  CLOSE_TIMEOUT = 1
233 02cab3e7 Michael Hanselmann
234 02cab3e7 Michael Hanselmann
  def __init__(self, server, sock, client_addr):
235 02cab3e7 Michael Hanselmann
    """Initializes this class.
236 02cab3e7 Michael Hanselmann

237 02cab3e7 Michael Hanselmann
    """
238 02cab3e7 Michael Hanselmann
    self.server = server
239 02cab3e7 Michael Hanselmann
    self.sock = sock
240 02cab3e7 Michael Hanselmann
    self.client_addr = client_addr
241 02cab3e7 Michael Hanselmann
242 02cab3e7 Michael Hanselmann
    self.request_msg = http.HttpMessage()
243 02cab3e7 Michael Hanselmann
    self.response_msg = http.HttpMessage()
244 02cab3e7 Michael Hanselmann
245 02cab3e7 Michael Hanselmann
    self.response_msg.start_line = \
246 02cab3e7 Michael Hanselmann
      http.HttpServerToClientStartLine(version=self.default_request_version,
247 02cab3e7 Michael Hanselmann
                                       code=None, reason=None)
248 02cab3e7 Michael Hanselmann
249 02cab3e7 Michael Hanselmann
    # Disable Python's timeout
250 02cab3e7 Michael Hanselmann
    self.sock.settimeout(None)
251 02cab3e7 Michael Hanselmann
252 02cab3e7 Michael Hanselmann
    # Operate in non-blocking mode
253 02cab3e7 Michael Hanselmann
    self.sock.setblocking(0)
254 02cab3e7 Michael Hanselmann
255 14d57a8b Iustin Pop
    logging.debug("Connection from %s:%s", client_addr[0], client_addr[1])
256 02cab3e7 Michael Hanselmann
    try:
257 02cab3e7 Michael Hanselmann
      request_msg_reader = None
258 02cab3e7 Michael Hanselmann
      force_close = True
259 02cab3e7 Michael Hanselmann
      try:
260 f2e13d55 Michael Hanselmann
        # Do the secret SSL handshake
261 f2e13d55 Michael Hanselmann
        if self.server.using_ssl:
262 f2e13d55 Michael Hanselmann
          self.sock.set_accept_state()
263 f2e13d55 Michael Hanselmann
          try:
264 aea0ed67 Michael Hanselmann
            http.Handshake(self.sock, self.WRITE_TIMEOUT)
265 f2e13d55 Michael Hanselmann
          except http.HttpSessionHandshakeUnexpectedEOF:
266 f2e13d55 Michael Hanselmann
            # Ignore rest
267 f2e13d55 Michael Hanselmann
            return
268 f2e13d55 Michael Hanselmann
269 02cab3e7 Michael Hanselmann
        try:
270 02cab3e7 Michael Hanselmann
          try:
271 02cab3e7 Michael Hanselmann
            request_msg_reader = self._ReadRequest()
272 02cab3e7 Michael Hanselmann
            self._HandleRequest()
273 02cab3e7 Michael Hanselmann
274 02cab3e7 Michael Hanselmann
            # Only wait for client to close if we didn't have any exception.
275 02cab3e7 Michael Hanselmann
            force_close = False
276 02cab3e7 Michael Hanselmann
          except http.HttpException, err:
277 02cab3e7 Michael Hanselmann
            self._SetErrorStatus(err)
278 02cab3e7 Michael Hanselmann
        finally:
279 02cab3e7 Michael Hanselmann
          # Try to send a response
280 02cab3e7 Michael Hanselmann
          self._SendResponse()
281 02cab3e7 Michael Hanselmann
      finally:
282 aea0ed67 Michael Hanselmann
        http.ShutdownConnection(sock, self.CLOSE_TIMEOUT, self.WRITE_TIMEOUT,
283 02cab3e7 Michael Hanselmann
                                request_msg_reader, force_close)
284 02cab3e7 Michael Hanselmann
285 02cab3e7 Michael Hanselmann
      self.sock.close()
286 02cab3e7 Michael Hanselmann
      self.sock = None
287 02cab3e7 Michael Hanselmann
    finally:
288 14d57a8b Iustin Pop
      logging.debug("Disconnected %s:%s", client_addr[0], client_addr[1])
289 02cab3e7 Michael Hanselmann
290 02cab3e7 Michael Hanselmann
  def _ReadRequest(self):
291 02cab3e7 Michael Hanselmann
    """Reads a request sent by client.
292 02cab3e7 Michael Hanselmann

293 02cab3e7 Michael Hanselmann
    """
294 02cab3e7 Michael Hanselmann
    try:
295 02cab3e7 Michael Hanselmann
      request_msg_reader = \
296 02cab3e7 Michael Hanselmann
        _HttpClientToServerMessageReader(self.sock, self.request_msg,
297 02cab3e7 Michael Hanselmann
                                         self.READ_TIMEOUT)
298 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
299 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading request")
300 02cab3e7 Michael Hanselmann
    except socket.error, err:
301 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading request: %s" % err)
302 02cab3e7 Michael Hanselmann
303 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.version = self.request_msg.start_line.version
304 02cab3e7 Michael Hanselmann
305 02cab3e7 Michael Hanselmann
    return request_msg_reader
306 02cab3e7 Michael Hanselmann
307 02cab3e7 Michael Hanselmann
  def _HandleRequest(self):
308 02cab3e7 Michael Hanselmann
    """Calls the handler function for the current request.
309 02cab3e7 Michael Hanselmann

310 02cab3e7 Michael Hanselmann
    """
311 02cab3e7 Michael Hanselmann
    handler_context = _HttpServerRequest(self.request_msg)
312 02cab3e7 Michael Hanselmann
313 02cab3e7 Michael Hanselmann
    try:
314 68fa9caf Michael Hanselmann
      try:
315 68fa9caf Michael Hanselmann
        # Authentication, etc.
316 68fa9caf Michael Hanselmann
        self.server.PreHandleRequest(handler_context)
317 68fa9caf Michael Hanselmann
318 68fa9caf Michael Hanselmann
        # Call actual request handler
319 68fa9caf Michael Hanselmann
        result = self.server.HandleRequest(handler_context)
320 68fa9caf Michael Hanselmann
      except (http.HttpException, KeyboardInterrupt, SystemExit):
321 68fa9caf Michael Hanselmann
        raise
322 68fa9caf Michael Hanselmann
      except Exception, err:
323 68fa9caf Michael Hanselmann
        logging.exception("Caught exception")
324 68fa9caf Michael Hanselmann
        raise http.HttpInternalServerError(message=str(err))
325 68fa9caf Michael Hanselmann
      except:
326 68fa9caf Michael Hanselmann
        logging.exception("Unknown exception")
327 68fa9caf Michael Hanselmann
        raise http.HttpInternalServerError(message="Unknown error")
328 68fa9caf Michael Hanselmann
329 68fa9caf Michael Hanselmann
      # TODO: Content-type
330 68fa9caf Michael Hanselmann
      encoder = http.HttpJsonConverter()
331 68fa9caf Michael Hanselmann
      self.response_msg.start_line.code = http.HTTP_OK
332 68fa9caf Michael Hanselmann
      self.response_msg.body = encoder.Encode(result)
333 68fa9caf Michael Hanselmann
      self.response_msg.headers = handler_context.resp_headers
334 68fa9caf Michael Hanselmann
      self.response_msg.headers[http.HTTP_CONTENT_TYPE] = encoder.CONTENT_TYPE
335 68fa9caf Michael Hanselmann
    finally:
336 68fa9caf Michael Hanselmann
      # No reason to keep this any longer, even for exceptions
337 68fa9caf Michael Hanselmann
      handler_context.private = None
338 02cab3e7 Michael Hanselmann
339 02cab3e7 Michael Hanselmann
  def _SendResponse(self):
340 02cab3e7 Michael Hanselmann
    """Sends the response to the client.
341 02cab3e7 Michael Hanselmann

342 02cab3e7 Michael Hanselmann
    """
343 02cab3e7 Michael Hanselmann
    if self.response_msg.start_line.code is None:
344 02cab3e7 Michael Hanselmann
      return
345 02cab3e7 Michael Hanselmann
346 02cab3e7 Michael Hanselmann
    if not self.response_msg.headers:
347 02cab3e7 Michael Hanselmann
      self.response_msg.headers = {}
348 02cab3e7 Michael Hanselmann
349 02cab3e7 Michael Hanselmann
    self.response_msg.headers.update({
350 02cab3e7 Michael Hanselmann
      # TODO: Keep-alive is not supported
351 02cab3e7 Michael Hanselmann
      http.HTTP_CONNECTION: "close",
352 02cab3e7 Michael Hanselmann
      http.HTTP_DATE: _DateTimeHeader(),
353 02cab3e7 Michael Hanselmann
      http.HTTP_SERVER: http.HTTP_GANETI_VERSION,
354 02cab3e7 Michael Hanselmann
      })
355 02cab3e7 Michael Hanselmann
356 02cab3e7 Michael Hanselmann
    # Get response reason based on code
357 02cab3e7 Michael Hanselmann
    response_code = self.response_msg.start_line.code
358 02cab3e7 Michael Hanselmann
    if response_code in self.responses:
359 02cab3e7 Michael Hanselmann
      response_reason = self.responses[response_code][0]
360 02cab3e7 Michael Hanselmann
    else:
361 02cab3e7 Michael Hanselmann
      response_reason = ""
362 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.reason = response_reason
363 02cab3e7 Michael Hanselmann
364 02cab3e7 Michael Hanselmann
    logging.info("%s:%s %s %s", self.client_addr[0], self.client_addr[1],
365 02cab3e7 Michael Hanselmann
                 self.request_msg.start_line, response_code)
366 02cab3e7 Michael Hanselmann
367 02cab3e7 Michael Hanselmann
    try:
368 02cab3e7 Michael Hanselmann
      _HttpServerToClientMessageWriter(self.sock, self.request_msg,
369 02cab3e7 Michael Hanselmann
                                       self.response_msg, self.WRITE_TIMEOUT)
370 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
371 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while sending response")
372 02cab3e7 Michael Hanselmann
    except socket.error, err:
373 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error sending response: %s" % err)
374 02cab3e7 Michael Hanselmann
375 02cab3e7 Michael Hanselmann
  def _SetErrorStatus(self, err):
376 02cab3e7 Michael Hanselmann
    """Sets the response code and body from a HttpException.
377 02cab3e7 Michael Hanselmann

378 02cab3e7 Michael Hanselmann
    @type err: HttpException
379 02cab3e7 Michael Hanselmann
    @param err: Exception instance
380 02cab3e7 Michael Hanselmann

381 02cab3e7 Michael Hanselmann
    """
382 02cab3e7 Michael Hanselmann
    try:
383 02cab3e7 Michael Hanselmann
      (shortmsg, longmsg) = self.responses[err.code]
384 02cab3e7 Michael Hanselmann
    except KeyError:
385 02cab3e7 Michael Hanselmann
      shortmsg = longmsg = "Unknown"
386 02cab3e7 Michael Hanselmann
387 02cab3e7 Michael Hanselmann
    if err.message:
388 02cab3e7 Michael Hanselmann
      message = err.message
389 02cab3e7 Michael Hanselmann
    else:
390 02cab3e7 Michael Hanselmann
      message = shortmsg
391 02cab3e7 Michael Hanselmann
392 02cab3e7 Michael Hanselmann
    values = {
393 02cab3e7 Michael Hanselmann
      "code": err.code,
394 02cab3e7 Michael Hanselmann
      "message": cgi.escape(message),
395 02cab3e7 Michael Hanselmann
      "explain": longmsg,
396 02cab3e7 Michael Hanselmann
      }
397 02cab3e7 Michael Hanselmann
398 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.code = err.code
399 a8e01e9f Michael Hanselmann
400 a8e01e9f Michael Hanselmann
    headers = {}
401 a8e01e9f Michael Hanselmann
    if err.headers:
402 a8e01e9f Michael Hanselmann
      headers.update(err.headers)
403 a8e01e9f Michael Hanselmann
    headers[http.HTTP_CONTENT_TYPE] = self.error_content_type
404 a8e01e9f Michael Hanselmann
    self.response_msg.headers = headers
405 a8e01e9f Michael Hanselmann
406 1f8588f6 Iustin Pop
    self.response_msg.body = self._FormatErrorMessage(values)
407 02cab3e7 Michael Hanselmann
408 1f8588f6 Iustin Pop
  def _FormatErrorMessage(self, values):
409 1f8588f6 Iustin Pop
    """Formats the body of an error message.
410 1f8588f6 Iustin Pop

411 1f8588f6 Iustin Pop
    @type values: dict
412 1f8588f6 Iustin Pop
    @param values: dictionary with keys code, message and explain.
413 1f8588f6 Iustin Pop
    @rtype: string
414 1f8588f6 Iustin Pop
    @return: the body of the message
415 1f8588f6 Iustin Pop

416 1f8588f6 Iustin Pop
    """
417 1f8588f6 Iustin Pop
    return self.error_message_format % values
418 02cab3e7 Michael Hanselmann
419 112d240d Guido Trotter
class HttpServer(http.HttpBase, asyncore.dispatcher):
420 02cab3e7 Michael Hanselmann
  """Generic HTTP server class
421 02cab3e7 Michael Hanselmann

422 02cab3e7 Michael Hanselmann
  Users of this class must subclass it and override the HandleRequest function.
423 02cab3e7 Michael Hanselmann

424 02cab3e7 Michael Hanselmann
  """
425 02cab3e7 Michael Hanselmann
  MAX_CHILDREN = 20
426 02cab3e7 Michael Hanselmann
427 02cab3e7 Michael Hanselmann
  def __init__(self, mainloop, local_address, port,
428 1f8588f6 Iustin Pop
               ssl_params=None, ssl_verify_peer=False,
429 1f8588f6 Iustin Pop
               request_executor_class=None):
430 02cab3e7 Michael Hanselmann
    """Initializes the HTTP server
431 02cab3e7 Michael Hanselmann

432 02cab3e7 Michael Hanselmann
    @type mainloop: ganeti.daemon.Mainloop
433 02cab3e7 Michael Hanselmann
    @param mainloop: Mainloop used to poll for I/O events
434 c41eea6e Iustin Pop
    @type local_address: string
435 02cab3e7 Michael Hanselmann
    @param local_address: Local IP address to bind to
436 02cab3e7 Michael Hanselmann
    @type port: int
437 02cab3e7 Michael Hanselmann
    @param port: TCP port to listen on
438 02cab3e7 Michael Hanselmann
    @type ssl_params: HttpSslParams
439 02cab3e7 Michael Hanselmann
    @param ssl_params: SSL key and certificate
440 02cab3e7 Michael Hanselmann
    @type ssl_verify_peer: bool
441 25e7b43f Iustin Pop
    @param ssl_verify_peer: Whether to require client certificate
442 25e7b43f Iustin Pop
        and compare it with our certificate
443 1f8588f6 Iustin Pop
    @type request_executor_class: class
444 1f8588f6 Iustin Pop
    @param request_executor_class: an class derived from the
445 1f8588f6 Iustin Pop
        HttpServerRequestExecutor class
446 02cab3e7 Michael Hanselmann

447 02cab3e7 Michael Hanselmann
    """
448 f4322a1e Michael Hanselmann
    http.HttpBase.__init__(self)
449 112d240d Guido Trotter
    asyncore.dispatcher.__init__(self)
450 02cab3e7 Michael Hanselmann
451 1f8588f6 Iustin Pop
    if request_executor_class is None:
452 1f8588f6 Iustin Pop
      self.request_executor = HttpServerRequestExecutor
453 1f8588f6 Iustin Pop
    else:
454 1f8588f6 Iustin Pop
      self.request_executor = request_executor_class
455 1f8588f6 Iustin Pop
456 02cab3e7 Michael Hanselmann
    self.mainloop = mainloop
457 02cab3e7 Michael Hanselmann
    self.local_address = local_address
458 02cab3e7 Michael Hanselmann
    self.port = port
459 02cab3e7 Michael Hanselmann
460 02cab3e7 Michael Hanselmann
    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
461 02cab3e7 Michael Hanselmann
462 02cab3e7 Michael Hanselmann
    # Allow port to be reused
463 02cab3e7 Michael Hanselmann
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
464 02cab3e7 Michael Hanselmann
465 02cab3e7 Michael Hanselmann
    self._children = []
466 112d240d Guido Trotter
    self.set_socket(self.socket)
467 112d240d Guido Trotter
    self.accepting = True
468 02cab3e7 Michael Hanselmann
    mainloop.RegisterSignal(self)
469 02cab3e7 Michael Hanselmann
470 02cab3e7 Michael Hanselmann
  def Start(self):
471 02cab3e7 Michael Hanselmann
    self.socket.bind((self.local_address, self.port))
472 59305197 Michael Hanselmann
    self.socket.listen(1024)
473 02cab3e7 Michael Hanselmann
474 02cab3e7 Michael Hanselmann
  def Stop(self):
475 02cab3e7 Michael Hanselmann
    self.socket.close()
476 02cab3e7 Michael Hanselmann
477 112d240d Guido Trotter
  def handle_accept(self):
478 112d240d Guido Trotter
    self._IncomingConnection()
479 02cab3e7 Michael Hanselmann
480 02cab3e7 Michael Hanselmann
  def OnSignal(self, signum):
481 02cab3e7 Michael Hanselmann
    if signum == signal.SIGCHLD:
482 02cab3e7 Michael Hanselmann
      self._CollectChildren(True)
483 02cab3e7 Michael Hanselmann
484 02cab3e7 Michael Hanselmann
  def _CollectChildren(self, quick):
485 02cab3e7 Michael Hanselmann
    """Checks whether any child processes are done
486 02cab3e7 Michael Hanselmann

487 02cab3e7 Michael Hanselmann
    @type quick: bool
488 02cab3e7 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
489 02cab3e7 Michael Hanselmann

490 02cab3e7 Michael Hanselmann
    """
491 02cab3e7 Michael Hanselmann
    if not quick:
492 02cab3e7 Michael Hanselmann
      # Don't wait for other processes if it should be a quick check
493 02cab3e7 Michael Hanselmann
      while len(self._children) > self.MAX_CHILDREN:
494 02cab3e7 Michael Hanselmann
        try:
495 02cab3e7 Michael Hanselmann
          # Waiting without a timeout brings us into a potential DoS situation.
496 02cab3e7 Michael Hanselmann
          # As soon as too many children run, we'll not respond to new
497 02cab3e7 Michael Hanselmann
          # requests. The real solution would be to add a timeout for children
498 02cab3e7 Michael Hanselmann
          # and killing them after some time.
499 7c4d6c7b Michael Hanselmann
          pid, _ = os.waitpid(0, 0)
500 02cab3e7 Michael Hanselmann
        except os.error:
501 02cab3e7 Michael Hanselmann
          pid = None
502 02cab3e7 Michael Hanselmann
        if pid and pid in self._children:
503 02cab3e7 Michael Hanselmann
          self._children.remove(pid)
504 02cab3e7 Michael Hanselmann
505 02cab3e7 Michael Hanselmann
    for child in self._children:
506 02cab3e7 Michael Hanselmann
      try:
507 02cab3e7 Michael Hanselmann
        pid, status = os.waitpid(child, os.WNOHANG)
508 02cab3e7 Michael Hanselmann
      except os.error:
509 02cab3e7 Michael Hanselmann
        pid = None
510 02cab3e7 Michael Hanselmann
      if pid and pid in self._children:
511 02cab3e7 Michael Hanselmann
        self._children.remove(pid)
512 02cab3e7 Michael Hanselmann
513 02cab3e7 Michael Hanselmann
  def _IncomingConnection(self):
514 02cab3e7 Michael Hanselmann
    """Called for each incoming connection
515 02cab3e7 Michael Hanselmann

516 02cab3e7 Michael Hanselmann
    """
517 02cab3e7 Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
518 02cab3e7 Michael Hanselmann
519 02cab3e7 Michael Hanselmann
    self._CollectChildren(False)
520 02cab3e7 Michael Hanselmann
521 02cab3e7 Michael Hanselmann
    pid = os.fork()
522 02cab3e7 Michael Hanselmann
    if pid == 0:
523 02cab3e7 Michael Hanselmann
      # Child process
524 02cab3e7 Michael Hanselmann
      try:
525 bcb1a39e Michael Hanselmann
        # The client shouldn't keep the listening socket open. If the parent
526 bcb1a39e Michael Hanselmann
        # process is restarted, it would fail when there's already something
527 bcb1a39e Michael Hanselmann
        # listening (in this case its own child from a previous run) on the
528 bcb1a39e Michael Hanselmann
        # same port.
529 bcb1a39e Michael Hanselmann
        try:
530 bcb1a39e Michael Hanselmann
          self.socket.close()
531 bcb1a39e Michael Hanselmann
        except socket.error:
532 bcb1a39e Michael Hanselmann
          pass
533 bcb1a39e Michael Hanselmann
        self.socket = None
534 bcb1a39e Michael Hanselmann
535 1f8588f6 Iustin Pop
        self.request_executor(self, connection, client_addr)
536 02cab3e7 Michael Hanselmann
      except Exception:
537 02cab3e7 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
538 02cab3e7 Michael Hanselmann
                          client_addr[0], client_addr[1])
539 02cab3e7 Michael Hanselmann
        os._exit(1)
540 02cab3e7 Michael Hanselmann
      os._exit(0)
541 02cab3e7 Michael Hanselmann
    else:
542 02cab3e7 Michael Hanselmann
      self._children.append(pid)
543 02cab3e7 Michael Hanselmann
544 f8bd7df3 Michael Hanselmann
  def PreHandleRequest(self, req):
545 f8bd7df3 Michael Hanselmann
    """Called before handling a request.
546 f8bd7df3 Michael Hanselmann

547 5bbd3f7f Michael Hanselmann
    Can be overridden by a subclass.
548 f8bd7df3 Michael Hanselmann

549 f8bd7df3 Michael Hanselmann
    """
550 f8bd7df3 Michael Hanselmann
551 02cab3e7 Michael Hanselmann
  def HandleRequest(self, req):
552 02cab3e7 Michael Hanselmann
    """Handles a request.
553 02cab3e7 Michael Hanselmann

554 5bbd3f7f Michael Hanselmann
    Must be overridden by subclass.
555 02cab3e7 Michael Hanselmann

556 02cab3e7 Michael Hanselmann
    """
557 02cab3e7 Michael Hanselmann
    raise NotImplementedError()