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

Commit c629998

Browse files
committed
Merge branch 'development' into plaintext_http20_via_http11_upgrade
Conflicts: hyper/common/connection.py
2 parents 016d73c + 52af7fc commit c629998

File tree

6 files changed

+122
-4
lines changed

6 files changed

+122
-4
lines changed

HISTORY.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
Release History
22
===============
33

4+
dev
5+
---
6+
7+
*New Features*
8+
9+
- ``HTTP11Connection`` and ``HTTPConnection`` objects are now both context
10+
managers.
11+
412
0.3.1 (2015-04-03)
513
------------------
614

examples/ip.hy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(import hyper json)
2+
3+
(defn http2bin [path]
4+
(with [[conn (hyper.HTTPConnection "http2bin.org")]]
5+
(.request conn "GET" path)
6+
(-> (.get_response conn) (.read) (json.loads))))
7+
8+
(-> (http2bin "/ip") (get "origin") (print))

examples/twscrape.hy

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; Reports the users mentioned in the initially loaded tweets on a user's
2+
; Twitter page.
3+
4+
(import hyper bs4)
5+
6+
(setv results {})
7+
8+
; Get hold of a stream ID for the data for a single page.
9+
(defn request-page [conn path]
10+
(.request conn "GET" path))
11+
12+
; Read the stream to exhaustion.
13+
(defn get-page-data [conn req-id]
14+
(-> (.get_response conn req-id) (.read)))
15+
16+
; Yield all at-reply elements from the html.
17+
(defn at-replies [html]
18+
(let [[soup (bs4.BeautifulSoup html)]]
19+
(apply .find_all [soup] {"class_" "twitter-atreply"})))
20+
21+
(defn get-refs [replies]
22+
(list-comp (.get reply "href") [reply replies]))
23+
24+
(defn mentions [html]
25+
(for [ref (remove none? (get-refs (at-replies html)))]
26+
(-> (.lstrip ref "/") (yield))))
27+
28+
; Simple test: print the people referenced on the most recent tweets page.
29+
(defn main []
30+
(with [[conn (hyper.HTTPConnection "twitter.com" 443)]]
31+
(let [[req-id (request-page conn "/Lukasaoz")]]
32+
(for [mention (mentions (get-page-data conn req-id))]
33+
(print mention)))))
34+
35+
(main)

hyper/common/connection.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ def get_response(self):
132132

133133
return self._conn.get_response(1)
134134

135+
# The following two methods are the implementation of the context manager
136+
# protocol.
137+
def __enter__(self): # pragma: no cover
138+
return self
139+
140+
def __exit__(self, type, value, tb): # pragma: no cover
141+
self._conn.close()
142+
return False # Never swallow exceptions.
143+
135144
# Can anyone say 'proxy object pattern'?
136145
def __getattr__(self, name):
137146
return getattr(self._conn, name)

hyper/http11/connection.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,23 @@ def _send_body(self, body, body_type):
301301
def close(self):
302302
"""
303303
Closes the connection. This closes the socket and then abandons the
304-
reference to it. After calling this method, it any outstanding
304+
reference to it. After calling this method, any outstanding
305305
:class:`Response <hyper.http11.response.Response>` objects will throw
306306
exceptions if attempts are made to read their bodies.
307307
308-
This method should absolutely only be called when you are certain the
309-
connection object is no longer needed.
310-
311308
In some cases this method will automatically be called.
309+
310+
.. warning:: This method should absolutely only be called when you are
311+
certain the connection object is no longer needed.
312312
"""
313313
self._sock.close()
314314
self._sock = None
315+
316+
# The following two methods are the implementation of the context manager
317+
# protocol.
318+
def __enter__(self):
319+
return self
320+
321+
def __exit__(self, type, value, tb):
322+
self.close()
323+
return False # Never swallow exceptions.

test/test_integration_http11.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,52 @@ def socket_handler(listener):
165165
assert r.headers[b'content-length'] == [b'15']
166166

167167
assert r.read() == b'hellotherehello'
168+
169+
def test_connection_context_manager(self):
170+
self.set_up()
171+
172+
send_event = threading.Event()
173+
174+
def socket_handler(listener):
175+
sock = listener.accept()[0]
176+
177+
# We should get the initial request.
178+
data = b''
179+
while not data.endswith(b'\r\n\r\n'):
180+
data += sock.recv(65535)
181+
182+
send_event.wait()
183+
184+
# We need to send back a response.
185+
resp = (
186+
b'HTTP/1.1 200 OK\r\n'
187+
b'Server: socket-level-server\r\n'
188+
b'Content-Length: 15\r\n'
189+
b'\r\n'
190+
)
191+
sock.send(resp)
192+
193+
chunks = [
194+
b'hello',
195+
b'there',
196+
b'hello',
197+
]
198+
199+
for chunk in chunks:
200+
sock.send(chunk)
201+
202+
sock.close()
203+
204+
self._start_server(socket_handler)
205+
with self.get_connection() as c:
206+
c.request('GET', '/')
207+
send_event.set()
208+
r = c.get_response()
209+
data = r.read()
210+
211+
assert r.status == 200
212+
assert r.reason == b'OK'
213+
assert len(r.headers) == 2
214+
assert data == b'hellotherehello'
215+
216+
assert c._sock is None

0 commit comments

Comments
 (0)