-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttpchunked.py
77 lines (62 loc) · 1.87 KB
/
httpchunked.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""Implementation of chunked transfer encoding as defined in RFC 7230."""
from io import DEFAULT_BUFFER_SIZE
from re import compile
from typing import BinaryIO
def encode(dst: BinaryIO, src: BinaryIO) -> None:
"""
Encodes from the given source and writes chunks to the given destination.
"""
while True:
chunk_data = src.read(DEFAULT_BUFFER_SIZE)
chunk_size = len(chunk_data)
args = chunk_size, chunk_data
chunk = b"%X\r\n%s\r\n" % args
dst.write(chunk)
if chunk_size == 0:
break
def decode(dst: BinaryIO, src: BinaryIO) -> None:
"""
Decodes from the given source and writes chunk contents to the given
destination.
"""
while True:
chunk = src.readline()
match = _CHUNK_PATTERN.fullmatch(chunk)
if match is None:
raise ValueError
chunk_size_str = match.group("chunk_size")
chunk_size = int(chunk_size_str, 16)
if chunk_size == 0:
break
while chunk_size > 0:
buf = src.read(chunk_size)
dst.write(buf)
chunk_size -= len(buf)
crlf = src.readline()
if _CRLF_PATTERN.fullmatch(crlf) is None:
raise ValueError
for line in src:
if _CRLF_PATTERN.fullmatch(line) is not None:
return
if _TRAILER_PART_PATTERN.fullmatch(line) is None:
raise ValueError
_CHUNK_PATTERN = compile(
br"(?P<chunk_size>[\dA-F]+)"
br"(?:"
br";[-!#$%&'*+.^`|~\w]+"
br"(?"
br":=[-!#$%&'*+.^`|~\w]+|\""
br"(?:[\t !#-\[\]-~\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])"
br"\""
br")?"
br")*"
br"\r\n"
)
_CRLF_PATTERN = compile(b"\r\n")
_TRAILER_PART_PATTERN = compile(
br"[-!#$%&'*+.^`|~\w]+:[ \t]*"
br"(?:"
br"(?:[\x21-\x7E\x80-\xFF](?:[ \t]+[\x21-\x7E\x80-\xFF])?)|(?:\r\n[ \t]+)"
br")*"
br"[ \t]*\r\n"
)