1
1
use bytes:: IntoBuf ;
2
2
use futures:: { Async , Future , Poll , Stream } ;
3
3
use futures:: future:: { self , Either } ;
4
- use futures:: sync:: oneshot ;
4
+ use futures:: sync:: mpsc ;
5
5
use h2:: client:: { Builder , Handshake , SendRequest } ;
6
6
use tokio_io:: { AsyncRead , AsyncWrite } ;
7
7
@@ -11,6 +11,9 @@ use super::{PipeToSendStream, SendBuf};
11
11
use :: { Body , Request , Response } ;
12
12
13
13
type ClientRx < B > = :: client:: dispatch:: Receiver < Request < B > , Response < Body > > ;
14
+ /// An mpsc channel is used to help notify the `Connection` task when *all*
15
+ /// other handles to it have been dropped, so that it can shutdown.
16
+ type ConnDropRef = mpsc:: Sender < Never > ;
14
17
15
18
pub struct Client < T , B >
16
19
where
23
26
24
27
enum State < T , B > where B : IntoBuf {
25
28
Handshaking ( Handshake < T , B > ) ,
26
- Ready ( SendRequest < B > , oneshot :: Sender < Never > ) ,
29
+ Ready ( SendRequest < B > , ConnDropRef ) ,
27
30
}
28
31
29
32
impl < T , B > Client < T , B >
@@ -58,11 +61,17 @@ where
58
61
let next = match self . state {
59
62
State :: Handshaking ( ref mut h) => {
60
63
let ( request_tx, conn) = try_ready ! ( h. poll( ) . map_err( :: Error :: new_h2) ) ;
61
- // A oneshot channel is used entirely to detect when the
64
+ // An mpsc channel is used entirely to detect when the
62
65
// 'Client' has been dropped. This is to get around a bug
63
66
// in h2 where dropping all SendRequests won't notify a
64
67
// parked Connection.
65
- let ( tx, rx) = oneshot:: channel ( ) ;
68
+ let ( tx, rx) = mpsc:: channel ( 0 ) ;
69
+ let rx = rx. into_future ( )
70
+ . map ( |( msg, _) | match msg {
71
+ Some ( never) => match never { } ,
72
+ None => ( ) ,
73
+ } )
74
+ . map_err ( |_| -> Never { unreachable ! ( "mpsc cannot error" ) } ) ;
66
75
let fut = conn
67
76
. inspect ( |_| trace ! ( "connection complete" ) )
68
77
. map_err ( |e| debug ! ( "connection error: {}" , e) )
@@ -73,19 +82,19 @@ where
73
82
// conn has finished either way
74
83
Either :: A ( future:: ok ( ( ) ) )
75
84
} ,
76
- Err ( Either :: B ( ( _ , conn) ) ) => {
77
- // oneshot has been dropped, hopefully polling
85
+ Ok ( Either :: B ( ( ( ) , conn) ) ) => {
86
+ // mpsc has been dropped, hopefully polling
78
87
// the connection some more should start shutdown
79
88
// and then close
80
89
trace ! ( "send_request dropped, starting conn shutdown" ) ;
81
90
Either :: B ( conn)
82
91
}
83
- Ok ( Either :: B ( ( never, _) ) ) => match never { } ,
92
+ Err ( Either :: B ( ( never, _) ) ) => match never { } ,
84
93
} ) ;
85
94
self . executor . execute ( fut) ;
86
95
State :: Ready ( request_tx, tx)
87
96
} ,
88
- State :: Ready ( ref mut tx, _ ) => {
97
+ State :: Ready ( ref mut tx, ref conn_dropper ) => {
89
98
try_ready ! ( tx. poll_ready( ) . map_err( :: Error :: new_h2) ) ;
90
99
match self . rx . poll ( ) {
91
100
Ok ( Async :: Ready ( Some ( ( req, mut cb) ) ) ) => {
@@ -107,8 +116,14 @@ where
107
116
}
108
117
} ;
109
118
if !eos {
110
- let pipe = PipeToSendStream :: new ( body, body_tx) ;
111
- self . executor . execute ( pipe. map_err ( |e| debug ! ( "client request body error: {}" , e) ) ) ;
119
+ let conn_drop_ref = conn_dropper. clone ( ) ;
120
+ let pipe = PipeToSendStream :: new ( body, body_tx)
121
+ . map_err ( |e| debug ! ( "client request body error: {}" , e) )
122
+ . then ( move |x| {
123
+ drop ( conn_drop_ref) ;
124
+ x
125
+ } ) ;
126
+ self . executor . execute ( pipe) ;
112
127
}
113
128
114
129
let fut = fut
0 commit comments