Skip to content

Commit defae74

Browse files
authored
python: Support http/1.1 (#24)
* python: Support http/1.1 * http.py: Handle unsupported case
1 parent 55d3d2a commit defae74

File tree

4 files changed

+75
-5
lines changed

4 files changed

+75
-5
lines changed

.github/workflows/python.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ jobs:
1818
uses: actions/setup-python@v2
1919
with:
2020
python-version: 3.9
21+
- name: Install requirements
22+
run: pip install -r requirements.txt
2123
- name: Install Black
22-
run: pip install black
24+
run: pip install black pytest
2325
- name: Run black --check .
2426
run: black --check .
27+
- name: Test with pytest
28+
run: pytest -v

python/http.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import gzip
22
import socket
33
import ssl
4+
import sys
45
import zlib
56

67
try:
@@ -40,11 +41,17 @@ def request(url):
4041

4142
def _get_headers_and_body(sock, host, port, path):
4243
sock.connect((host, port))
44+
accept_encoding = ["gzip", "deflate"]
45+
if brotli:
46+
accept_encoding.append("br")
47+
accept_encoding = ",".join(accept_encoding)
4348

4449
# 5. Send request
45-
sock.send(f"GET {path} HTTP/1.0\r\n".encode())
50+
sock.send(f"GET {path} HTTP/1.1\r\n".encode())
4651
sock.send(f"Host: {host}\r\n".encode())
47-
sock.send("Accept-Encoding: br,gzip,deflate\r\n".encode())
52+
sock.send(f"Connection: close\r\n".encode())
53+
sock.send(f"User-Agent: Mozilla/5.0 ({sys.platform})\r\n".encode())
54+
sock.send(f"Accept-Encoding: {accept_encoding}\r\n".encode())
4855
sock.send("\r\n".encode())
4956

5057
# 6. Receive response
@@ -63,11 +70,18 @@ def _get_headers_and_body(sock, host, port, path):
6370
line = response.readline().decode()
6471
if line == "\r\n":
6572
break
66-
6773
header, value = line.split(":", 1)
6874
headers[header.lower()] = value.strip()
6975

70-
body = response.read()
76+
if "transfer-encoding" in headers:
77+
encoding = headers["transfer-encoding"]
78+
if encoding == "chunked":
79+
body = unchunked(response)
80+
else:
81+
raise RuntimeError(f"Unsupported transfer-encoding: {encoding}")
82+
else:
83+
body = response.read()
84+
7185
if "content-encoding" in headers:
7286
encoding = headers["content-encoding"]
7387
body = decompress(body, encoding)
@@ -77,6 +91,23 @@ def _get_headers_and_body(sock, host, port, path):
7791
return headers, body
7892

7993

94+
def unchunked(response):
95+
ret = b""
96+
97+
def get_chunk_size():
98+
chunk_size = response.readline().rstrip()
99+
return int(chunk_size, 16)
100+
101+
while True:
102+
chunk_size = get_chunk_size()
103+
if chunk_size == 0:
104+
break
105+
else:
106+
ret += response.read(chunk_size)
107+
response.read(2)
108+
return ret
109+
110+
80111
def decompress(data, encoding):
81112
if encoding == "gzip":
82113
return gzip.decompress(data)

python/tests/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import sys
2+
3+
sys.path.append("../")

python/tests/test_http.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import unittest
2+
3+
from http import request
4+
5+
6+
class RequestTest(unittest.TestCase):
7+
def test_http_request(self):
8+
http_sites = ["http://www.google.com/", "http://example.com/"]
9+
for site in http_sites:
10+
headers, body = request(site)
11+
self.assertGreater(len(body), 0)
12+
self.assertIn("content-type", headers)
13+
14+
def test_https_request(self):
15+
https_sites = [
16+
"https://www.google.com/",
17+
"https://www.facebook.com/",
18+
"https://example.com/",
19+
]
20+
for site in https_sites:
21+
headers, body = request(site)
22+
self.assertGreater(len(body), 0)
23+
self.assertIn("content-type", headers)
24+
25+
def test_data_request(self):
26+
headers, body = request("data:text/html,Hello world")
27+
self.assertEqual(body, "Hello world")
28+
self.assertEqual(headers["content-type"], "text/html")
29+
30+
31+
if __name__ == "__main__":
32+
unittest.main()

0 commit comments

Comments
 (0)