Skip to content

Commit 7e7fc78

Browse files
cdelerpgjones
authored andcommitted
Fixed some performance issues
1 parent ec0ff35 commit 7e7fc78

File tree

3 files changed

+34
-31
lines changed

3 files changed

+34
-31
lines changed

h11/_readers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def __call__(self, buf):
153153
assert self._bytes_to_discard == 0
154154
if self._bytes_in_chunk == 0:
155155
# We need to refill our chunk count
156-
chunk_header = buf.maybe_extract_until_delimiter(b"\r?\n")
156+
chunk_header = buf.maybe_extract_until_next(b"\r?\n")
157157
if chunk_header is None:
158158
return None
159159
matches = validate(

h11/_receivebuffer.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,8 @@ def __init__(self):
5151
# These are both absolute offsets into self._data:
5252
self._start = 0
5353
self._looked_at = 0
54-
self._looked_for = b""
55-
56-
self._delimiter = b"\n\r?\n"
57-
self._delimiter_regex = delimiter_regex
54+
self._looked_for = default_delimiter
55+
self._looked_for_regex = delimiter_regex
5856

5957
def __bool__(self):
6058
return bool(len(self))
@@ -89,22 +87,22 @@ def maybe_extract_at_most(self, count):
8987
self._start += len(out)
9088
return out
9189

92-
def maybe_extract_until_delimiter(self, delimiter=b"\n\r?\n"):
90+
def maybe_extract_until_next(self, needle):
9391
# Returns extracted bytes on success (advancing offset), or None on
9492
# failure
95-
if delimiter == self._delimiter:
96-
looked_at = max(self._start, self._looked_at - len(delimiter) + 1)
93+
if self._looked_for == needle:
94+
looked_at = max(self._start, self._looked_at - len(needle) + 1)
9795
else:
9896
looked_at = self._start
99-
self._delimiter = delimiter
97+
self._looked_for = needle
10098
# re.compile operation is more expensive than just byte compare
101-
if delimiter == default_delimiter:
102-
self._delimiter_regex = delimiter_regex
99+
if needle == default_delimiter:
100+
self._looked_for_regex = delimiter_regex
103101
else:
104-
self._delimiter_regex = re.compile(delimiter, re.MULTILINE)
102+
self._looked_for_regex = re.compile(needle, re.MULTILINE)
105103

106104
delimiter_match = next(
107-
self._delimiter_regex.finditer(self._data, looked_at), None
105+
self._looked_for_regex.finditer(self._data, looked_at), None
108106
)
109107

110108
if delimiter_match is None:
@@ -119,25 +117,30 @@ def maybe_extract_until_delimiter(self, delimiter=b"\n\r?\n"):
119117

120118
return out
121119

120+
def _get_fields_delimiter(self, data, lines_delimiter_regex):
121+
delimiter_match = next(lines_delimiter_regex.finditer(data), None)
122+
123+
if delimiter_match is not None:
124+
begin, end = delimiter_match.span(0)
125+
result = data[begin:end]
126+
else:
127+
result = b"\r\n"
128+
129+
return bytes(result)
130+
122131
# HTTP/1.1 has a number of constructs where you keep reading lines until
123132
# you see a blank one. This does that, and then returns the lines.
124133
def maybe_extract_lines(self):
125-
if self._data[self._start : self._start + 2] == b"\r\n":
126-
self._start += 2
127-
return []
128-
elif self._start < len(self._data) and self._data[self._start] == b"\n":
129-
self._start += 1
134+
start_chunk = self._data[self._start : self._start + 2]
135+
if start_chunk in [b"\r\n", b"\n"]:
136+
self._start += len(start_chunk)
130137
return []
131138
else:
132-
data = self.maybe_extract_until_delimiter(b"\n\r?\n")
133-
139+
data = self.maybe_extract_until_next(default_delimiter)
134140
if data is None:
135141
return None
136142

137-
lines = line_delimiter_regex.split(data)
138-
139-
assert lines[-2] == lines[-1] == b""
140-
141-
del lines[-2:]
143+
delimiter = self._get_fields_delimiter(data, line_delimiter_regex)
144+
lines = data.rstrip(b"\r\n").split(delimiter)
142145

143146
return lines

h11/tests/test_receivebuffer.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,28 @@ def test_receivebuffer():
3232
assert not b
3333

3434
################################################################
35-
# maybe_extract_until_delimiter
35+
# maybe_extract_until_next
3636
################################################################
3737

3838
b += b"12345a6789aa"
3939

40-
assert b.maybe_extract_until_delimiter(b"a") == b"12345a"
40+
assert b.maybe_extract_until_next(b"a") == b"12345a"
4141
assert bytes(b) == b"6789aa"
4242

43-
assert b.maybe_extract_until_delimiter(b"aaa") is None
43+
assert b.maybe_extract_until_next(b"aaa") is None
4444
assert bytes(b) == b"6789aa"
4545

4646
b += b"a12"
47-
assert b.maybe_extract_until_delimiter(b"aaa") == b"6789aaa"
47+
assert b.maybe_extract_until_next(b"aaa") == b"6789aaa"
4848
assert bytes(b) == b"12"
4949

5050
# check repeated searches for the same needle, triggering the
5151
# pickup-where-we-left-off logic
5252
b += b"345"
53-
assert b.maybe_extract_until_delimiter(b"aaa") is None
53+
assert b.maybe_extract_until_next(b"aaa") is None
5454

5555
b += b"6789aaa123"
56-
assert b.maybe_extract_until_delimiter(b"aaa") == b"123456789aaa"
56+
assert b.maybe_extract_until_next(b"aaa") == b"123456789aaa"
5757
assert bytes(b) == b"123"
5858

5959
################################################################

0 commit comments

Comments
 (0)