Revision fa10bdc5

b/lib/http.py
173 173
    return time.strftime(format, tm)
174 174

  
175 175

  
176
class HTTPServer(BaseHTTPServer.HTTPServer, object):
177
  """Class to provide an HTTP/HTTPS server.
178

  
179
  """
180
  allow_reuse_address = True
181

  
182
  def __init__(self, server_address, HandlerClass, httplog=None,
183
               enable_ssl=False, ssl_key=None, ssl_cert=None):
184
    """Server constructor.
185

  
186
    Args:
187
      server_address: a touple containing:
188
        ip: a string with IP address, localhost if empty string
189
        port: port number, integer
190
      HandlerClass: HTTPRequestHandler object
191
      httplog: Access log object
192
      enable_ssl: Whether to enable SSL
193
      ssl_key: SSL key file
194
      ssl_cert: SSL certificate key
195

  
196
    """
197
    BaseHTTPServer.HTTPServer.__init__(self, server_address, HandlerClass)
198

  
199
    self.httplog = httplog
200

  
201
    if enable_ssl:
202
      # Set up SSL
203
      context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
204
      context.use_privatekey_file(ssl_key)
205
      context.use_certificate_file(ssl_cert)
206
      self.socket = OpenSSL.SSL.Connection(context,
207
                                           socket.socket(self.address_family,
208
                                           self.socket_type))
209
    else:
210
      self.socket = socket.socket(self.address_family, self.socket_type)
211

  
212
    self.server_bind()
213
    self.server_activate()
214

  
215

  
216 176
class HTTPJsonConverter:
217 177
  CONTENT_TYPE = "application/json"
218 178

  
......
223 183
    return serializer.LoadJson(data)
224 184

  
225 185

  
226
class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
227
  """Request handler class.
228

  
229
  """
230
  def setup(self):
231
    """Setup secure read and write file objects.
232

  
233
    """
234
    self.connection = self.request
235
    self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
236
    self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
237

  
238
  def handle_one_request(self):
239
    """Parses a request and calls the handler function.
240

  
241
    """
242
    self.raw_requestline = None
243
    try:
244
      self.raw_requestline = self.rfile.readline()
245
    except OpenSSL.SSL.Error, ex:
246
      logger.Error("Error in SSL: %s" % str(ex))
247
    if not self.raw_requestline:
248
      self.close_connection = 1
249
      return
250
    if not self.parse_request(): # An error code has been sent, just exit
251
      return
252
    logging.debug("HTTP request: %s", self.raw_requestline.rstrip("\r\n"))
253

  
254
    try:
255
      self._ReadPostData()
256

  
257
      result = self.HandleRequest()
258

  
259
      # TODO: Content-type
260
      encoder = HTTPJsonConverter()
261
      encoded_result = encoder.Encode(result)
262

  
263
      self.send_response(200)
264
      self.send_header("Content-Type", encoder.CONTENT_TYPE)
265
      self.send_header("Content-Length", str(len(encoded_result)))
266
      self.end_headers()
267

  
268
      self.wfile.write(encoded_result)
269

  
270
    except HTTPException, err:
271
      self.send_error(err.code, message=err.message)
272

  
273
    except Exception, err:
274
      self.send_error(HTTPInternalError.code, message=str(err))
275

  
276
    except:
277
      self.send_error(HTTPInternalError.code, message="Unknown error")
278

  
279
  def _ReadPostData(self):
280
    if self.command.upper() not in ("POST", "PUT"):
281
      self.post_data = None
282
      return
283

  
284
    # TODO: Decide what to do when Content-Length header was not sent
285
    try:
286
      content_length = int(self.headers.get('Content-Length', 0))
287
    except ValueError:
288
      raise HTTPBadRequest("No Content-Length header or invalid format")
289

  
290
    try:
291
      data = self.rfile.read(content_length)
292
    except socket.error, err:
293
      logger.Error("Socket error while reading: %s" % str(err))
294
      return
295

  
296
    # TODO: Content-type, error handling
297
    self.post_data = HTTPJsonConverter().Decode(data)
298

  
299
    logging.debug("HTTP POST data: %s", self.post_data)
300

  
301
  def HandleRequest(self):
302
    """Handles a request.
303

  
304
    """
305
    raise NotImplementedError()
306

  
307
  def log_message(self, format, *args):
308
    """Log an arbitrary message.
309

  
310
    This is used by all other logging functions.
311

  
312
    The first argument, FORMAT, is a format string for the
313
    message to be logged.  If the format string contains
314
    any % escapes requiring parameters, they should be
315
    specified as subsequent arguments (it's just like
316
    printf!).
317

  
318
    """
319
    logging.debug("Handled request: %s", format % args)
320
    if self.server.httplog:
321
      self.server.httplog.LogRequest(self, format, *args)
322

  
323

  
324 186
class _HttpConnectionHandler(object):
325 187
  """Implements server side of HTTP
326 188

  
b/test/ganeti.http_unittest.py
31 31

  
32 32

  
33 33
class HttpLogfileTests(unittest.TestCase):
34
  """Rests for ApacheLogfile class."""
34
  """Tests for ApacheLogfile class."""
35 35

  
36 36
  class FakeRequest:
37 37
    FAKE_ADDRESS = "1.2.3.4"

Also available in: Unified diff