Package ganeti :: Package http
[hide private]
[frames] | no frames]

Source Code for Package ganeti.http

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2007, 2008, 2010 Google Inc. 
   5  # 
   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. 
  10  # 
  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. 
  15  # 
  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 
  19  # 02110-1301, USA. 
  20   
  21  """HTTP module. 
  22   
  23  """ 
  24   
  25  import logging 
  26  import mimetools 
  27  import OpenSSL 
  28  import select 
  29  import socket 
  30  import errno 
  31   
  32  from cStringIO import StringIO 
  33   
  34  from ganeti import constants 
  35  from ganeti import utils 
  36   
  37   
  38  HTTP_GANETI_VERSION = "Ganeti %s" % constants.RELEASE_VERSION 
  39   
  40  HTTP_OK = 200 
  41  HTTP_NO_CONTENT = 204 
  42  HTTP_NOT_MODIFIED = 304 
  43   
  44  HTTP_0_9 = "HTTP/0.9" 
  45  HTTP_1_0 = "HTTP/1.0" 
  46  HTTP_1_1 = "HTTP/1.1" 
  47   
  48  HTTP_GET = "GET" 
  49  HTTP_HEAD = "HEAD" 
  50  HTTP_POST = "POST" 
  51  HTTP_PUT = "PUT" 
  52  HTTP_DELETE = "DELETE" 
  53   
  54  HTTP_ETAG = "ETag" 
  55  HTTP_HOST = "Host" 
  56  HTTP_SERVER = "Server" 
  57  HTTP_DATE = "Date" 
  58  HTTP_USER_AGENT = "User-Agent" 
  59  HTTP_CONTENT_TYPE = "Content-Type" 
  60  HTTP_CONTENT_LENGTH = "Content-Length" 
  61  HTTP_CONNECTION = "Connection" 
  62  HTTP_KEEP_ALIVE = "Keep-Alive" 
  63  HTTP_WWW_AUTHENTICATE = "WWW-Authenticate" 
  64  HTTP_AUTHORIZATION = "Authorization" 
  65  HTTP_AUTHENTICATION_INFO = "Authentication-Info" 
  66  HTTP_ALLOW = "Allow" 
  67   
  68  HTTP_APP_OCTET_STREAM = "application/octet-stream" 
  69  HTTP_APP_JSON = "application/json" 
  70   
  71  _SSL_UNEXPECTED_EOF = "Unexpected EOF" 
  72   
  73  # Socket operations 
  74  (SOCKOP_SEND, 
  75   SOCKOP_RECV, 
  76   SOCKOP_SHUTDOWN, 
  77   SOCKOP_HANDSHAKE) = range(4) 
  78   
  79  # send/receive quantum 
  80  SOCK_BUF_SIZE = 32768 
  81   
  82   
