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

Commit 8c3b2a4

Browse files
committed
Add connection management code.
1 parent 5721082 commit 8c3b2a4

File tree

5 files changed

+180
-27
lines changed

5 files changed

+180
-27
lines changed

hyper/http11/connection.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ def get_response(self):
159159
response.status,
160160
response.msg.tobytes(),
161161
headers,
162-
self._sock
162+
self._sock,
163+
self
163164
)
164165

165166
def _send_headers(self, method, url, headers):
@@ -262,3 +263,18 @@ def _send_body(self, body, body_type):
262263

263264
self._sock.send(b'0\r\n\r\n')
264265
return
266+
267+
def close(self):
268+
"""
269+
Closes the connection. This closes the socket and then abandons the
270+
reference to it. After calling this method, it any outstanding
271+
:class:`Response <hyper.http11.response.Response>` objects will throw
272+
exceptions if attempts are made to read their bodies.
273+
274+
This method should absolutely only be called when you are certain the
275+
connection object is no longer needed.
276+
277+
In some cases this method will automatically be called.
278+
"""
279+
self._sock.close()
280+
self._sock = None

hyper/http11/response.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
httplib/http.client.
88
"""
99
import logging
10+
import weakref
1011
import zlib
1112

1213
from ..common.decoder import DeflateDecoder
@@ -22,7 +23,7 @@ class HTTP11Response(object):
2223
provides access to the response headers and the entity body. The response
2324
is an iterable object and can be used in a with statement.
2425
"""
25-
def __init__(self, code, reason, headers, sock):
26+
def __init__(self, code, reason, headers, sock, connection=None):
2627
#: The reason phrase returned by the server.
2728
self.reason = reason
2829

@@ -74,6 +75,17 @@ def __init__(self, code, reason, headers, sock):
7475
else:
7576
self._decompressobj = None
7677

78+
# This is a reference that allows for the Response class to tell the
79+
# parent connection object to throw away its socket object. This is to
80+
# be used when the connection is genuinely closed, so that the user
81+
# can keep using the Connection object.
82+
# Strictly, we take a weakreference to this so that we don't set up a
83+
# reference cycle.
84+
if connection is not None:
85+
self._parent = weakref.ref(connection)
86+
else:
87+
self._parent = None
88+
7789
self._buffered_data = b''
7890
self._chunker = None
7991

@@ -115,8 +127,10 @@ def read(self, amt=None, decode_content=True):
115127
if self._length is not None:
116128
amt = min(amt, self._length)
117129

118-
# If we are now going to read nothing, exit early.
130+
# If we are now going to read nothing, exit early. We still need to
131+
# close the socket.
119132
if not amt:
133+
self.close(socket_close=self._expect_close)
120134
return b''
121135

122136
# Now, issue reads until we read that length. This is to account for
@@ -136,7 +150,7 @@ def read(self, amt=None, decode_content=True):
136150
# but if we were expecting the remote end to close then it's ok.
137151
if not chunk:
138152
if self._length is not None or not self._expect_close:
139-
self.close()
153+
self.close(socket_close=True)
140154
raise ConnectionResetError("Remote end hung up!")
141155

142156
break
@@ -167,7 +181,7 @@ def read(self, amt=None, decode_content=True):
167181
# We're at the end. Close the connection. Explicit check for zero here
168182
# because self._length might be None.
169183
if end_of_request:
170-
self.close()
184+
self.close(socket_close=self._expect_close)
171185

172186
return data
173187

@@ -204,6 +218,7 @@ def read_chunked(self, decode_content=True):
204218
if decode_content and self._decompressobj:
205219
yield self._decompressobj.flush()
206220

221+
self.close(socket_close=self._expect_close)
207222
break
208223

209224
# Then read that many bytes.
@@ -225,14 +240,23 @@ def read_chunked(self, decode_content=True):
225240

226241
return
227242

228-
def close(self):
243+
def close(self, socket_close=False):
229244
"""
230-
Close the response. In effect this closes the backing socket.
245+
Close the response. This causes the Response to lose access to the
246+
backing socket. In some cases, it can also cause the backing connection
247+
to be torn down.
231248
249+
:param socket_close: Whether to close the backing socket.
232250
:returns: Nothing.
233251
"""
234-
# FIXME: This should notify the parent connection object if possible.
235-
self._sock.close()
252+
if socket_close and self._parent is not None:
253+
# The double call is necessary because we need to dereference the
254+
# weakref. If the weakref is no longer valid, that's fine, there's
255+
# no connection object to tell.
256+
parent = self._parent()
257+
if parent is not None:
258+
parent.close()
259+
236260
self._sock = None
237261

238262
def _read_expect_closed(self, decode_content):
@@ -253,7 +277,7 @@ def _read_expect_closed(self, decode_content):
253277
else:
254278
chunks.append(chunk)
255279

256-
self.close()
280+
self.close(socket_close=True)
257281

258282
# We may need to decompress the data.
259283
data = b''.join(chunks)
@@ -283,7 +307,7 @@ def _normal_read_chunked(self, amt, decode_content):
283307
try:
284308
chunk = next(self._chunker)
285309
except StopIteration:
286-
self.close()
310+
self.close(socket_close=self._expect_close)
287311
break
288312

289313
current_amount += len(chunk)

0 commit comments

Comments
 (0)