@@ -2,7 +2,7 @@ use super::{h1, h2, Body};
2
2
use futures:: prelude:: * ;
3
3
use http:: header:: { HeaderValue , TRANSFER_ENCODING } ;
4
4
use http_body:: Frame ;
5
- use linkerd_error:: { Error , Result } ;
5
+ use linkerd_error:: Result ;
6
6
use linkerd_http_box:: BoxBody ;
7
7
use linkerd_stack:: { layer, MakeConnection , Service } ;
8
8
use std:: {
@@ -43,6 +43,20 @@ pub struct Downgrade<S> {
43
43
#[ derive( Clone , Debug ) ]
44
44
pub struct WasUpgrade ( ( ) ) ;
45
45
46
+ /// An error returned by the [`Upgrade`] client.
47
+ ///
48
+ /// This can represent an error presented by either of the underlying HTTP/1 or HTTP/2 clients,
49
+ /// or a "downgraded" HTTP/2 error.
50
+ #[ derive( Debug , Error ) ]
51
+ pub enum Error {
52
+ #[ error( "{0}" ) ]
53
+ Downgraded ( #[ from] DowngradedH2Error ) ,
54
+ #[ error( transparent) ]
55
+ H1 ( linkerd_error:: Error ) ,
56
+ #[ error( transparent) ]
57
+ H2 ( hyper:: Error ) ,
58
+ }
59
+
46
60
// === impl Upgrade ===
47
61
48
62
impl < C , T , B > Upgrade < C , T , B > {
@@ -59,22 +73,24 @@ where
59
73
C :: Future : Unpin + Send + ' static ,
60
74
B : crate :: Body + Send + Unpin + ' static ,
61
75
B :: Data : Send ,
62
- B :: Error : Into < Error > + Send + Sync ,
76
+ B :: Error : Into < linkerd_error :: Error > + Send + Sync ,
63
77
{
64
78
type Response = http:: Response < BoxBody > ;
65
79
type Error = Error ;
66
- type Future = Pin < Box < dyn Future < Output = Result < http:: Response < BoxBody > > > + Send + ' static > > ;
80
+ type Future = Pin <
81
+ Box < dyn Future < Output = Result < http:: Response < BoxBody > , Self :: Error > > + Send + ' static > ,
82
+ > ;
67
83
68
84
#[ inline]
69
85
fn poll_ready ( & mut self , cx : & mut Context < ' _ > ) -> Poll < Result < ( ) , Self :: Error > > {
70
86
let Self { http1, h2 } = self ;
71
87
72
- match http1. poll_ready ( cx) {
88
+ match http1. poll_ready ( cx) . map_err ( Error :: H1 ) {
73
89
Poll :: Ready ( Ok ( ( ) ) ) => { }
74
90
poll => return poll,
75
91
}
76
92
77
- h2. poll_ready ( cx) . map_err ( downgrade_h2_error )
93
+ h2. poll_ready ( cx) . map_err ( Error :: h2 )
78
94
}
79
95
80
96
fn call ( & mut self , mut req : http:: Request < B > ) -> Self :: Future {
@@ -85,7 +101,12 @@ where
85
101
. is_some ( )
86
102
{
87
103
debug ! ( "Skipping orig-proto upgrade due to HTTP/1.1 upgrade" ) ;
88
- return Box :: pin ( self . http1 . call ( req) . map_ok ( |rsp| rsp. map ( BoxBody :: new) ) ) ;
104
+ return Box :: pin (
105
+ self . http1
106
+ . call ( req)
107
+ . map_ok ( |rsp| rsp. map ( BoxBody :: new) )
108
+ . map_err ( Error :: H1 ) ,
109
+ ) ;
89
110
}
90
111
91
112
let orig_version = req. version ( ) ;
@@ -113,97 +134,113 @@ where
113
134
114
135
* req. version_mut ( ) = http:: Version :: HTTP_2 ;
115
136
116
- Box :: pin (
117
- self . h2
118
- . call ( req)
119
- . map_err ( downgrade_h2_error)
120
- . map_ok ( move |mut rsp| {
121
- let version = rsp
122
- . headers_mut ( )
123
- . remove ( L5D_ORIG_PROTO )
124
- . and_then ( |orig_proto| {
125
- if orig_proto == "HTTP/1.1" {
126
- Some ( http:: Version :: HTTP_11 )
127
- } else if orig_proto == "HTTP/1.0" {
128
- Some ( http:: Version :: HTTP_10 )
129
- } else {
130
- None
131
- }
132
- } )
133
- . unwrap_or ( orig_version) ;
134
- trace ! ( ?version, "Downgrading response" ) ;
135
- * rsp. version_mut ( ) = version;
136
- rsp. map ( |inner| BoxBody :: new ( UpgradeResponseBody { inner } ) )
137
- } ) ,
138
- )
137
+ Box :: pin ( self . h2 . call ( req) . map_err ( Error :: h2) . map_ok ( move |mut rsp| {
138
+ let version = rsp
139
+ . headers_mut ( )
140
+ . remove ( L5D_ORIG_PROTO )
141
+ . and_then ( |orig_proto| {
142
+ if orig_proto == "HTTP/1.1" {
143
+ Some ( http:: Version :: HTTP_11 )
144
+ } else if orig_proto == "HTTP/1.0" {
145
+ Some ( http:: Version :: HTTP_10 )
146
+ } else {
147
+ None
148
+ }
149
+ } )
150
+ . unwrap_or ( orig_version) ;
151
+ trace ! ( ?version, "Downgrading response" ) ;
152
+ * rsp. version_mut ( ) = version;
153
+ rsp. map ( |inner| BoxBody :: new ( UpgradeResponseBody { inner } ) )
154
+ } ) )
155
+ }
156
+ }
157
+
158
+ // === impl Error ===
159
+
160
+ impl Error {
161
+ fn h2 ( err : hyper:: Error ) -> Self {
162
+ if let Some ( downgraded) = downgrade_h2_error ( & err) {
163
+ return Self :: Downgraded ( downgraded) ;
164
+ }
165
+
166
+ Self :: H2 ( err)
139
167
}
140
168
}
141
169
142
170
/// Handles HTTP/2 client errors for HTTP/1.1 requests by wrapping the error type. This
143
171
/// simplifies error handling elsewhere so that HTTP/2 errors can only be encountered when the
144
172
/// original request was HTTP/2.
145
- fn downgrade_h2_error < E : std:: error:: Error + Send + Sync + ' static > ( orig : E ) -> Error {
173
+ fn downgrade_h2_error < E : std:: error:: Error + Send + Sync + ' static > (
174
+ orig : & E ,
175
+ ) -> Option < DowngradedH2Error > {
146
176
#[ inline]
147
177
fn reason ( e : & ( dyn std:: error:: Error + ' static ) ) -> Option < h2:: Reason > {
148
178
e. downcast_ref :: < h2:: H2Error > ( ) ?. reason ( )
149
179
}
150
180
151
181
// If the provided error was an H2 error, wrap it as a downgraded error.
152
- if let Some ( reason) = reason ( & orig) {
153
- return DowngradedH2Error ( reason) . into ( ) ;
182
+ if let Some ( reason) = reason ( orig) {
183
+ return Some ( DowngradedH2Error ( reason) ) ;
154
184
}
155
185
156
186
// Otherwise, check the source chain to see if its original error was an H2 error.
157
187
let mut cause = orig. source ( ) ;
158
188
while let Some ( error) = cause {
159
189
if let Some ( reason) = reason ( error) {
160
- return DowngradedH2Error ( reason) . into ( ) ;
190
+ return Some ( DowngradedH2Error ( reason) ) ;
161
191
}
162
192
163
193
cause = error. source ( ) ;
164
194
}
165
195
166
- // If the error was not an H2 error, return the original error (boxed) .
167
- orig . into ( )
196
+ // If the error was not an H2 error, return None .
197
+ None
168
198
}
169
199
170
200
#[ cfg( test) ]
171
201
#[ test]
172
202
fn test_downgrade_h2_error ( ) {
173
203
assert ! (
174
- downgrade_h2_error( h2:: H2Error :: from( h2:: Reason :: PROTOCOL_ERROR ) ) . is :: < DowngradedH2Error > ( ) ,
204
+ downgrade_h2_error( & h2:: H2Error :: from( h2:: Reason :: PROTOCOL_ERROR ) ) . is_some ( ) ,
175
205
"h2 errors must be downgraded"
176
206
) ;
177
207
178
208
#[ derive( Debug , Error ) ]
179
209
#[ error( "wrapped h2 error: {0}" ) ]
180
- struct WrapError ( #[ source] Error ) ;
210
+ struct WrapError ( #[ source] linkerd_error :: Error ) ;
181
211
assert ! (
182
- downgrade_h2_error( WrapError (
212
+ downgrade_h2_error( & WrapError (
183
213
h2:: H2Error :: from( h2:: Reason :: PROTOCOL_ERROR ) . into( )
184
214
) )
185
- . is :: < DowngradedH2Error > ( ) ,
215
+ . is_some ( ) ,
186
216
"wrapped h2 errors must be downgraded"
187
217
) ;
188
218
189
219
assert ! (
190
- downgrade_h2_error( WrapError (
220
+ downgrade_h2_error( & WrapError (
191
221
WrapError ( h2:: H2Error :: from( h2:: Reason :: PROTOCOL_ERROR ) . into( ) ) . into( )
192
222
) )
193
- . is :: < DowngradedH2Error > ( ) ,
223
+ . is_some ( ) ,
194
224
"double-wrapped h2 errors must be downgraded"
195
225
) ;
196
226
197
227
assert ! (
198
- ! downgrade_h2_error( std:: io:: Error :: new(
228
+ downgrade_h2_error( & std:: io:: Error :: new(
199
229
std:: io:: ErrorKind :: Other ,
200
230
"non h2 error"
201
231
) )
202
- . is :: < DowngradedH2Error > ( ) ,
232
+ . is_none ( ) ,
203
233
"other h2 errors must not be downgraded"
204
234
) ;
205
235
}
206
236
237
+ #[ cfg( test) ]
238
+ #[ test]
239
+ fn test_downgrade_error_source ( ) {
240
+ let err = Error :: Downgraded ( DowngradedH2Error ( h2:: Reason :: PROTOCOL_ERROR ) ) ;
241
+ assert ! ( linkerd_error:: is_caused_by:: <DowngradedH2Error >( & err) ) ;
242
+ }
243
+
207
244
// === impl UpgradeResponseBody ===
208
245
209
246
impl < B > Body for UpgradeResponseBody < B >
@@ -212,7 +249,7 @@ where
212
249
B :: Error : std:: error:: Error + Send + Sync + ' static ,
213
250
{
214
251
type Data = B :: Data ;
215
- type Error = Error ;
252
+ type Error = linkerd_error :: Error ;
216
253
217
254
#[ inline]
218
255
fn is_end_stream ( & self ) -> bool {
@@ -223,10 +260,11 @@ where
223
260
self : Pin < & mut Self > ,
224
261
cx : & mut Context < ' _ > ,
225
262
) -> Poll < Option < Result < Frame < Self :: Data > , Self :: Error > > > {
226
- self . project ( )
227
- . inner
228
- . poll_frame ( cx)
229
- . map_err ( downgrade_h2_error)
263
+ self . project ( ) . inner . poll_frame ( cx) . map_err ( |err| {
264
+ downgrade_h2_error ( & err)
265
+ . map ( Into :: into)
266
+ . unwrap_or_else ( || err. into ( ) )
267
+ } )
230
268
}
231
269
232
270
#[ inline]
0 commit comments