@@ -484,57 +484,67 @@ where
484484 if self . body . is_end_stream ( ) {
485485 break ' result Ok ( None ) ;
486486 } else {
487- return Poll :: Ready ( Ok ( StreamResult :: Completed ) ) ;
487+ // Destination has zero capacity but the body could still have data. Cannot return
488+ // `Completed` (nothing was produced — violates the StreamProducer contract). Fall
489+ // through to poll the body with `cap = None` so it buffers the frame.
490+ None
488491 }
489492 }
490493 None => None ,
491494 } ;
492- match Pin :: new ( & mut self . body ) . poll_frame ( cx) {
493- Poll :: Ready ( Some ( Ok ( frame) ) ) => {
494- match frame. into_data ( ) . map_err ( http_body:: Frame :: into_trailers) {
495- Ok ( mut frame) => {
496- // Libraries like `Reqwest` generate a 0-length frame after sensing end-of-stream,
497- // so we have to check for the body's end-of-stream indicator here too
498- if frame. len ( ) == 0 && self . body . is_end_stream ( ) {
499- break ' result Ok ( None ) ;
500- }
495+ loop {
496+ match Pin :: new ( & mut self . body ) . poll_frame ( cx) {
497+ Poll :: Ready ( Some ( Ok ( frame) ) ) => {
498+ match frame. into_data ( ) . map_err ( http_body:: Frame :: into_trailers) {
499+ Ok ( mut frame) => {
500+ if frame. len ( ) == 0 {
501+ // Zero-length data frames are valid per the http_body::Body
502+ // trait contract and RFC 9113 §6.1 — skip and re-poll
503+ // directly rather than using `wake_by_ref()` + Pending, which
504+ // would just re-enter this function via the task queue
505+ if self . body . is_end_stream ( ) {
506+ break ' result Ok ( None ) ;
507+ }
508+ continue ;
509+ }
501510
502- if let Some ( cap) = cap {
503- let n = frame. len ( ) ;
504- let cap = cap. into ( ) ;
505- if n > cap {
506- // data frame does not fit in destination, fill it and buffer the rest
507- dst. set_buffer ( Cursor :: new ( frame. split_off ( cap) ) ) ;
508- let mut dst = dst. as_direct ( store, cap) ;
509- dst. remaining ( ) . copy_from_slice ( & frame) ;
510- dst. mark_written ( cap) ;
511+ if let Some ( cap) = cap {
512+ let n = frame. len ( ) ;
513+ let cap = cap. into ( ) ;
514+ if n > cap {
515+ // data frame does not fit in destination, fill it and buffer the rest
516+ dst. set_buffer ( Cursor :: new ( frame. split_off ( cap) ) ) ;
517+ let mut dst = dst. as_direct ( store, cap) ;
518+ dst. remaining ( ) . copy_from_slice ( & frame) ;
519+ dst. mark_written ( cap) ;
520+ } else {
521+ // copy the whole frame into the destination
522+ let mut dst = dst. as_direct ( store, n) ;
523+ dst. remaining ( ) [ ..n] . copy_from_slice ( & frame) ;
524+ dst. mark_written ( n) ;
525+ }
511526 } else {
512- // copy the whole frame into the destination
513- let mut dst = dst. as_direct ( store, n) ;
514- dst. remaining ( ) [ ..n] . copy_from_slice ( & frame) ;
515- dst. mark_written ( n) ;
527+ dst. set_buffer ( Cursor :: new ( frame) ) ;
516528 }
517- } else {
518- dst. set_buffer ( Cursor :: new ( frame) ) ;
529+ return Poll :: Ready ( Ok ( StreamResult :: Completed ) ) ;
519530 }
520- return Poll :: Ready ( Ok ( StreamResult :: Completed ) ) ;
521- }
522- Err ( Ok ( trailers) ) => {
523- let view = ( self . getter ) ( store . data_mut ( ) ) ;
524- let trailers = FieldMap :: new_immutable ( trailers ) ;
525- let trailers = view
526- . table
527- . push ( trailers)
528- . context ( "failed to push trailers to table" ) ? ;
529- break ' result Ok ( Some ( trailers ) ) ;
531+ Err ( Ok ( trailers ) ) => {
532+ let view = ( self . getter ) ( store . data_mut ( ) ) ;
533+ let trailers = FieldMap :: new_immutable ( trailers ) ;
534+ let trailers = view
535+ . table
536+ . push ( trailers )
537+ . context ( "failed to push trailers to table" ) ? ;
538+ break ' result Ok ( Some ( trailers) ) ;
539+ }
540+ Err ( Err ( .. ) ) => break ' result Err ( ErrorCode :: HttpProtocolError ) ,
530541 }
531- Err ( Err ( ..) ) => break ' result Err ( ErrorCode :: HttpProtocolError ) ,
532542 }
543+ Poll :: Ready ( Some ( Err ( err) ) ) => break ' result Err ( err) ,
544+ Poll :: Ready ( None ) => break ' result Ok ( None ) ,
545+ Poll :: Pending if finish => return Poll :: Ready ( Ok ( StreamResult :: Cancelled ) ) ,
546+ Poll :: Pending => return Poll :: Pending ,
533547 }
534- Poll :: Ready ( Some ( Err ( err) ) ) => break ' result Err ( err) ,
535- Poll :: Ready ( None ) => break ' result Ok ( None ) ,
536- Poll :: Pending if finish => return Poll :: Ready ( Ok ( StreamResult :: Cancelled ) ) ,
537- Poll :: Pending => return Poll :: Pending ,
538548 }
539549 } ;
540550 self . close ( res) ;
0 commit comments