Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit 799e37e

Browse files
committed
Draft of a requests transport adapter.
1 parent 877a205 commit 799e37e

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

hyper/contrib.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
hyper/contrib
4+
~~~~~~~~~~~~~
5+
6+
Contains a few utilities for use with other HTTP libraries.
7+
"""
8+
try:
9+
from requests.adapters import HTTPAdapter
10+
from requests.models import Response
11+
from requests.structures import CaseInsensitiveDict
12+
from requests.utils import get_encoding_from_headers
13+
from requests.cookies import extract_cookies_to_jar
14+
except ImportError:
15+
HTTPAdapter = object
16+
17+
from hyper import HTTP20Connection
18+
from urllib.parse import urlparse
19+
20+
class HTTP20Adapter(HTTPAdapter):
21+
"""
22+
A Requests Transport Adapter that uses hyper to send requests over
23+
HTTP/2.0. This implements some degree of connection pooling to maximise the
24+
HTTP/2.0 gain.
25+
"""
26+
def __init__(self, *args, **kwargs):
27+
#: A mapping between HTTP netlocs and `HTTP20Connection` objects.
28+
self.connections = {}
29+
30+
def get_connection(self, netloc):
31+
"""
32+
Gets an appropriate HTTP/2.0 connection object based on netloc.
33+
"""
34+
try:
35+
conn = self.connections[netloc]
36+
except KeyError:
37+
conn = HTTP20Connection(netloc)
38+
39+
return conn
40+
41+
def send(self, request, stream=False, **kwargs):
42+
"""
43+
Sends a HTTP message to the server.
44+
"""
45+
parsed = urlparse(request.url)
46+
47+
conn = self.get_connection(parsed.netloc)
48+
49+
# Build the selector.
50+
selector = parsed.path
51+
selector += '?' + parsed.query if parsed.query else ''
52+
selector += '#' + parsed.fragment if parsed.fragment else ''
53+
54+
stream_id = conn.request(
55+
request.method,
56+
selector,
57+
request.body,
58+
request.headers
59+
)
60+
resp = conn.getresponse(stream_id)
61+
62+
r = self.build_response(request, resp)
63+
64+
if not stream:
65+
r.content
66+
67+
return r
68+
69+
def build_response(self, request, resp):
70+
"""
71+
Builds a Requests' response object. This emulates most of the logic of
72+
the standard fuction but deals with the lack of the ``.headers``
73+
property on the HTTP20Response object.
74+
"""
75+
response = Response()
76+
77+
response.status_code = resp.status
78+
response.headers = CaseInsensitiveDict(resp.getheaders())
79+
response.raw = resp
80+
response.reason = resp.reason
81+
response.encoding = get_encoding_from_headers(response.headers)
82+
83+
extract_cookies_to_jar(response.cookies, request, response)
84+
85+
if isinstance(request.url, bytes):
86+
response.url = request.url.decode('utf-8')
87+
else:
88+
response.url = request.url
89+
90+
response.request = request
91+
response.connection = self
92+
93+
# One last horrible patch: Requests expects its raw responses to have a
94+
# release_conn method, which I don't. We should monkeypatch a no-op on.
95+
resp.release_conn = lambda: None
96+
97+
return response

0 commit comments

Comments
 (0)