Revision 02cab3e7 lib/http/__init__.py

b/lib/http/__init__.py
18 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 19
# 02110-1301, USA.
20 20

  
21
"""HTTP server module.
21
"""HTTP module.
22 22

  
23 23
"""
24 24

  
......
30 30
import os
31 31
import select
32 32
import socket
33
import sys
34 33
import time
35 34
import signal
36
import logging
37 35
import errno
38 36
import threading
39 37

  
......
103 101
  pass
104 102

  
105 103

  
104
class HttpError(Exception):
105
  """Internal exception for HTTP errors.
106

  
107
  This should only be used for internal error reporting.
108

  
109
  """
110

  
111

  
106 112
class _HttpClientError(Exception):
107 113
  """Internal exception for HTTP client errors.
108 114

  
......
330 336
      raise
331 337

  
332 338

  
339
def ShutdownConnection(poller, sock, close_timeout, write_timeout, msgreader,
340
                       force):
341
  """Closes the connection.
342

  
343
  """
344
  poller = select.poll()
345

  
346
  #print msgreader.peer_will_close, force
347
  if msgreader and msgreader.peer_will_close and not force:
348
    # Wait for peer to close
349
    try:
350
      # Check whether it's actually closed
351
      if not SocketOperation(poller, sock, SOCKOP_RECV, 1, close_timeout):
352
        return
353
    except (socket.error, HttpError, HttpSocketTimeout):
354
      # Ignore errors at this stage
355
      pass
356

  
357
  # Close the connection from our side
358
  try:
359
    SocketOperation(poller, sock, SOCKOP_SHUTDOWN, socket.SHUT_RDWR,
360
                    write_timeout)
361
  except HttpSocketTimeout:
362
    raise HttpError("Timeout while shutting down connection")
363
  except socket.error, err:
364
    raise HttpError("Error while shutting down connection: %s" % err)
365

  
366

  
333 367
class HttpSslParams(object):
334 368
  """Data class for SSL key and certificate.
335 369

  
......
1475 1509
      except OpenSSL.SSL.Error, err:
1476 1510
        self._ConnectionLost()
1477 1511
        raise socket.error(err.args)
1512

  
1513

  
1514
class HttpMessage(object):
1515
  """Data structure for HTTP message.
1516

  
1517
  """
1518
  def __init__(self):
1519
    self.start_line = None
1520
    self.headers = None
1521
    self.body = None
1522
    self.decoded_body = None
1523

  
1524

  
1525
class HttpClientToServerStartLine(object):
1526
  """Data structure for HTTP request start line.
1527

  
1528
  """
1529
  def __init__(self, method, path, version):
1530
    self.method = method
1531
    self.path = path
1532
    self.version = version
1533

  
1534
  def __str__(self):
1535
    return "%s %s %s" % (self.method, self.path, self.version)
1536

  
1537

  
1538
class HttpServerToClientStartLine(object):
1539
  """Data structure for HTTP response start line.
1540

  
1541
  """
1542
  def __init__(self, version, code, reason):
1543
    self.version = version
1544
    self.code = code
1545
    self.reason = reason
1546

  
1547
  def __str__(self):
1548
    return "%s %s %s" % (self.version, self.code, self.reason)
1549

  
1550

  
1551
class HttpMessageWriter(object):
1552
  """Writes an HTTP message to a socket.
1553

  
1554
  """
1555
  def __init__(self, sock, msg, write_timeout):
1556
    self._msg = msg
1557

  
1558
    self._PrepareMessage()
1559

  
1560
    buf = self._FormatMessage()
1561

  
1562
    poller = select.poll()
1563
    while buf:
1564
      # Send only 4 KB at a time
1565
      data = buf[:4096]
1566

  
1567
      sent = SocketOperation(poller, sock, SOCKOP_SEND, data,
1568
                             write_timeout)
1569

  
1570
      # Remove sent bytes
1571
      buf = buf[sent:]
1572

  
1573
    assert not buf, "Message wasn't sent completely"
1574

  
1575
  def _PrepareMessage(self):
1576
    """Prepares the HTTP message by setting mandatory headers.
1577

  
1578
    """
1579
    # RFC2616, section 4.3: "The presence of a message-body in a request is
1580
    # signaled by the inclusion of a Content-Length or Transfer-Encoding header
1581
    # field in the request's message-headers."
1582
    if self._msg.body:
1583
      self._msg.headers[HTTP_CONTENT_LENGTH] = len(self._msg.body)
1584

  
1585
  def _FormatMessage(self):
1586
    """Serializes the HTTP message into a string.
1587

  
1588
    """
1589
    buf = StringIO()
1590

  
1591
    # Add start line
1592
    buf.write(str(self._msg.start_line))
1593
    buf.write("\r\n")
1594

  
1595
    # Add headers
1596
    if self._msg.start_line.version != HTTP_0_9:
1597
      for name, value in self._msg.headers.iteritems():
1598
        buf.write("%s: %s\r\n" % (name, value))
1599

  
1600
    buf.write("\r\n")
1601

  
1602
    # Add message body if needed
1603
    if self.HasMessageBody():
1604
      buf.write(self._msg.body)
1605

  
1606
    elif self._msg.body:
1607
      logging.warning("Ignoring message body")
1608

  
1609
    return buf.getvalue()
1610

  
1611
  def HasMessageBody(self):
1612
    """Checks whether the HTTP message contains a body.
