@@ -10,14 +10,14 @@ use crate::{
10
10
} ;
11
11
use bytes:: Bytes ;
12
12
use http:: {
13
- header:: { self , HeaderMap , HeaderValue } ,
13
+ header:: { self , HeaderMap , HeaderName , HeaderValue } ,
14
14
StatusCode ,
15
15
} ;
16
16
use http_body:: {
17
17
combinators:: { MapData , MapErr } ,
18
18
Empty , Full ,
19
19
} ;
20
- use std:: { borrow:: Cow , convert:: Infallible } ;
20
+ use std:: { borrow:: Cow , convert:: Infallible , iter } ;
21
21
22
22
mod headers;
23
23
@@ -148,6 +148,42 @@ pub trait IntoResponse {
148
148
fn into_response ( self ) -> Response ;
149
149
}
150
150
151
+ /// Trait for generating response headers.
152
+ ///
153
+
154
+ /// **Note: If you see this trait not being implemented in an error message, you are almost
155
+ /// certainly being mislead by the compiler¹. Look for the following snippet in the output and
156
+ /// check [`IntoResponse`]'s documentation if you find it:**
157
+ ///
158
+ /// ```text
159
+ /// note: required because of the requirements on the impl of `IntoResponse` for `<type>`
160
+ /// ```
161
+ ///
162
+ /// Any type that implements this trait automatically implements `IntoResponse` as well, but can
163
+ /// also be used in a tuple like `(StatusCode, Self)`, `(Self, impl IntoResponseHeaders)`,
164
+ /// `(StatusCode, Self, impl IntoResponseHeaders, impl IntoResponse)` and so on.
165
+ ///
166
+ /// This trait can't currently be implemented outside of axum.
167
+ ///
168
+ /// ¹ See also [this rustc issue](https://github.com/rust-lang/rust/issues/22590)
169
+ pub trait IntoResponseHeaders {
170
+ /// The return type of [`.into_headers()`].
171
+ ///
172
+ /// The iterator item is a `Result` to allow the implementation to return a server error
173
+ /// instead.
174
+ ///
175
+ /// The header name is optional because `HeaderMap`s iterator doesn't yield it multiple times
176
+ /// for headers that have multiple values, to avoid unnecessary copies.
177
+ #[ doc( hidden) ]
178
+ type IntoIter : IntoIterator < Item = Result < ( Option < HeaderName > , HeaderValue ) , Response > > ;
179
+
180
+ /// Attempt to turn `self` into a list of headers.
181
+ ///
182
+ /// In practice, only the implementation for `axum::response::Headers` ever returns `Err(_)`.
183
+ #[ doc( hidden) ]
184
+ fn into_headers ( self ) -> Self :: IntoIter ;
185
+ }
186
+
151
187
impl IntoResponse for ( ) {
152
188
fn into_response ( self ) -> Response {
153
189
Response :: new ( boxed ( Empty :: new ( ) ) )
@@ -320,6 +356,21 @@ impl IntoResponse for StatusCode {
320
356
}
321
357
}
322
358
359
+ impl < H > IntoResponse for H
360
+ where
361
+ H : IntoResponseHeaders ,
362
+ {
363
+ fn into_response ( self ) -> Response {
364
+ let mut res = Response :: new ( boxed ( Empty :: new ( ) ) ) ;
365
+
366
+ if let Err ( e) = try_extend_headers ( res. headers_mut ( ) , self . into_headers ( ) ) {
367
+ return e;
368
+ }
369
+
370
+ res
371
+ }
372
+ }
373
+
323
374
impl < T > IntoResponse for ( StatusCode , T )
324
375
where
325
376
T : IntoResponse ,
@@ -331,33 +382,98 @@ where
331
382
}
332
383
}
333
384
334
- impl < T > IntoResponse for ( HeaderMap , T )
385
+ impl < H , T > IntoResponse for ( H , T )
335
386
where
387
+ H : IntoResponseHeaders ,
336
388
T : IntoResponse ,
337
389
{
338
390
fn into_response ( self ) -> Response {
339
391
let mut res = self . 1 . into_response ( ) ;
340
- res. headers_mut ( ) . extend ( self . 0 ) ;
392
+
393
+ if let Err ( e) = try_extend_headers ( res. headers_mut ( ) , self . 0 . into_headers ( ) ) {
394
+ return e;
395
+ }
396
+
341
397
res
342
398
}
343
399
}
344
400
345
- impl < T > IntoResponse for ( StatusCode , HeaderMap , T )
401
+ impl < H , T > IntoResponse for ( StatusCode , H , T )
346
402
where
403
+ H : IntoResponseHeaders ,
347
404
T : IntoResponse ,
348
405
{
349
406
fn into_response ( self ) -> Response {
350
407
let mut res = self . 2 . into_response ( ) ;
351
408
* res. status_mut ( ) = self . 0 ;
352
- res. headers_mut ( ) . extend ( self . 1 ) ;
409
+
410
+ if let Err ( e) = try_extend_headers ( res. headers_mut ( ) , self . 1 . into_headers ( ) ) {
411
+ return e;
412
+ }
413
+
353
414
res
354
415
}
355
416
}
356
417
357
- impl IntoResponse for HeaderMap {
358
- fn into_response ( self ) -> Response {
359
- let mut res = Response :: new ( boxed ( Empty :: new ( ) ) ) ;
360
- * res. headers_mut ( ) = self ;
361
- res
418
+ impl IntoResponseHeaders for HeaderMap {
419
+ // FIXME: Use type_alias_impl_trait when available
420
+ type IntoIter = iter:: Map <
421
+ http:: header:: IntoIter < HeaderValue > ,
422
+ fn (
423
+ ( Option < HeaderName > , HeaderValue ) ,
424
+ ) -> Result < ( Option < HeaderName > , HeaderValue ) , Response > ,
425
+ > ;
426
+
427
+ fn into_headers ( self ) -> Self :: IntoIter {
428
+ self . into_iter ( ) . map ( Ok )
429
+ }
430
+ }
431
+
432
+ // Slightly adjusted version of `impl<T> Extend<(Option<HeaderName>, T)> for HeaderMap<T>`.
433
+ // Accepts an iterator that returns Results and short-circuits on an `Err`.
434
+ fn try_extend_headers (
435
+ headers : & mut HeaderMap ,
436
+ iter : impl IntoIterator < Item = Result < ( Option < HeaderName > , HeaderValue ) , Response > > ,
437
+ ) -> Result < ( ) , Response > {
438
+ use http:: header:: Entry ;
439
+
440
+ let mut iter = iter. into_iter ( ) ;
441
+
442
+ // The structure of this is a bit weird, but it is mostly to make the
443
+ // borrow checker happy.
444
+ let ( mut key, mut val) = match iter. next ( ) . transpose ( ) ? {
445
+ Some ( ( Some ( key) , val) ) => ( key, val) ,
446
+ Some ( ( None , _) ) => panic ! ( "expected a header name, but got None" ) ,
447
+ None => return Ok ( ( ) ) ,
448
+ } ;
449
+
450
+ ' outer: loop {
451
+ let mut entry = match headers. entry ( key) {
452
+ Entry :: Occupied ( mut e) => {
453
+ // Replace all previous values while maintaining a handle to
454
+ // the entry.
455
+ e. insert ( val) ;
456
+ e
457
+ }
458
+ Entry :: Vacant ( e) => e. insert_entry ( val) ,
459
+ } ;
460
+
461
+ // As long as `HeaderName` is none, keep inserting the value into
462
+ // the current entry
463
+ loop {
464
+ match iter. next ( ) . transpose ( ) ? {
465
+ Some ( ( Some ( k) , v) ) => {
466
+ key = k;
467
+ val = v;
468
+ continue ' outer;
469
+ }
470
+ Some ( ( None , v) ) => {
471
+ entry. append ( v) ;
472
+ }
473
+ None => {
474
+ return Ok ( ( ) ) ;
475
+ }
476
+ }
477
+ }
362
478
}
363
479
}
0 commit comments