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