83 -class HttpError(Exception):
84 """Internal exception for HTTP errors. 85 86 This should only be used for internal error reporting. 87 88 """
89 90
91 -class HttpConnectionClosed(Exception):
92 """Internal exception for a closed connection. 93 94 This should only be used for internal error reporting. Only use 95 it if there's no other way to report this condition. 96 97 """
98 99
100 -class HttpSessionHandshakeUnexpectedEOF(HttpError):
101 """Internal exception for errors during SSL handshake. 102 103 This should only be used for internal error reporting. 104 105 """
106 107
108 -class HttpSocketTimeout(Exception):
109 """Internal exception for socket timeouts. 110 111 This should only be used for internal error reporting. 112 113 """
114 115
116 -class HttpException(Exception):
117 code = None 118 message = None 119
120 - def __init__(self, message=None, headers=None):
121 Exception.__init__(self) 122 self.message = message 123 self.headers = headers
124 125
126 -class HttpBadRequest(HttpException):
127 """400 Bad Request 128 129 RFC2616, 10.4.1: The request could not be understood by the server 130 due to malformed syntax. The client SHOULD NOT repeat the request 131 without modifications. 132 133 """ 134 code = 400
135 136
137 -class HttpUnauthorized(HttpException):
138 """401 Unauthorized 139 140 RFC2616, section 10.4.2: The request requires user 141 authentication. The response MUST include a WWW-Authenticate header 142 field (section 14.47) containing a challenge applicable to the 143 requested resource. 144 145 """ 146 code = 401
147 148
149 -class HttpForbidden(HttpException):
150 """403 Forbidden 151 152 RFC2616, 10.4.4: The server understood the request, but is refusing 153 to fulfill it. Authorization will not help and the request SHOULD 154 NOT be repeated. 155 156 """ 157 code = 403
158 159
160 -class HttpNotFound(HttpException):
161 """404 Not Found 162 163 RFC2616, 10.4.5: The server has not found anything matching the 164 Request-URI. No indication is given of whether the condition is 165 temporary or permanent. 166 167 """ 168 code = 404
169 170
171 -class HttpMethodNotAllowed(HttpException):
172 """405 Method Not Allowed 173 174 RFC2616, 10.4.6: The method specified in the Request-Line is not 175 allowed for the resource identified by the Request-URI. The response 176 MUST include an Allow header containing a list of valid methods for 177 the requested resource. 178 179 """ 180 code = 405
181 182
183 -class HttpNotAcceptable(HttpException):
184 """406 Not Acceptable 185 186 RFC2616, 10.4.7: The resource identified by the request is only capable of 187 generating response entities which have content characteristics not 188 acceptable according to the accept headers sent in the request. 189 190 """ 191 code = 406
192 193
194 -class HttpRequestTimeout(HttpException):
195 """408 Request Timeout 196 197 RFC2616, 10.4.9: The client did not produce a request within the 198 time that the server was prepared to wait. The client MAY repeat the 199 request without modifications at any later time. 200 201 """ 202 code = 408
203 204
205 -class HttpConflict(HttpException):
206 """409 Conflict 207 208 RFC2616, 10.4.10: The request could not be completed due to a 209 conflict with the current state of the resource. This code is only 210 allowed in situations where it is expected that the user might be 211 able to resolve the conflict and resubmit the request. 212 213 """ 214 code = 409
215 216
217 -class HttpGone(HttpException):
218 """410 Gone 219 220 RFC2616, 10.4.11: The requested resource is no longer available at 221 the server and no forwarding address is known. This condition is 222 expected to be considered permanent. 223 224 """ 225 code = 410
226 227
228 -class HttpLengthRequired(HttpException):
229 """411 Length Required 230 231 RFC2616, 10.4.12: The server refuses to accept the request without a 232 defined Content-Length. The client MAY repeat the request if it adds 233 a valid Content-Length header field containing the length of the 234 message-body in the request message. 235 236 """ 237 code = 411
238 239
240 -class HttpPreconditionFailed(HttpException):
241 """412 Precondition Failed 242 243 RFC2616, 10.4.13: The precondition given in one or more of the 244 request-header fields evaluated to false when it was tested on the 245 server. 246 247 """ 248 code = 412
249 250
251 -class HttpUnsupportedMediaType(HttpException):
252 """415 Unsupported Media Type 253 254 RFC2616, 10.4.16: The server is refusing to service the request because the 255 entity of the request is in a format not supported by the requested resource 256 for the requested method. 257 258 """ 259 code = 415
260 261
262 -class HttpInternalServerError(HttpException):
263 """500 Internal Server Error 264 265 RFC2616, 10.5.1: The server encountered an unexpected condition 266 which prevented it from fulfilling the request. 267 268 """ 269 code = 500
270 271
272 -class HttpNotImplemented(HttpException):
273 """501 Not Implemented 274 275 RFC2616, 10.5.2: The server does not support the functionality 276 required to fulfill the request. 277 278 """ 279 code = 501
280 281
282 -class HttpBadGateway(HttpException):
283 """502 Bad Gateway 284 285 RFC2616, 10.5.3: The server, while acting as a gateway or proxy, 286 received an invalid response from the upstream server it accessed in 287 attempting to fulfill the request. 288 289 """ 290 code = 502
291 292
293 -class HttpServiceUnavailable(HttpException):
294 """503 Service Unavailable 295 296 RFC2616, 10.5.4: The server is currently unable to handle the 297 request due to a temporary overloading or maintenance of the server. 298 299 """ 300 code = 503
301 302
303 -class HttpGatewayTimeout(HttpException):
304 """504 Gateway Timeout 305 306 RFC2616, 10.5.5: The server, while acting as a gateway or proxy, did 307 not receive a timely response from the upstream server specified by 308 the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server 309 (e.g. DNS) it needed to access in attempting to complete the 310 request. 311 312 """ 313 code = 504
314 315
316 -class HttpVersionNotSupported(HttpException):
317 """505 HTTP Version Not Supported 318 319 RFC2616, 10.5.6: The server does not support, or refuses to support, 320 the HTTP protocol version that was used in the request message. 321 322 """ 323 code = 505
324 325
326 -def SocketOperation(sock, op, arg1, timeout):
327 """Wrapper around socket functions. 328 329 This function abstracts error handling for socket operations, especially 330 for the complicated interaction with OpenSSL. 331 332 @type sock: socket 333 @param sock: Socket for the operation 334 @type op: int 335 @param op: Operation to execute (SOCKOP_* constants) 336 @type arg1: any 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 341 342 """ 343 # TODO: event_poll/event_check/override 344 if op in (SOCKOP_SEND, SOCKOP_HANDSHAKE): 345 event_poll = select.POLLOUT 346 347 elif op == SOCKOP_RECV: 348 event_poll = select.POLLIN 349 350 elif op == SOCKOP_SHUTDOWN: 351 event_poll = None 352 353 # The timeout is only used when OpenSSL requests polling for a condition. 354 # It is not advisable to have no timeout for shutdown. 355 assert timeout 356 357 else: 358 raise AssertionError("Invalid socket operation") 359 360 # Handshake is only supported by SSL sockets 361 if (op == SOCKOP_HANDSHAKE and 362 not isinstance(sock, OpenSSL.SSL.ConnectionType)): 363 return 364 365 # No override by default 366 event_override = 0 367 368 while True: 369 # Poll only for certain operations and when asked for by an override 370 if event_override or op in (SOCKOP_SEND, SOCKOP_RECV, SOCKOP_HANDSHAKE): 371 if event_override: 372 wait_for_event = event_override 373 else: 374 wait_for_event = event_poll 375 376 event = utils.WaitForFdCondition(sock, wait_for_event, timeout) 377 if event is None: 378 raise HttpSocketTimeout() 379 380 if event & (select.POLLNVAL | select.POLLHUP | select.POLLERR): 381 # Let the socket functions handle these 382 break 383 384 if not event & wait_for_event: 385 continue 386 387 # Reset override 388 event_override = 0 389 390 try: 391 try: 392 if op == SOCKOP_SEND: 393 return sock.send(arg1) 394 395 elif op == SOCKOP_RECV: 396 return sock.recv(arg1) 397 398 elif op == SOCKOP_SHUTDOWN: 399 if isinstance(sock, OpenSSL.SSL.ConnectionType): 400 # PyOpenSSL's shutdown() doesn't take arguments 401 return sock.shutdown() 402 else: 403 return sock.shutdown(arg1) 404 405 elif op == SOCKOP_HANDSHAKE: 406 return sock.do_handshake() 407 408 except OpenSSL.SSL.WantWriteError: 409 # OpenSSL wants to write, poll for POLLOUT 410 event_override = select.POLLOUT 411 continue 412 413 except OpenSSL.SSL.WantReadError: 414 # OpenSSL wants to read, poll for POLLIN 415 event_override = select.POLLIN | select.POLLPRI 416 continue 417 418 except OpenSSL.SSL.WantX509LookupError: 419 continue 420 421 except OpenSSL.SSL.ZeroReturnError, err: 422 # SSL Connection has been closed. In SSL 3.0 and TLS 1.0, this only 423 # occurs if a closure alert has occurred in the protocol, i.e. the 424 # connection has been closed cleanly. Note that this does not 425 # necessarily mean that the transport layer (e.g. a socket) has been 426 # closed. 427 if op == SOCKOP_SEND: 428 # Can happen during a renegotiation 429 raise HttpConnectionClosed(err.args) 430 elif op == SOCKOP_RECV: 431 return "" 432 433 # SSL_shutdown shouldn't return SSL_ERROR_ZERO_RETURN 434 raise socket.error(err.args) 435 436 except OpenSSL.SSL.SysCallError, err: 437 if op == SOCKOP_SEND: 438 # arg1 is the data when writing 439 if err.args and err.args[0] == -1 and arg1 == "": 440 # errors when writing empty strings are expected 441 # and can be ignored 442 return 0 443 444 if err.args == (-1, _SSL_UNEXPECTED_EOF): 445 if op == SOCKOP_RECV: 446 return "" 447 elif op == SOCKOP_HANDSHAKE: 448 # Can happen if peer disconnects directly after the connection is 449 # opened. 450 raise HttpSessionHandshakeUnexpectedEOF(err.args) 451 452 raise socket.error(err.args) 453 454 except OpenSSL.SSL.Error, err: 455 raise socket.error(err.args) 456 457 except socket.error, err: 458 if err.args and err.args[0] == errno.EAGAIN: 459 # Ignore EAGAIN 460 continue 461 462 raise
463 464
465 -def ShutdownConnection(sock, close_timeout, write_timeout, msgreader, force):
466 """Closes the connection. 467 468 @type sock: socket 469 @param sock: Socket to be shut down 470 @type close_timeout: float 471 @param close_timeout: How long to wait for the peer to close 472 the connection 473 @type write_timeout: float 474 @param write_timeout: Write timeout for shutdown 475 @type msgreader: http.HttpMessageReader 476 @param msgreader: Request message reader, used to determine whether 477 peer should close connection 478 @type force: bool 479 @param force: Whether to forcibly close the connection without 480 waiting for peer 481 482 """ 483 #print msgreader.peer_will_close, force 484 if msgreader and msgreader.peer_will_close and not force: 485 # Wait for peer to close 486 try: 487 # Check whether it's actually closed 488 if not SocketOperation(sock, SOCKOP_RECV, 1, close_timeout): 489 return 490 except (socket.error, HttpError, HttpSocketTimeout): 491 # Ignore errors at this stage 492 pass 493 494 # Close the connection from our side 495 try: 496 # We don't care about the return value, see NOTES in SSL_shutdown(3). 497 SocketOperation(sock, SOCKOP_SHUTDOWN, socket.SHUT_RDWR, 498 write_timeout) 499 except HttpSocketTimeout: 500 raise HttpError("Timeout while shutting down connection") 501 except socket.error, err: 502 # Ignore ENOTCONN 503 if not (err.args and err.args[0] == errno.ENOTCONN): 504 raise HttpError("Error while shutting down connection: %s" % err)
505 506
507 -def Handshake(sock, write_timeout):
508 """Shakes peer's hands. 509 510 @type sock: socket 511 @param sock: Socket to be shut down 512 @type write_timeout: float 513 @param write_timeout: Write timeout for handshake 514 515 """ 516 try: 517 return SocketOperation(sock, SOCKOP_HANDSHAKE, None, write_timeout) 518 except HttpSocketTimeout: 519 raise HttpError("Timeout during SSL handshake") 520 except socket.error, err: 521 raise HttpError("Error in SSL handshake: %s" % err)
522 523
524 -def InitSsl():
525 """Initializes the SSL infrastructure. 526 527 This function is idempotent. 528 529 """ 530 if not OpenSSL.rand.status(): 531 raise EnvironmentError("OpenSSL could not collect enough entropy" 532 " for the PRNG")
533 534 # TODO: Maybe add some additional seeding for OpenSSL's PRNG 535 536
537 -class HttpSslParams(object):
538 """Data class for SSL key and certificate. 539 540 """
541 - def __init__(self, ssl_key_path, ssl_cert_path):
542 """Initializes this class. 543 544 @type ssl_key_path: string 545 @param ssl_key_path: Path to file containing SSL key in PEM format 546 @type ssl_cert_path: string 547 @param ssl_cert_path: Path to file containing SSL certificate 548 in PEM format 549 550 """ 551 self.ssl_key_pem = utils.ReadFile(ssl_key_path) 552 self.ssl_cert_pem = utils.ReadFile(ssl_cert_path) 553 self.ssl_cert_path = ssl_cert_path
554
555 - def GetKey(self):
556 return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, 557 self.ssl_key_pem)
558
559 - def GetCertificate(self):
560 return OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, 561 self.ssl_cert_pem)
562 563
564 -class HttpBase(object):
565 """Base class for HTTP server and client. 566 567 """
568 - def __init__(self):
569 self.using_ssl = None 570 self._ssl_params = None 571 self._ssl_key = None 572 self._ssl_cert = None
573
574 - def _CreateSocket(self, ssl_params, ssl_verify_peer, family):
575 """Creates a TCP socket and initializes SSL if needed. 576 577 @type ssl_params: HttpSslParams 578 @param ssl_params: SSL key and certificate 579 @type ssl_verify_peer: bool 580 @param ssl_verify_peer: Whether to require client certificate 581 and compare it with our certificate 582 @type family: int 583 @param family: socket.AF_INET | socket.AF_INET6 584 585 """ 586 assert family in (socket.AF_INET, socket.AF_INET6) 587 588 self._ssl_params = ssl_params 589 sock = socket.socket(family, socket.SOCK_STREAM) 590 591 # Should we enable SSL? 592 self.using_ssl = ssl_params is not None 593 594 if not self.using_ssl: 595 return sock 596 597 self._ssl_key = ssl_params.GetKey() 598 self._ssl_cert = ssl_params.GetCertificate() 599 600 ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) 601 ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2) 602 603 ciphers = self.GetSslCiphers() 604 logging.debug("Setting SSL cipher string %s", ciphers) 605 ctx.set_cipher_list(ciphers) 606 607 ctx.use_privatekey(self._ssl_key) 608 ctx.use_certificate(self._ssl_cert) 609 ctx.check_privatekey() 610 611 if ssl_verify_peer: 612 ctx.set_verify(OpenSSL.SSL.VERIFY_PEER | 613 OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, 614 self._SSLVerifyCallback) 615 616 # Also add our certificate as a trusted CA to be sent to the client. 617 # This is required at least for GnuTLS clients to work. 618 try: 619 # This will fail for PyOpenssl versions before 0.10 620 ctx.add_client_ca(self._ssl_cert) 621 except AttributeError: 622 # Fall back to letting OpenSSL read the certificate file directly. 623 ctx.load_client_ca(ssl_params.ssl_cert_path) 624 625 return OpenSSL.SSL.Connection(ctx, sock)
626
627 - def GetSslCiphers(self): # pylint: disable=R0201
628 """Returns the ciphers string for SSL. 629 630 """ 631 return constants.OPENSSL_CIPHERS
632
633 - def _SSLVerifyCallback(self, conn, cert, errnum, errdepth, ok):
634 """Verify the certificate provided by the peer 635 636 We only compare fingerprints. The client must use the same certificate as 637 we do on our side. 638 639 """ 640 # some parameters are unused, but this is the API 641 # pylint: disable=W0613 642 assert self._ssl_params, "SSL not initialized" 643 644 return (self._ssl_cert.digest("sha1") == cert.digest("sha1") and 645 self._ssl_cert.digest("md5") == cert.digest("md5"))
646 647
648 -class HttpMessage(object):
649 """Data structure for HTTP message. 650 651 """
652 - def __init__(self):
653 self.start_line = None 654 self.headers = None 655 self.body = None
656 657
658 -class HttpClientToServerStartLine(object):
659 """Data structure for HTTP request start line. 660 661 """
662 - def __init__(self, method, path, version):
663 self.method = method 664 self.path = path 665 self.version = version
666
667 - def __str__(self):
668 return "%s %s %s" % (self.method, self.path, self.version)
669 670
671 -class HttpServerToClientStartLine(object):
672 """Data structure for HTTP response start line. 673 674 """
675 - def __init__(self, version, code, reason):
676 self.version = version 677 self.code = code 678 self.reason = reason
679
680 - def __str__(self):
681 return "%s %s %s" % (self.version, self.code, self.reason)
682 683
684 -class HttpMessageWriter(object):
685 """Writes an HTTP message to a socket. 686 687 """
688 - def __init__(self, sock, msg, write_timeout):
689 """Initializes this class and writes an HTTP message to a socket. 690 691 @type sock: socket 692 @param sock: Socket to be written to 693 @type msg: http.HttpMessage 694 @param msg: HTTP message to be written 695 @type write_timeout: float 696 @param write_timeout: Write timeout for socket 697 698 """ 699 self._msg = msg 700 701 self._PrepareMessage() 702 703 buf = self._FormatMessage() 704 705 pos = 0 706 end = len(buf) 707 while pos < end: 708 # Send only SOCK_BUF_SIZE bytes at a time 709 data = buf[pos:(pos + SOCK_BUF_SIZE)] 710 711 sent = SocketOperation(sock, SOCKOP_SEND, data, write_timeout) 712 713 # Remove sent bytes 714 pos += sent 715 716 assert pos == end, "Message wasn't sent completely"
717
718 - def _PrepareMessage(self):
719 """Prepares the HTTP message by setting mandatory headers. 720 721 """ 722 # RFC2616, section 4.3: "The presence of a message-body in a request is 723 # signaled by the inclusion of a Content-Length or Transfer-Encoding header 724 # field in the request's message-headers." 725 if self._msg.body: 726 self._msg.headers[HTTP_CONTENT_LENGTH] = len(self._msg.body)
727
728 - def _FormatMessage(self):
729 """Serializes the HTTP message into a string. 730 731 """ 732 buf = StringIO() 733 734 # Add start line 735 buf.write(str(self._msg.start_line)) 736 buf.write("\r\n") 737 738 # Add headers 739 if self._msg.start_line.version != HTTP_0_9: 740 for name, value in self._msg.headers.iteritems(): 741 buf.write("%s: %s\r\n" % (name, value)) 742 743 buf.write("\r\n") 744 745 # Add message body if needed 746 if self.HasMessageBody(): 747 buf.write(self._msg.body) 748 749 elif self._msg.body: 750 logging.warning("Ignoring message body") 751 752 return buf.getvalue()
753
754 - def HasMessageBody(self):
755 """Checks whether the HTTP message contains a body. 756 757 Can be overridden by subclasses. 758 759 """ 760 return bool(self._msg.body)
761 762
763 -class HttpMessageReader(object):
764 """Reads HTTP message from socket. 765 766 """ 767 # Length limits 768 START_LINE_LENGTH_MAX = None 769 HEADER_LENGTH_MAX = None 770 771 # Parser state machine 772 PS_START_LINE = "start-line" 773 PS_HEADERS = "headers" 774 PS_BODY = "entity-body" 775 PS_COMPLETE = "complete" 776
777 - def __init__(self, sock, msg, read_timeout):
778 """Reads an HTTP message from a socket. 779 780 @type sock: socket 781 @param sock: Socket to be read from 782 @type msg: http.HttpMessage 783 @param msg: Object for the read message 784 @type read_timeout: float 785 @param read_timeout: Read timeout for socket 786 787 """ 788 self.sock = sock 789 self.msg = msg 790 791 self.start_line_buffer = None 792 self.header_buffer = StringIO() 793 self.body_buffer = StringIO() 794 self.parser_status = self.PS_START_LINE 795 self.content_length = None 796 self.peer_will_close = None 797 798 buf = "" 799 eof = False 800 while self.parser_status != self.PS_COMPLETE: 801 # TODO: Don't read more than necessary (Content-Length), otherwise 802 # data might be lost and/or an error could occur 803 data = SocketOperation(sock, SOCKOP_RECV, SOCK_BUF_SIZE, read_timeout) 804 805 if data: 806 buf += data 807 else: 808 eof = True 809 810 # Do some parsing and error checking while more data arrives 811 buf = self._ContinueParsing(buf, eof) 812 813 # Must be done only after the buffer has been evaluated 814 # TODO: Content-Length < len(data read) and connection closed 815 if (eof and 816 self.parser_status in (self.PS_START_LINE, 817 self.PS_HEADERS)): 818 raise HttpError("Connection closed prematurely") 819 820 # Parse rest 821 buf = self._ContinueParsing(buf, True) 822 823 assert self.parser_status == self.PS_COMPLETE 824 assert not buf, "Parser didn't read full response" 825 826 # Body is complete 827 msg.body = self.body_buffer.getvalue()
828
829 - def _ContinueParsing(self, buf, eof):
830 """Main function for HTTP message state machine. 831 832 @type buf: string 833 @param buf: Receive buffer 834 @type eof: bool 835 @param eof: Whether we've reached EOF on the socket 836 @rtype: string 837 @return: Updated receive buffer 838 839 """ 840 # TODO: Use offset instead of slicing when possible 841 if self.parser_status == self.PS_START_LINE: 842 # Expect start line 843 while True: 844 idx = buf.find("\r\n") 845 846 # RFC2616, section 4.1: "In the interest of robustness, servers SHOULD 847 # ignore any empty line(s) received where a Request-Line is expected. 848 # In other words, if the server is reading the protocol stream at the 849 # beginning of a message and receives a CRLF first, it should ignore 850 # the CRLF." 851 if idx == 0: 852 # TODO: Limit number of CRLFs/empty lines for safety? 853 buf = buf[2:] 854 continue 855 856 if idx > 0: 857 self.start_line_buffer = buf[:idx] 858 859 self._CheckStartLineLength(len(self.start_line_buffer)) 860 861 # Remove status line, including CRLF 862 buf = buf[idx + 2:] 863 864 self.msg.start_line = self.ParseStartLine(self.start_line_buffer) 865 866 self.parser_status = self.PS_HEADERS 867 else: 868 # Check whether incoming data is getting too large, otherwise we just 869 # fill our read buffer. 870 self._CheckStartLineLength(len(buf)) 871 872 break 873 874 # TODO: Handle messages without headers 875 if self.parser_status == self.PS_HEADERS: 876 # Wait for header end 877 idx = buf.find("\r\n\r\n") 878 if idx >= 0: 879 self.header_buffer.write(buf[:idx + 2]) 880 881 self._CheckHeaderLength(self.header_buffer.tell()) 882 883 # Remove headers, including CRLF 884 buf = buf[idx + 4:] 885 886 self._ParseHeaders() 887 888 self.parser_status = self.PS_BODY 889 else: 890 # Check whether incoming data is getting too large, otherwise we just 891 # fill our read buffer. 892 self._CheckHeaderLength(len(buf)) 893 894 if self.parser_status == self.PS_BODY: 895 # TODO: Implement max size for body_buffer 896 self.body_buffer.write(buf) 897 buf = "" 898 899 # Check whether we've read everything 900 # 901 # RFC2616, section 4.4: "When a message-body is included with a message, 902 # the transfer-length of that body is determined by one of the following 903 # [...] 5. By the server closing the connection. (Closing the connection 904 # cannot be used to indicate the end of a request body, since that would 905 # leave no possibility for the server to send back a response.)" 906 # 907 # TODO: Error when buffer length > Content-Length header 908 if (eof or 909 self.content_length is None or 910 (self.content_length is not None and 911 self.body_buffer.tell() >= self.content_length)): 912 self.parser_status = self.PS_COMPLETE 913 914 return buf
915
916 - def _CheckStartLineLength(self, length):
917 """Limits the start line buffer size. 918 919 @type length: int 920 @param length: Buffer size 921 922 """ 923 if (self.START_LINE_LENGTH_MAX is not None and 924 length > self.START_LINE_LENGTH_MAX): 925 raise HttpError("Start line longer than %d chars" % 926 self.START_LINE_LENGTH_MAX)
927
928 - def _CheckHeaderLength(self, length):
929 """Limits the header buffer size. 930 931 @type length: int 932 @param length: Buffer size 933 934 """ 935 if (self.HEADER_LENGTH_MAX is not None and 936 length > self.HEADER_LENGTH_MAX): 937 raise HttpError("Headers longer than %d chars" % self.HEADER_LENGTH_MAX)
938
939 - def ParseStartLine(self, start_line):
940 """Parses the start line of a message. 941 942 Must be overridden by subclass. 943 944 @type start_line: string 945 @param start_line: Start line string 946 947 """ 948 raise NotImplementedError()
949
950 - def _WillPeerCloseConnection(self):
951 """Evaluate whether peer will close the connection. 952 953 @rtype: bool 954 @return: Whether peer will close the connection 955 956 """ 957 # RFC2616, section 14.10: "HTTP/1.1 defines the "close" connection option 958 # for the sender to signal that the connection will be closed after 959 # completion of the response. For example, 960 # 961 # Connection: close 962 # 963 # in either the request or the response header fields indicates that the 964 # connection SHOULD NOT be considered `persistent' (section 8.1) after the 965 # current request/response is complete." 966 967 hdr_connection = self.msg.headers.get(HTTP_CONNECTION, None) 968 if hdr_connection: 969 hdr_connection = hdr_connection.lower() 970 971 # An HTTP/1.1 server is assumed to stay open unless explicitly closed. 972 if self.msg.start_line.version == HTTP_1_1: 973 return (hdr_connection and "close" in hdr_connection) 974 975 # Some HTTP/1.0 implementations have support for persistent connections, 976 # using rules different than HTTP/1.1. 977 978 # For older HTTP, Keep-Alive indicates persistent connection. 979 if self.msg.headers.get(HTTP_KEEP_ALIVE): 980 return False 981 982 # At least Akamai returns a "Connection: Keep-Alive" header, which was 983 # supposed to be sent by the client. 984 if hdr_connection and "keep-alive" in hdr_connection: 985 return False 986 987 return True
988
989 - def _ParseHeaders(self):
990 """Parses the headers. 991 992 This function also adjusts internal variables based on header values. 993 994 RFC2616, section 4.3: The presence of a message-body in a request is 995 signaled by the inclusion of a Content-Length or Transfer-Encoding header 996 field in the request's message-headers. 997 998 """ 999 # Parse headers 1000 self.header_buffer.seek(0, 0) 1001 self.msg.headers = mimetools.Message(self.header_buffer, 0) 1002 1003 self.peer_will_close = self._WillPeerCloseConnection() 1004 1005 # Do we have a Content-Length header? 1006 hdr_content_length = self.msg.headers.get(HTTP_CONTENT_LENGTH, None) 1007 if hdr_content_length: 1008 try: 1009 self.content_length = int(hdr_content_length) 1010 except (TypeError, ValueError): 1011 self.content_length = None 1012 if self.content_length is not None and self.content_length < 0: 1013 self.content_length = None 1014 1015 # if the connection remains open and a content-length was not provided, 1016 # then assume that the connection WILL close. 1017 if self.content_length is None: 1018 self.peer_will_close = True
1019