1
1
use bytes:: IntoBuf ;
2
2
use futures:: { Async , Future , Poll , Stream } ;
3
3
use futures:: future:: { self , Either } ;
4
- use futures:: sync:: mpsc;
4
+ use futures:: sync:: { mpsc, oneshot } ;
5
5
use h2:: client:: { Builder , Handshake , SendRequest } ;
6
6
use tokio_io:: { AsyncRead , AsyncWrite } ;
7
7
@@ -18,6 +18,10 @@ type ClientRx<B> = ::client::dispatch::Receiver<Request<B>, Response<Body>>;
18
18
/// other handles to it have been dropped, so that it can shutdown.
19
19
type ConnDropRef = mpsc:: Sender < Never > ;
20
20
21
+ /// A oneshot channel watches the `Connection` task, and when it completes,
22
+ /// the "dispatch" task will be notified and can shutdown sooner.
23
+ type ConnEof = oneshot:: Receiver < Never > ;
24
+
21
25
pub ( crate ) struct Client < T , B >
22
26
where
23
27
B : Payload ,
29
33
30
34
enum State < T , B > where B : IntoBuf {
31
35
Handshaking ( Handshake < T , B > ) ,
32
- Ready ( SendRequest < B > , ConnDropRef ) ,
36
+ Ready ( SendRequest < B > , ConnDropRef , ConnEof ) ,
33
37
}
34
38
35
39
impl < T , B > Client < T , B >
@@ -66,14 +70,18 @@ where
66
70
// in h2 where dropping all SendRequests won't notify a
67
71
// parked Connection.
68
72
let ( tx, rx) = mpsc:: channel ( 0 ) ;
73
+ let ( cancel_tx, cancel_rx) = oneshot:: channel ( ) ;
69
74
let rx = rx. into_future ( )
70
75
. map ( |( msg, _) | match msg {
71
76
Some ( never) => match never { } ,
72
77
None => ( ) ,
73
78
} )
74
79
. map_err ( |_| -> Never { unreachable ! ( "mpsc cannot error" ) } ) ;
75
80
let fut = conn
76
- . inspect ( |_| trace ! ( "connection complete" ) )
81
+ . inspect ( move |_| {
82
+ drop ( cancel_tx) ;
83
+ trace ! ( "connection complete" )
84
+ } )
77
85
. map_err ( |e| debug ! ( "connection error: {}" , e) )
78
86
. select2 ( rx)
79
87
. then ( |res| match res {
@@ -92,10 +100,21 @@ where
92
100
Err ( Either :: B ( ( never, _) ) ) => match never { } ,
93
101
} ) ;
94
102
self . executor . execute ( fut) ?;
95
- State :: Ready ( request_tx, tx)
103
+ State :: Ready ( request_tx, tx, cancel_rx )
96
104
} ,
97
- State :: Ready ( ref mut tx, ref conn_dropper) => {
98
- try_ready ! ( tx. poll_ready( ) . map_err( :: Error :: new_h2) ) ;
105
+ State :: Ready ( ref mut tx, ref conn_dropper, ref mut cancel_rx) => {
106
+ match tx. poll_ready ( ) {
107
+ Ok ( Async :: Ready ( ( ) ) ) => ( ) ,
108
+ Ok ( Async :: NotReady ) => return Ok ( Async :: NotReady ) ,
109
+ Err ( err) => {
110
+ return if err. reason ( ) == Some ( :: h2:: Reason :: NO_ERROR ) {
111
+ trace ! ( "connection gracefully shutdown" ) ;
112
+ Ok ( Async :: Ready ( Dispatched :: Shutdown ) )
113
+ } else {
114
+ Err ( :: Error :: new_h2 ( err) )
115
+ } ;
116
+ }
117
+ }
99
118
match self . rx . poll ( ) {
100
119
Ok ( Async :: Ready ( Some ( ( req, cb) ) ) ) => {
101
120
// check that future hasn't been canceled already
@@ -157,7 +176,16 @@ where
157
176
continue ;
158
177
} ,
159
178
160
- Ok ( Async :: NotReady ) => return Ok ( Async :: NotReady ) ,
179
+ Ok ( Async :: NotReady ) => {
180
+ match cancel_rx. poll ( ) {
181
+ Ok ( Async :: Ready ( never) ) => match never { } ,
182
+ Ok ( Async :: NotReady ) => return Ok ( Async :: NotReady ) ,
183
+ Err ( _conn_is_eof) => {
184
+ trace ! ( "connection task is closed, closing dispatch task" ) ;
185
+ return Ok ( Async :: Ready ( Dispatched :: Shutdown ) ) ;
186
+ }
187
+ }
188
+ } ,
161
189
162
190
Ok ( Async :: Ready ( None ) ) => {
163
191
trace ! ( "client::dispatch::Sender dropped" ) ;
0 commit comments