4 # Copyright (C) 2007, 2008 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 from cStringIO import StringIO
34 from ganeti import constants
35 from ganeti import serializer
36 from ganeti import utils
39 HTTP_GANETI_VERSION = "Ganeti %s" % constants.RELEASE_VERSION
43 HTTP_NOT_MODIFIED = 304
53 HTTP_DELETE = "DELETE"
57 HTTP_SERVER = "Server"
59 HTTP_USER_AGENT = "User-Agent"
60 HTTP_CONTENT_TYPE = "Content-Type"
61 HTTP_CONTENT_LENGTH = "Content-Length"
62 HTTP_CONNECTION = "Connection"
63 HTTP_KEEP_ALIVE = "Keep-Alive"
64 HTTP_WWW_AUTHENTICATE = "WWW-Authenticate"
65 HTTP_AUTHORIZATION = "Authorization"
66 HTTP_AUTHENTICATION_INFO = "Authentication-Info"
69 _SSL_UNEXPECTED_EOF = "Unexpected EOF"
75 SOCKOP_HANDSHAKE) = range(4)
77 # send/receive quantum
81 class HttpError(Exception):
82 """Internal exception for HTTP errors.
84 This should only be used for internal error reporting.
89 class HttpConnectionClosed(Exception):
90 """Internal exception for a closed connection.
92 This should only be used for internal error reporting. Only use
93 it if there's no other way to report this condition.
98 class HttpSessionHandshakeUnexpectedEOF(HttpError):
99 """Internal exception for errors during SSL handshake.
101 This should only be used for internal error reporting.
106 class HttpSocketTimeout(Exception):
107 """Internal exception for socket timeouts.
109 This should only be used for internal error reporting.
114 class HttpException(Exception):
118 def __init__(self, message=None, headers=None):
119 Exception.__init__(self)
120 self.message = message
121 self.headers = headers
124 class HttpBadRequest(HttpException):
127 RFC2616, 10.4.1: The request could not be understood by the server
128 due to malformed syntax. The client SHOULD NOT repeat the request
129 without modifications.
135 class HttpUnauthorized(HttpException):
138 RFC2616, section 10.4.2: The request requires user
139 authentication. The response MUST include a WWW-Authenticate header
140 field (section 14.47) containing a challenge applicable to the
147 class HttpForbidden(HttpException):
150 RFC2616, 10.4.4: The server understood the request, but is refusing
151 to fulfill it. Authorization will not help and the request SHOULD
158 class HttpNotFound(HttpException):
161 RFC2616, 10.4.5: The server has not found anything matching the
162 Request-URI. No indication is given of whether the condition is
163 temporary or permanent.
169 class HttpMethodNotAllowed(HttpException):
170 """405 Method Not Allowed
172 RFC2616, 10.4.6: The method specified in the Request-Line is not
173 allowed for the resource identified by the Request-URI. The response
174 MUST include an Allow header containing a list of valid methods for
175 the requested resource.
181 class HttpRequestTimeout(HttpException):
182 """408 Request Timeout
184 RFC2616, 10.4.9: The client did not produce a request within the
185 time that the server was prepared to wait. The client MAY repeat the
186 request without modifications at any later time.
192 class HttpConflict(HttpException):
195 RFC2616, 10.4.10: The request could not be completed due to a
196 conflict with the current state of the resource. This code is only
197 allowed in situations where it is expected that the user might be
198 able to resolve the conflict and resubmit the request.
204 class HttpGone(HttpException):
207 RFC2616, 10.4.11: The requested resource is no longer available at
208 the server and no forwarding address is known. This condition is
209 expected to be considered permanent.
215 class HttpLengthRequired(HttpException):
216 """411 Length Required
218 RFC2616, 10.4.12: The server refuses to accept the request without a
219 defined Content-Length. The client MAY repeat the request if it adds
220 a valid Content-Length header field containing the length of the
221 message-body in the request message.
227 class HttpPreconditionFailed(HttpException):
228 """412 Precondition Failed
230 RFC2616, 10.4.13: The precondition given in one or more of the
231 request-header fields evaluated to false when it was tested on the
238 class HttpInternalServerError(HttpException):
239 """500 Internal Server Error
241 RFC2616, 10.5.1: The server encountered an unexpected condition
242 which prevented it from fulfilling the request.
248 class HttpNotImplemented(HttpException):
249 """501 Not Implemented
251 RFC2616, 10.5.2: The server does not support the functionality
252 required to fulfill the request.
258 class HttpServiceUnavailable(HttpException):
259 """503 Service Unavailable
261 RFC2616, 10.5.4: The server is currently unable to handle the
262 request due to a temporary overloading or maintenance of the server.
268 class HttpVersionNotSupported(HttpException):
269 """505 HTTP Version Not Supported
271 RFC2616, 10.5.6: The server does not support, or refuses to support,
272 the HTTP protocol version that was used in the request message.
278 class HttpJsonConverter:
279 CONTENT_TYPE = "application/json"
281 def Encode(self, data):
282 return serializer.DumpJson(data)
284 def Decode(self, data):
285 return serializer.LoadJson(data)
288 def WaitForSocketCondition(sock, event, timeout):
289 """Waits for a condition to occur on the socket.
292 @param sock: Wait for events on this socket
294 @param event: ORed condition (see select module)
295 @type timeout: float or None
296 @param timeout: Timeout in seconds
298 @return: None for timeout, otherwise occured conditions
301 check = (event | select.POLLPRI |
302 select.POLLNVAL | select.POLLHUP | select.POLLERR)
304 if timeout is not None:
305 # Poller object expects milliseconds
308 poller = select.poll()
309 poller.register(sock, event)
312 # TODO: If the main thread receives a signal and we have no timeout, we
313 # could wait forever. This should check a global "quit" flag or
314 # something every so often.
315 io_events = poller.poll(timeout)
319 for (evfd, evcond) in io_events:
323 poller.unregister(sock)
326 def SocketOperation(sock, op, arg1, timeout):
327 """Wrapper around socket functions.
329 This function abstracts error handling for socket operations, especially
330 for the complicated interaction with OpenSSL.
333 @param sock: Socket for the operation
335 @param op: Operation to execute (SOCKOP_* constants)
337 @param arg1: Parameter for function (if needed)
338 @type timeout: None or float
339 @param timeout: Timeout in seconds or None
340 @return: Return value of socket function
343 # TODO: event_poll/event_check/override
344 if op in (SOCKOP_SEND, SOCKOP_HANDSHAKE):
345 event_poll = select.POLLOUT
346 event_check = select.POLLOUT
348 elif op == SOCKOP_RECV:
349 event_poll = select.POLLIN
350 event_check = select.POLLIN | select.POLLPRI
352 elif op == SOCKOP_SHUTDOWN:
356 # The timeout is only used when OpenSSL requests polling for a condition.
357 # It is not advisable to have no timeout for shutdown.
361 raise AssertionError("Invalid socket operation")
363 # Handshake is only supported by SSL sockets
364 if (op == SOCKOP_HANDSHAKE and
365 not isinstance(sock, OpenSSL.SSL.ConnectionType)):
368 # No override by default
372 # Poll only for certain operations and when asked for by an override
373 if event_override or op in (SOCKOP_SEND, SOCKOP_RECV, SOCKOP_HANDSHAKE):
375 wait_for_event = event_override
377 wait_for_event = event_poll
379 event = WaitForSocketCondition(sock, wait_for_event, timeout)
381 raise HttpSocketTimeout()
383 if (op == SOCKOP_RECV and
384 event & (select.POLLNVAL | select.POLLHUP | select.POLLERR)):
387 if not event & wait_for_event:
395 if op == SOCKOP_SEND:
396 return sock.send(arg1)
398 elif op == SOCKOP_RECV:
399 return sock.recv(arg1)
401 elif op == SOCKOP_SHUTDOWN:
402 if isinstance(sock, OpenSSL.SSL.ConnectionType):
403 # PyOpenSSL's shutdown() doesn't take arguments
404 return sock.shutdown()
406 return sock.shutdown(arg1)
408 elif op == SOCKOP_HANDSHAKE:
409 return sock.do_handshake()
411 except OpenSSL.SSL.WantWriteError:
412 # OpenSSL wants to write, poll for POLLOUT
413 event_override = select.POLLOUT
416 except OpenSSL.SSL.WantReadError:
417 # OpenSSL wants to read, poll for POLLIN
418 event_override = select.POLLIN | select.POLLPRI
421 except OpenSSL.SSL.WantX509LookupError:
424 except OpenSSL.SSL.ZeroReturnError, err:
425 # SSL Connection has been closed. In SSL 3.0 and TLS 1.0, this only
426 # occurs if a closure alert has occurred in the protocol, i.e. the
427 # connection has been closed cleanly. Note that this does not
428 # necessarily mean that the transport layer (e.g. a socket) has been
430 if op == SOCKOP_SEND:
431 # Can happen during a renegotiation
432 raise HttpConnectionClosed(err.args)
433 elif op == SOCKOP_RECV:
436 # SSL_shutdown shouldn't return SSL_ERROR_ZERO_RETURN
437 raise socket.error(err.args)
439 except OpenSSL.SSL.SysCallError, err:
440 if op == SOCKOP_SEND:
441 # arg1 is the data when writing
442 if err.args and err.args[0] == -1 and arg1 == "":
443 # errors when writing empty strings are expected
447 if err.args == (-1, _SSL_UNEXPECTED_EOF):
448 if op == SOCKOP_RECV:
450 elif op == SOCKOP_HANDSHAKE:
451 # Can happen if peer disconnects directly after the connection is
453 raise HttpSessionHandshakeUnexpectedEOF(err.args)
455 raise socket.error(err.args)
457 except OpenSSL.SSL.Error, err:
458 raise socket.error(err.args)
460 except socket.error, err:
461 if err.args and err.args[0] == errno.EAGAIN:
468 def ShutdownConnection(sock, close_timeout, write_timeout, msgreader, force):
469 """Closes the connection.
472 @param sock: Socket to be shut down
473 @type close_timeout: float
474 @param close_timeout: How long to wait for the peer to close
476 @type write_timeout: float
477 @param write_timeout: Write timeout for shutdown
478 @type msgreader: http.HttpMessageReader
479 @param msgreader: Request message reader, used to determine whether
480 peer should close connection
482 @param force: Whether to forcibly close the connection without
486 #print msgreader.peer_will_close, force
487 if msgreader and msgreader.peer_will_close and not force:
488 # Wait for peer to close
490 # Check whether it's actually closed
491 if not SocketOperation(sock, SOCKOP_RECV, 1, close_timeout):
493 except (socket.error, HttpError, HttpSocketTimeout):
494 # Ignore errors at this stage
497 # Close the connection from our side
499 # We don't care about the return value, see NOTES in SSL_shutdown(3).
500 SocketOperation(sock, SOCKOP_SHUTDOWN, socket.SHUT_RDWR,
502 except HttpSocketTimeout:
503 raise HttpError("Timeout while shutting down connection")
504 except socket.error, err:
506 if not (err.args and err.args[0] == errno.ENOTCONN):
507 raise HttpError("Error while shutting down connection: %s" % err)
510 def Handshake(sock, write_timeout):
511 """Shakes peer's hands.
514 @param sock: Socket to be shut down
515 @type write_timeout: float
516 @param write_timeout: Write timeout for handshake
520 return SocketOperation(sock, SOCKOP_HANDSHAKE, None, write_timeout)
521 except HttpSocketTimeout:
522 raise HttpError("Timeout during SSL handshake")
523 except socket.error, err:
524 raise HttpError("Error in SSL handshake: %s" % err)
527 class HttpSslParams(object):
528 """Data class for SSL key and certificate.
531 def __init__(self, ssl_key_path, ssl_cert_path):
532 """Initializes this class.
534 @type ssl_key_path: string
535 @param ssl_key_path: Path to file containing SSL key in PEM format
536 @type ssl_cert_path: string
537 @param ssl_cert_path: Path to file containing SSL certificate
541 self.ssl_key_pem = utils.ReadFile(ssl_key_path)
542 self.ssl_cert_pem = utils.ReadFile(ssl_cert_path)
545 return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
548 def GetCertificate(self):
549 return OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
553 class HttpBase(object):
554 """Base class for HTTP server and client.
558 self.using_ssl = None
559 self._ssl_params = None
561 self._ssl_cert = None
563 def _CreateSocket(self, ssl_params, ssl_verify_peer):
564 """Creates a TCP socket and initializes SSL if needed.
566 @type ssl_params: HttpSslParams
567 @param ssl_params: SSL key and certificate
568 @type ssl_verify_peer: bool
569 @param ssl_verify_peer: Whether to require client certificate
570 and compare it with our certificate
573 self._ssl_params = ssl_params
575 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
577 # Should we enable SSL?
578 self.using_ssl = ssl_params is not None
580 if not self.using_ssl:
583 self._ssl_key = ssl_params.GetKey()
584 self._ssl_cert = ssl_params.GetCertificate()
586 ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
587 ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2)
589 ctx.use_privatekey(self._ssl_key)
590 ctx.use_certificate(self._ssl_cert)
591 ctx.check_privatekey()
594 ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
595 OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
596 self._SSLVerifyCallback)
598 return OpenSSL.SSL.Connection(ctx, sock)
600 def _SSLVerifyCallback(self, conn, cert, errnum, errdepth, ok):
601 """Verify the certificate provided by the peer
603 We only compare fingerprints. The client must use the same certificate as
607 assert self._ssl_params, "SSL not initialized"
609 return (self._ssl_cert.digest("sha1") == cert.digest("sha1") and
610 self._ssl_cert.digest("md5") == cert.digest("md5"))
613 class HttpMessage(object):
614 """Data structure for HTTP message.
618 self.start_line = None
621 self.decoded_body = None
624 class HttpClientToServerStartLine(object):
625 """Data structure for HTTP request start line.
628 def __init__(self, method, path, version):
631 self.version = version
634 return "%s %s %s" % (self.method, self.path, self.version)
637 class HttpServerToClientStartLine(object):
638 """Data structure for HTTP response start line.
641 def __init__(self, version, code, reason):
642 self.version = version
647 return "%s %s %s" % (self.version, self.code, self.reason)
650 class HttpMessageWriter(object):
651 """Writes an HTTP message to a socket.
654 def __init__(self, sock, msg, write_timeout):
655 """Initializes this class and writes an HTTP message to a socket.
658 @param sock: Socket to be written to
659 @type msg: http.HttpMessage
660 @param msg: HTTP message to be written
661 @type write_timeout: float
662 @param write_timeout: Write timeout for socket
667 self._PrepareMessage()
669 buf = self._FormatMessage()
674 # Send only SOCK_BUF_SIZE bytes at a time
675 data = buf[pos:(pos + SOCK_BUF_SIZE)]
677 sent = SocketOperation(sock, SOCKOP_SEND, data, write_timeout)
682 assert pos == end, "Message wasn't sent completely"
684 def _PrepareMessage(self):
685 """Prepares the HTTP message by setting mandatory headers.
688 # RFC2616, section 4.3: "The presence of a message-body in a request is
689 # signaled by the inclusion of a Content-Length or Transfer-Encoding header
690 # field in the request's message-headers."
692 self._msg.headers[HTTP_CONTENT_LENGTH] = len(self._msg.body)
694 def _FormatMessage(self):
695 """Serializes the HTTP message into a string.
701 buf.write(str(self._msg.start_line))
705 if self._msg.start_line.version != HTTP_0_9:
706 for name, value in self._msg.headers.iteritems():
707 buf.write("%s: %s\r\n" % (name, value))
711 # Add message body if needed
712 if self.HasMessageBody():
713 buf.write(self._msg.body)
716 logging.warning("Ignoring message body")
718 return buf.getvalue()
720 def HasMessageBody(self):
721 """Checks whether the HTTP message contains a body.
723 Can be overriden by subclasses.
726 return bool(self._msg.body)
729 class HttpMessageReader(object):
730 """Reads HTTP message from socket.
734 START_LINE_LENGTH_MAX = None
735 HEADER_LENGTH_MAX = None
737 # Parser state machine
738 PS_START_LINE = "start-line"
739 PS_HEADERS = "headers"
740 PS_BODY = "entity-body"
741 PS_COMPLETE = "complete"
743 def __init__(self, sock, msg, read_timeout):
744 """Reads an HTTP message from a socket.
747 @param sock: Socket to be read from
748 @type msg: http.HttpMessage
749 @param msg: Object for the read message
750 @type read_timeout: float
751 @param read_timeout: Read timeout for socket
757 self.start_line_buffer = None
758 self.header_buffer = StringIO()
759 self.body_buffer = StringIO()
760 self.parser_status = self.PS_START_LINE
761 self.content_length = None
762 self.peer_will_close = None
766 while self.parser_status != self.PS_COMPLETE:
767 # TODO: Don't read more than necessary (Content-Length), otherwise
768 # data might be lost and/or an error could occur
769 data = SocketOperation(sock, SOCKOP_RECV, SOCK_BUF_SIZE, read_timeout)
776 # Do some parsing and error checking while more data arrives
777 buf = self._ContinueParsing(buf, eof)
779 # Must be done only after the buffer has been evaluated
780 # TODO: Connection-length < len(data read) and connection closed
782 self.parser_status in (self.PS_START_LINE,
784 raise HttpError("Connection closed prematurely")
787 buf = self._ContinueParsing(buf, True)
789 assert self.parser_status == self.PS_COMPLETE
790 assert not buf, "Parser didn't read full response"
792 msg.body = self.body_buffer.getvalue()
794 # TODO: Content-type, error handling
796 msg.decoded_body = HttpJsonConverter().Decode(msg.body)
798 msg.decoded_body = None
801 logging.debug("Message body: %s", msg.decoded_body)
803 def _ContinueParsing(self, buf, eof):
804 """Main function for HTTP message state machine.
807 @param buf: Receive buffer
809 @param eof: Whether we've reached EOF on the socket
811 @return: Updated receive buffer
814 # TODO: Use offset instead of slicing when possible
815 if self.parser_status == self.PS_START_LINE:
818 idx = buf.find("\r\n")
820 # RFC2616, section 4.1: "In the interest of robustness, servers SHOULD
821 # ignore any empty line(s) received where a Request-Line is expected.
822 # In other words, if the server is reading the protocol stream at the
823 # beginning of a message and receives a CRLF first, it should ignore
826 # TODO: Limit number of CRLFs/empty lines for safety?
831 self.start_line_buffer = buf[:idx]
833 self._CheckStartLineLength(len(self.start_line_buffer))
835 # Remove status line, including CRLF
838 self.msg.start_line = self.ParseStartLine(self.start_line_buffer)
840 self.parser_status = self.PS_HEADERS
842 # Check whether incoming data is getting too large, otherwise we just
843 # fill our read buffer.
844 self._CheckStartLineLength(len(buf))
848 # TODO: Handle messages without headers
849 if self.parser_status == self.PS_HEADERS:
850 # Wait for header end
851 idx = buf.find("\r\n\r\n")
853 self.header_buffer.write(buf[:idx + 2])
855 self._CheckHeaderLength(self.header_buffer.tell())
857 # Remove headers, including CRLF
862 self.parser_status = self.PS_BODY
864 # Check whether incoming data is getting too large, otherwise we just
865 # fill our read buffer.
866 self._CheckHeaderLength(len(buf))
868 if self.parser_status == self.PS_BODY:
869 # TODO: Implement max size for body_buffer
870 self.body_buffer.write(buf)
873 # Check whether we've read everything
875 # RFC2616, section 4.4: "When a message-body is included with a message,
876 # the transfer-length of that body is determined by one of the following
877 # [...] 5. By the server closing the connection. (Closing the connection
878 # cannot be used to indicate the end of a request body, since that would
879 # leave no possibility for the server to send back a response.)"
881 # TODO: Error when buffer length > Content-Length header
883 self.content_length is None or
884 (self.content_length is not None and
885 self.body_buffer.tell() >= self.content_length)):
886 self.parser_status = self.PS_COMPLETE
890 def _CheckStartLineLength(self, length):
891 """Limits the start line buffer size.
894 @param length: Buffer size
897 if (self.START_LINE_LENGTH_MAX is not None and
898 length > self.START_LINE_LENGTH_MAX):
899 raise HttpError("Start line longer than %d chars" %
900 self.START_LINE_LENGTH_MAX)
902 def _CheckHeaderLength(self, length):
903 """Limits the header buffer size.
906 @param length: Buffer size
909 if (self.HEADER_LENGTH_MAX is not None and
910 length > self.HEADER_LENGTH_MAX):
911 raise HttpError("Headers longer than %d chars" % self.HEADER_LENGTH_MAX)
913 def ParseStartLine(self, start_line):
914 """Parses the start line of a message.
916 Must be overriden by subclass.
918 @type start_line: string
919 @param start_line: Start line string
922 raise NotImplementedError()
924 def _WillPeerCloseConnection(self):
925 """Evaluate whether peer will close the connection.
928 @return: Whether peer will close the connection
931 # RFC2616, section 14.10: "HTTP/1.1 defines the "close" connection option
932 # for the sender to signal that the connection will be closed after
933 # completion of the response. For example,
937 # in either the request or the response header fields indicates that the
938 # connection SHOULD NOT be considered `persistent' (section 8.1) after the
939 # current request/response is complete."
941 hdr_connection = self.msg.headers.get(HTTP_CONNECTION, None)
943 hdr_connection = hdr_connection.lower()
945 # An HTTP/1.1 server is assumed to stay open unless explicitly closed.
946 if self.msg.start_line.version == HTTP_1_1:
947 return (hdr_connection and "close" in hdr_connection)
949 # Some HTTP/1.0 implementations have support for persistent connections,
950 # using rules different than HTTP/1.1.
952 # For older HTTP, Keep-Alive indicates persistent connection.
953 if self.msg.headers.get(HTTP_KEEP_ALIVE):
956 # At least Akamai returns a "Connection: Keep-Alive" header, which was
957 # supposed to be sent by the client.
958 if hdr_connection and "keep-alive" in hdr_connection:
963 def _ParseHeaders(self):
964 """Parses the headers.
966 This function also adjusts internal variables based on header values.
968 RFC2616, section 4.3: The presence of a message-body in a request is
969 signaled by the inclusion of a Content-Length or Transfer-Encoding header
970 field in the request's message-headers.
974 self.header_buffer.seek(0, 0)
975 self.msg.headers = mimetools.Message(self.header_buffer, 0)
977 self.peer_will_close = self._WillPeerCloseConnection()
979 # Do we have a Content-Length header?
980 hdr_content_length = self.msg.headers.get(HTTP_CONTENT_LENGTH, None)
981 if hdr_content_length:
983 self.content_length = int(hdr_content_length)
985 self.content_length = None
986 if self.content_length is not None and self.content_length < 0:
987 self.content_length = None
989 # if the connection remains open and a content-length was not provided,
990 # then assume that the connection WILL close.
991 if self.content_length is None:
992 self.peer_will_close = True