Skip to content

Commit 44050b4

Browse files
committed
Support HTTP upgrade
1 parent f18e483 commit 44050b4

File tree

4 files changed

+54
-2
lines changed

4 files changed

+54
-2
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class HttpRequestParser:
2323
protocol -- a Python object with the following methods
2424
(all optional):
2525
26+
- on_message_begin()
2627
- on_header(name: bytes, value: bytes)
2728
- on_headers_complete()
2829
- on_body(body: bytes)
@@ -42,6 +43,10 @@ class HttpRequestParser:
4243
4344
Will eventually trigger callbacks on the ``protocol``
4445
object.
46+
47+
On HTTP upgrade, this method will raise an
48+
``HttpParserUpgrade`` exception, with its sole argument
49+
set to the offset of the non-HTTP data in ``data``.
4550
"""
4651

4752
def get_method(self) -> str:

httptools/parser/errors.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
'HttpParserCallbackError',
33
'HttpParserInvalidStatusError',
44
'HttpParserInvalidMethodError',
5-
'HttpParserInvalidURLError')
5+
'HttpParserInvalidURLError',
6+
'HttpParserUpgrade')
67

78

89
class HttpParserError(Exception):
@@ -23,3 +24,7 @@ class HttpParserInvalidMethodError(HttpParserError):
2324

2425
class HttpParserInvalidURLError(HttpParserError):
2526
pass
27+
28+
29+
class HttpParserUpgrade(Exception):
30+
pass

httptools/parser/parser.pyx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ from .errors import (HttpParserError,
99
HttpParserCallbackError,
1010
HttpParserInvalidStatusError,
1111
HttpParserInvalidMethodError,
12-
HttpParserInvalidURLError)
12+
HttpParserInvalidURLError,
13+
HttpParserUpgrade)
1314

1415
cimport cython
1516
from . cimport cparser
@@ -156,6 +157,9 @@ cdef class HttpParser:
156157

157158
PyBuffer_Release(&self.py_buf)
158159

160+
if self._cparser.upgrade:
161+
raise HttpParserUpgrade(nb)
162+
159163
# TODO: Handle parser->upgrade
160164

161165
if self._cparser.http_errno != cparser.HPE_OK:

tests/test_parser.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@
4646
b\r\n+\xce\xcfM\xb5MI,I\x04\x00\r\n0\r\n\r\n'''
4747

4848

49+
UPGRADE_REQUEST1 = b'''GET /demo HTTP/1.1
50+
Host: example.com
51+
Connection: Upgrade
52+
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
53+
Sec-WebSocket-Protocol: sample
54+
Upgrade: WebSocket
55+
Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
56+
Origin: http://example.com
57+
58+
Hot diggity dogg'''
59+
60+
4961
class TestResponseParser(unittest.TestCase):
5062

5163
def test_parser_response_1(self):
@@ -199,6 +211,32 @@ def test_parser_request_chunked_3(self):
199211

200212
self.assertTrue(m.on_message_complete.called)
201213

214+
def test_parser_request_upgrade_1(self):
215+
m = mock.Mock()
216+
217+
headers = {}
218+
m.on_header.side_effect = headers.__setitem__
219+
220+
p = httptools.HttpRequestParser(m)
221+
222+
try:
223+
p.feed_data(UPGRADE_REQUEST1)
224+
except httptools.HttpParserUpgrade as ex:
225+
offset = ex.args[0]
226+
else:
227+
self.fail('HttpParserUpgrade was not raised')
228+
229+
self.assertEqual(UPGRADE_REQUEST1[offset:], b'Hot diggity dogg')
230+
231+
self.assertEqual(headers, {
232+
b'Sec-WebSocket-Key2': b'12998 5 Y3 1 .P00',
233+
b'Sec-WebSocket-Key1': b'4 @1 46546xW%0l 1 5',
234+
b'Connection': b'Upgrade',
235+
b'Origin': b'http://example.com',
236+
b'Sec-WebSocket-Protocol': b'sample',
237+
b'Host': b'example.com',
238+
b'Upgrade': b'WebSocket'})
239+
202240
def test_parser_request_2(self):
203241
p = httptools.HttpRequestParser(None)
204242
with self.assertRaises(httptools.HttpParserInvalidMethodError):

0 commit comments

Comments
 (0)