1613

  
1614
    Can be overriden by subclasses.
1615

  
1616
    """
1617
    return bool(self._msg.body)
1618

  
1619

  
1620
class HttpMessageReader(object):
1621
  """Reads HTTP message from socket.
1622

  
1623
  """
1624
  # Length limits
1625
  START_LINE_LENGTH_MAX = None
1626
  HEADER_LENGTH_MAX = None
1627

  
1628
  # Parser state machine
1629
  PS_START_LINE = "start-line"
1630
  PS_HEADERS = "headers"
1631
  PS_BODY = "entity-body"
1632
  PS_COMPLETE = "complete"
1633

  
1634
  def __init__(self, sock, msg, read_timeout):
1635
    self.sock = sock
1636
    self.msg = msg
1637

  
1638
    self.poller = select.poll()
1639
    self.start_line_buffer = None
1640
    self.header_buffer = StringIO()
1641
    self.body_buffer = StringIO()
1642
    self.parser_status = self.PS_START_LINE
1643
    self.content_length = None
1644
    self.peer_will_close = None
1645

  
1646
    buf = ""
1647
    eof = False
1648
    while self.parser_status != self.PS_COMPLETE:
1649
      data = SocketOperation(self.poller, sock, SOCKOP_RECV, 4096,
1650
                             read_timeout)
1651

  
1652
      if data:
1653
        buf += data
1654
      else:
1655
        eof = True
1656

  
1657
      # Do some parsing and error checking while more data arrives
1658
      buf = self._ContinueParsing(buf, eof)
1659

  
1660
      # Must be done only after the buffer has been evaluated
1661
      # TODO: Connection-length < len(data read) and connection closed
1662
      if (eof and
1663
          self.parser_status in (self.PS_START_LINE,
1664
                                 self.PS_HEADERS)):
1665
        raise HttpError("Connection closed prematurely")
1666

  
1667
    # Parse rest
1668
    buf = self._ContinueParsing(buf, True)
1669

  
1670
    assert self.parser_status == self.PS_COMPLETE
1671
    assert not buf, "Parser didn't read full response"
1672

  
1673
    msg.body = self.body_buffer.getvalue()
1674

  
1675
    # TODO: Content-type, error handling
1676
    if msg.body:
1677
      msg.decoded_body = HttpJsonConverter().Decode(msg.body)
1678
    else:
1679
      msg.decoded_body = None
1680

  
1681
    if msg.decoded_body:
1682
      logging.debug("Message body: %s", msg.decoded_body)
1683

  
1684
  def _ContinueParsing(self, buf, eof):
1685
    """Main function for HTTP message state machine.
1686

  
1687
    @type buf: string
1688
    @param buf: Receive buffer
1689
    @type eof: bool
1690
    @param eof: Whether we've reached EOF on the socket
1691
    @rtype: string
1692
    @return: Updated receive buffer
1693

  
1694
    """
1695
    if self.parser_status == self.PS_START_LINE:
1696
      # Expect start line
1697
      while True:
1698
        idx = buf.find("\r\n")
1699

  
1700
        # RFC2616, section 4.1: "In the interest of robustness, servers SHOULD
1701
        # ignore any empty line(s) received where a Request-Line is expected.
1702
        # In other words, if the server is reading the protocol stream at the
1703
        # beginning of a message and receives a CRLF first, it should ignore
1704
        # the CRLF."
1705
        if idx == 0:
1706
          # TODO: Limit number of CRLFs for safety?
1707
          buf = buf[:2]
1708
          continue
1709

  
1710
        if idx > 0:
1711
          self.start_line_buffer = buf[:idx]
1712

  
1713
          self._CheckStartLineLength(len(self.start_line_buffer))
1714

  
1715
          # Remove status line, including CRLF
1716
          buf = buf[idx + 2:]
1717

  
1718
          self.msg.start_line = self.ParseStartLine(self.start_line_buffer)
1719

  
1720
          self.parser_status = self.PS_HEADERS
1721
        else:
1722
          # Check whether incoming data is getting too large, otherwise we just
1723
          # fill our read buffer.
1724
          self._CheckStartLineLength(len(buf))
1725

  
1726
        break
1727

  
1728
    # TODO: Handle messages without headers
1729
    if self.parser_status == self.PS_HEADERS:
1730
      # Wait for header end
1731
      idx = buf.find("\r\n\r\n")
1732
      if idx >= 0:
1733
        self.header_buffer.write(buf[:idx + 2])
1734

  
1735
        self._CheckHeaderLength(self.header_buffer.tell())
1736

  
1737
        # Remove headers, including CRLF
1738
        buf = buf[idx + 4:]
1739

  
1740
        self._ParseHeaders()
1741

  
1742
        self.parser_status = self.PS_BODY
1743
      else:
1744
        # Check whether incoming data is getting too large, otherwise we just
1745
        # fill our read buffer.
1746
        self._CheckHeaderLength(len(buf))
1747

  
1748
    if self.parser_status == self.PS_BODY:
1749
      # TODO: Implement max size for body_buffer
1750
      self.body_buffer.write(buf)
1751
      buf = ""
1752

  
1753
      # Check whether we've read everything
1754
      #
1755
      # RFC2616, section 4.4: "When a message-body is included with a message,
1756
      # the transfer-length of that body is determined by one of the following
1757
      # [...] 5. By the server closing the connection. (Closing the connection
1758
      # cannot be used to indicate the end of a request body, since that would
1759
      # leave no possibility for the server to send back a response.)"
1760
      if (eof or
1761
          self.content_length is None or
1762
          (self.content_length is not None and
1763
           self.body_buffer.tell() >= self.content_length)):
1764
        self.parser_status = self.PS_COMPLETE
1765

  
1766
    return buf
1767

  
1768
  def _CheckStartLineLength(self, length):
1769
    """Limits the start line buffer size.
