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
78
|
"""Crummy HTTP client.
This is not meant as an example of how to write a good client.
"""
# Stdlib.
import re
import time
# Local.
import sockets
def urlfetch(host, port=None, path='/', method='GET',
body=None, hdrs=None, encoding='utf-8', ssl=None, af=0):
"""COROUTINE: Make an HTTP 1.0 request."""
t0 = time.time()
if port is None:
if ssl:
port = 443
else:
port = 80
trans = yield from sockets.create_transport(host, port, ssl=ssl, af=af)
yield from trans.send(method.encode(encoding) + b' ' +
path.encode(encoding) + b' HTTP/1.0\r\n')
if hdrs:
kwds = dict(hdrs)
else:
kwds = {}
if 'host' not in kwds:
kwds['host'] = host
if body is not None:
kwds['content_length'] = len(body)
for header, value in kwds.items():
yield from trans.send(header.replace('_', '-').encode(encoding) +
b': ' + value.encode(encoding) + b'\r\n')
yield from trans.send(b'\r\n')
if body is not None:
yield from trans.send(body)
# Read HTTP response line.
rdr = sockets.BufferedReader(trans)
resp = yield from rdr.readline()
m = re.match(br'(?ix) http/(\d\.\d) \s+ (\d\d\d) \s+ ([^\r]*)\r?\n\Z',
resp)
if not m:
trans.close()
raise IOError('No valid HTTP response: %r' % resp)
http_version, status, message = m.groups()
# Read HTTP headers.
headers = []
hdict = {}
while True:
line = yield from rdr.readline()
if not line.strip():
break
m = re.match(br'([^\s:]+):\s*([^\r]*)\r?\n\Z', line)
if not m:
raise IOError('Invalid header: %r' % line)
header, value = m.groups()
headers.append((header, value))
hdict[header.decode(encoding).lower()] = value.decode(encoding)
# Read response body.
content_length = hdict.get('content-length')
if content_length is not None:
size = int(content_length) # TODO: Catch errors.
assert size >= 0, size
else:
size = 2**20 # Protective limit (1 MB).
data = yield from rdr.readexactly(size)
trans.close() # Can this block?
t1 = time.time()
result = (host, port, path, int(status), len(data), round(t1-t0, 3))
## print(result)
return result
|