r47310 - beep boop
hawkowl-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org Sat, 23 Apr 2016 22:13:50 -0600 (MDT)
| Newsgroups | gmane.comp.python.twisted.commits |
|---|---|
| Message-ID | <[email protected]> |
Author: hawkowl
Date: Sat Apr 23 22:13:44 2016
New Revision: 47310
Modified:
branches/proper-upgrade-8301-2/twisted/web/http.py
branches/proper-upgrade-8301-2/twisted/web/test/test_http.py
Log:
beep boop
Modified: branches/proper-upgrade-8301-2/twisted/web/http.py
==============================================================================
--- branches/proper-upgrade-8301-2/twisted/web/http.py (original)
+++ branches/proper-upgrade-8301-2/twisted/web/http.py Sat Apr 23 22:13:44 2016
@@ -134,6 +134,7 @@
_intTypes = (int, long)
_version = networkString("TwistedWeb/%s" % (copyright.version,))
+_MAX_HEADERS_SIZE = 16384 # 16K, on par with IIS
protocol_version = "HTTP/1.1"
@@ -1612,7 +1613,7 @@
"""
maxHeaders = 500
- totalHeadersSize = 16384
+ totalHeadersSize = _MAX_HEADERS_SIZE
length = 0
persistent = 1
@@ -1999,7 +2000,6 @@
transport.write(b"Connection: Upgrade\r\n")
for k, v in headers.items():
-
transport.write(k + b": " + v + b"\r\n")
transport.write(b"\r\n")
@@ -2035,9 +2035,12 @@
_buffering = False
_replay = False
+ _maxHeadersLength = _MAX_HEADERS_SIZE
+
def __init__(self, *args, **kwargs):
- self._buffer = []
+ self._buffer = b""
+ self._bufferLen = 0
super(_GenericHTTPChannelProtocol, self).__init__(*args, **kwargs)
@property
@@ -2082,51 +2085,78 @@
"""
Look at the headers and determine if they want us to upgrade.
"""
- content = b"".join(self._buffer).split(b"\r\n\r\n", 1)[0].split(b"\r\n")
+ content = self._buffer.split(b"\r\n\r\n", 1)[0].split(b"\r\n")
headers = {}
+ requestLine = content[0].split(b" ")
+
+ if not len(requestLine) == 3:
+ _respondToBadRequestAndDisconnect(self._channel.transport)
+ return None
+
+ if requestLine[2].lower() == b"http/1.0":
+ # It's HTTP/1.0, return that
+ print("http1.0")
+ return b"http/1.0"
+
+ if not requestLine[2].lower() == b"http/1.1":
+ # If it's not HTTP/1.0 or HTTP/1.1, we don't really know what to
+ # do!
+ print("not 1.1")
+ _respondToBadRequestAndDisconnect(self._channel.transport)
+ return None
+
for line in content[1:]:
key, val = line.split(b":", 1)
headers[key.lower()] = val.lstrip()
+ path = requestLine[1]
+
if b"connection" not in headers or b"upgrade" not in headers:
- print("not negotiating")
+ print("no upgrade")
return b"http/1.1"
- else:
- if not b"upgrade" in headers[b"connection"].lower().split(b", "):
- # connection is there and upgrade is there but its not saying to upgrade
- return b"http/1.1"
-
- for upgrade in headers[b"upgrade"].split(b", "):
-
- upgrader = self.factory.upgradeables.get(upgrade.lower())
-
- if upgrader:
- try:
- res = upgrader(self, headers)
- transport = self._channel.transport
- self._channel, self._replay, headersToSend = res
- _respondToUpgrade(transport, upgrade, headersToSend)
- self._channel.makeConnection(transport)
- return upgrade
- except CannotUpgrade:
- pass
+ if not b"upgrade" in headers[b"connection"].lower().split(b", "):
+ # connection is there and upgrade is there but its not saying to upgrade
+ print("no upgrade")
+ return b"http/1.1"
- return b"http/1.1"
+ for upgrade in headers[b"upgrade"].split(b", "):
+ # See if we can upgrade to this. If we can't, try the next,
+ upgrader = self.factory.upgradeables.get(upgrade.lower())
- return None
+ if upgrader:
+ try:
+ res = upgrader(self, path, headers)
+ transport = self._channel.transport
+ self._channel, self._replay, headersToSend = res
+ _respondToUpgrade(transport, upgrade, headersToSend)
+ self._channel.makeConnection(transport)
+ return upgrade
+ except CannotUpgrade:
+ # This one failed, try the next one
+ pass
+
+ print("no negotiate")
+ # Negotiation failed!
+ return b"http/1.1"
def _bufferData(self, data):
+ """
+ Add data to the buffer.
+ """
+ self._buffer += (data)
+ self._bufferLen += len(data)
+
+ if self._bufferLen > self._maxHeadersLength:
+ _respondToBadRequestAndDisconnect(self._channel)
- self._buffer.append(data)
- if b"\r\n\r\n" in data:
+ if b"\r\n\r\n" in self._buffer:
self._negotiatedProtocol = self._upgrade()
if self._negotiatedProtocol == b"http/1.1":
- for x in self._buffer:
- self._channel.dataReceived(x)
+ self._channel.dataReceived(self._buffer)
self._buffering = False
self._buffer = []
Modified: branches/proper-upgrade-8301-2/twisted/web/test/test_http.py
==============================================================================
--- branches/proper-upgrade-8301-2/twisted/web/test/test_http.py (original)
+++ branches/proper-upgrade-8301-2/twisted/web/test/test_http.py Sat Apr 23 22:13:44 2016
@@ -21,8 +21,9 @@
from twisted.trial import unittest
from twisted.trial.unittest import TestCase
from twisted.web import http, http_headers
-from twisted.web.http import PotentialDataLoss, _DataLoss, _version
-from twisted.web.http import _IdentityTransferDecoder
+from twisted.web.http import (
+ _respondToUpgrade, HTTPFactory, PotentialDataLoss, _DataLoss, _version,
+ _IdentityTransferDecoder)
from twisted.internet.protocol import Protocol, Factory
from twisted.internet.task import Clock
from twisted.internet.error import ConnectionLost
@@ -272,7 +273,8 @@
def test_protocolUnspecified(self):
"""
If the transport has no support for protocol negotiation (no
- negotiatedProtocol attribute), HTTP/1.1 is assumed.
+ negotiatedProtocol attribute), the protocol of the first request is
+ used.
"""
b = StringTransport()
negotiatedProtocol = self._negotiatedProtocolForTransportInstance(b)
@@ -281,8 +283,8 @@
def test_protocolNone(self):
"""
- If the transport has no support for protocol negotiation (returns None
- for negotiatedProtocol), HTTP/1.1 is assumed.
+ If the transport has no support for protocol negotiation, the request
+ is inspected and the protocol the request requests is returned.
"""
b = StringTransport()
b.negotiatedProtocol = None
@@ -2541,9 +2543,24 @@
"""
Tests for HTTP/1.1 protocol upgrade.
"""
+ def _makeFactory(self):
+ """
+ Make a testing suitable L{HTTPFactory} which doesn't rely on a running
+ reactor.
+ """
+ factory = HTTPFactory()
+ factory._logDateTime = "sometime"
+ factory._logDateTimeCall = True
+ factory.startFactory()
+ return factory
- def test_basic(self):
+ def test_upgrade(self):
+ """
+ A HTTP/1.1 request with a "Connection" header that contains "Upgrade"
+ and an "Upgrade" header that lists a protocol we support will be
+ upgraded to that protocol.
+ """
piTimes = 10
class Pitocol(Protocol):
@@ -2565,18 +2582,11 @@
piFactory = Factory()
piFactory.protocol = Pitocol
- def piNegotiate(channel, headers):
+ def piNegotiate(channel, path, headers):
pi = piFactory.buildProtocol(None)
- return pi, False, {}
-
-
- from twisted.web.http import _respondToUpgrade, HTTPFactory
-
- factory = HTTPFactory()
- factory._logDateTime = "sometime"
- factory._logDateTimeCall = True
- factory.startFactory()
+ return pi, False, {b"beep": b"boop"}
+ factory = self._makeFactory()
factory.upgradeables[b"pitocol"] = piNegotiate
protocol = factory.buildProtocol(None)
@@ -2587,10 +2597,11 @@
val = [
b"GET / HTTP/1.1\r\n"
b"Connection: keep-alive, Upgrade\r\n",
- b"Upgrade: pitocol\r\n\r\n",
+ b"Upgrade: pitocol\r\n",
+ b"beep: boop\r\n\r\n",
]
- for x in val:
+ for x in iterbytes(b"".join(val)):
protocol.dataReceived(x)
expectedValue = b"".join([
@@ -2604,3 +2615,46 @@
self.assertEqual(trans.value(), b"3.14" * 10)
self.assertTrue(trans.disconnecting)
+
+
+ def test_notHTTP(self):
+ """
+ A non-HTTP request should return with a "bad request" error.
+ """
+ factory = self._makeFactory()
+ protocol = factory.buildProtocol(None)
+
+ trans = StringTransport()
+ protocol.makeConnection(trans)
+
+ val = [
+ b"BEEP BOOP IRC/1234\r\n\r\n",
+ ]
+
+ for x in iterbytes(b"".join(val)):
+ protocol.dataReceived(x)
+
+ expectedValue = b"HTTP/1.1 400 Bad Request\r\n\r\n"
+ self.assertEqual(trans.value(), expectedValue)
+
+
+ def test_mangledStatusLine(self):
+ """
+ A non-HTTP first-line (or a mangled one) will return with a
+ "bad request" error.
+ """
+ factory = self._makeFactory()
+ protocol = factory.buildProtocol(None)
+
+ trans = StringTransport()
+ protocol.makeConnection(trans)
+
+ val = [
+ b"GET/ HTTP/1.1\r\n\r\n",
+ ]
+
+ for x in iterbytes(b"".join(val)):
+ protocol.dataReceived(x)
+
+ expectedValue = b"HTTP/1.1 400 Bad Request\r\n\r\n"
+ self.assertEqual(trans.value(), expectedValue)