1770

  
1771
    @type length: int
1772
    @param length: Buffer size
1773

  
1774
    """
1775
    if (self.START_LINE_LENGTH_MAX is not None and
1776
        length > self.START_LINE_LENGTH_MAX):
1777
      raise HttpError("Start line longer than %d chars" %
1778
                       self.START_LINE_LENGTH_MAX)
1779

  
1780
  def _CheckHeaderLength(self, length):
1781
    """Limits the header buffer size.
1782

  
1783
    @type length: int
1784
    @param length: Buffer size
1785

  
1786
    """
1787
    if (self.HEADER_LENGTH_MAX is not None and
1788
        length > self.HEADER_LENGTH_MAX):
1789
      raise HttpError("Headers longer than %d chars" % self.HEADER_LENGTH_MAX)
1790

  
1791
  def ParseStartLine(self, start_line):
1792
    """Parses the start line of a message.
1793

  
1794
    Must be overriden by subclass.
1795

  
1796
    @type start_line: string
1797
    @param start_line: Start line string
1798

  
1799
    """
1800
    raise NotImplementedError()
1801

  
1802
  def _WillPeerCloseConnection(self):
1803
    """Evaluate whether peer will close the connection.
1804

  
1805
    @rtype: bool
1806
    @return: Whether peer will close the connection
1807

  
1808
    """
1809
    # RFC2616, section 14.10: "HTTP/1.1 defines the "close" connection option
1810
    # for the sender to signal that the connection will be closed after
1811
    # completion of the response. For example,
1812
    #
1813
    #        Connection: close
1814
    #
1815
    # in either the request or the response header fields indicates that the
1816
    # connection SHOULD NOT be considered `persistent' (section 8.1) after the
1817
    # current request/response is complete."
1818

  
1819
    hdr_connection = self.msg.headers.get(HTTP_CONNECTION, None)
1820
    if hdr_connection:
1821
      hdr_connection = hdr_connection.lower()
1822

  
1823
    # An HTTP/1.1 server is assumed to stay open unless explicitly closed.
1824
    if self.msg.start_line.version == HTTP_1_1:
1825
      return (hdr_connection and "close" in hdr_connection)
1826

  
1827
    # Some HTTP/1.0 implementations have support for persistent connections,
1828
    # using rules different than HTTP/1.1.
1829

  
1830
    # For older HTTP, Keep-Alive indicates persistent connection.
1831
    if self.msg.headers.get(HTTP_KEEP_ALIVE):
1832
      return False
1833

  
1834
    # At least Akamai returns a "Connection: Keep-Alive" header, which was
1835
    # supposed to be sent by the client.
1836
    if hdr_connection and "keep-alive" in hdr_connection:
1837
      return False
1838

  
1839
    return True
1840

  
1841
  def _ParseHeaders(self):
1842
    """Parses the headers.
1843

  
1844
    This function also adjusts internal variables based on header values.
1845

  
1846
    RFC2616, section 4.3: "The presence of a message-body in a request is
1847
    signaled by the inclusion of a Content-Length or Transfer-Encoding header
1848
    field in the request's message-headers."
1849

  
1850
    """
1851
    # Parse headers
1852
    self.header_buffer.seek(0, 0)
1853
    self.msg.headers = mimetools.Message(self.header_buffer, 0)
1854

  
1855
    self.peer_will_close = self._WillPeerCloseConnection()
1856

  
1857
    # Do we have a Content-Length header?
1858
    hdr_content_length = self.msg.headers.get(HTTP_CONTENT_LENGTH, None)
1859
    if hdr_content_length:
1860
      try:
1861
        self.content_length = int(hdr_content_length)
1862
      except ValueError:
1863
        self.content_length = None
1864
      if self.content_length is not None and self.content_length < 0:
1865
        self.content_length = None
1866

  
1867
    # if the connection remains open and a content-length was not provided,
1868
    # then assume that the connection WILL close.
1869
    if self.content_length is None:
1870
      self.peer_will_close = True

Also available in: Unified diff