9
9
ServerDisconnectedError ,
10
10
SocketTimeoutError ,
11
11
)
12
- from .helpers import BaseTimerContext , status_code_must_be_empty_body
12
+ from .helpers import (
13
+ _EXC_SENTINEL ,
14
+ BaseTimerContext ,
15
+ set_exception ,
16
+ status_code_must_be_empty_body ,
17
+ )
13
18
from .http import HttpResponseParser , RawResponseMessage
19
+ from .http_exceptions import HttpProcessingError
14
20
from .streams import EMPTY_PAYLOAD , DataQueue , StreamReader
15
21
16
22
@@ -73,36 +79,58 @@ def is_connected(self) -> bool:
73
79
def connection_lost (self , exc : Optional [BaseException ]) -> None :
74
80
self ._drop_timeout ()
75
81
82
+ original_connection_error = exc
83
+ reraised_exc = original_connection_error
84
+
85
+ connection_closed_cleanly = original_connection_error is None
86
+
76
87
if self ._payload_parser is not None :
77
- with suppress (Exception ):
88
+ with suppress (Exception ): # FIXME: log this somehow?
78
89
self ._payload_parser .feed_eof ()
79
90
80
91
uncompleted = None
81
92
if self ._parser is not None :
82
93
try :
83
94
uncompleted = self ._parser .feed_eof ()
84
- except Exception as e :
95
+ except Exception as underlying_exc :
85
96
if self ._payload is not None :
86
- exc = ClientPayloadError ("Response payload is not completed" )
87
- exc .__cause__ = e
88
- self ._payload .set_exception (exc )
97
+ client_payload_exc_msg = (
98
+ f"Response payload is not completed: { underlying_exc !r} "
99
+ )
100
+ if not connection_closed_cleanly :
101
+ client_payload_exc_msg = (
102
+ f"{ client_payload_exc_msg !s} . "
103
+ f"{ original_connection_error !r} "
104
+ )
105
+ set_exception (
106
+ self ._payload ,
107
+ ClientPayloadError (client_payload_exc_msg ),
108
+ underlying_exc ,
109
+ )
89
110
90
111
if not self .is_eof ():
91
- if isinstance (exc , OSError ):
92
- exc = ClientOSError (* exc .args )
93
- if exc is None :
94
- exc = ServerDisconnectedError (uncompleted )
112
+ if isinstance (original_connection_error , OSError ):
113
+ reraised_exc = ClientOSError (* original_connection_error .args )
114
+ if connection_closed_cleanly :
115
+ reraised_exc = ServerDisconnectedError (uncompleted )
95
116
# assigns self._should_close to True as side effect,
96
117
# we do it anyway below
97
- self .set_exception (exc )
118
+ underlying_non_eof_exc = (
119
+ _EXC_SENTINEL
120
+ if connection_closed_cleanly
121
+ else original_connection_error
122
+ )
123
+ assert underlying_non_eof_exc is not None
124
+ assert reraised_exc is not None
125
+ self .set_exception (reraised_exc , underlying_non_eof_exc )
98
126
99
127
self ._should_close = True
100
128
self ._parser = None
101
129
self ._payload = None
102
130
self ._payload_parser = None
103
131
self ._reading_paused = False
104
132
105
- super ().connection_lost (exc )
133
+ super ().connection_lost (reraised_exc )
106
134
107
135
def eof_received (self ) -> None :
108
136
# should call parser.feed_eof() most likely
@@ -116,10 +144,14 @@ def resume_reading(self) -> None:
116
144
super ().resume_reading ()
117
145
self ._reschedule_timeout ()
118
146
119
- def set_exception (self , exc : BaseException ) -> None :
147
+ def set_exception (
148
+ self ,
149
+ exc : BaseException ,
150
+ exc_cause : BaseException = _EXC_SENTINEL ,
151
+ ) -> None :
120
152
self ._should_close = True
121
153
self ._drop_timeout ()
122
- super ().set_exception (exc )
154
+ super ().set_exception (exc , exc_cause )
123
155
124
156
def set_parser (self , parser : Any , payload : Any ) -> None :
125
157
# TODO: actual types are:
@@ -196,7 +228,7 @@ def _on_read_timeout(self) -> None:
196
228
exc = SocketTimeoutError ("Timeout on reading data from socket" )
197
229
self .set_exception (exc )
198
230
if self ._payload is not None :
199
- self ._payload . set_exception ( exc )
231
+ set_exception ( self ._payload , exc )
200
232
201
233
def data_received (self , data : bytes ) -> None :
202
234
self ._reschedule_timeout ()
@@ -222,14 +254,14 @@ def data_received(self, data: bytes) -> None:
222
254
# parse http messages
223
255
try :
224
256
messages , upgraded , tail = self ._parser .feed_data (data )
225
- except BaseException as exc :
257
+ except BaseException as underlying_exc :
226
258
if self .transport is not None :
227
259
# connection.release() could be called BEFORE
228
260
# data_received(), the transport is already
229
261
# closed in this case
230
262
self .transport .close ()
231
263
# should_close is True after the call
232
- self .set_exception (exc )
264
+ self .set_exception (HttpProcessingError (), underlying_exc )
233
265
return
234
266
235
267
self ._upgraded = upgraded
0 commit comments