@@ -1687,24 +1687,31 @@ HttpSM::handle_api_return()
16871687
16881688 switch (t_state.next_action ) {
16891689 case HttpTransact::StateMachineAction_t::TRANSFORM_READ : {
1690+ // A plugin has installed an internal response body (TSHttpTxnErrorBodySet()).
1691+ // In this branch we bypass transform streaming and switch to internal transfer.
16901692 if (t_state.internal_msg_buffer && !t_state.api_server_request_body_set && t_state.hdr_info .server_response .valid ()) {
16911693 SMDbg (dbg_ctl_http, " plugin set internal body, bypassing response transform for internal transfer" );
16921694 t_state.api_info .cache_untransformed = true ;
1695+ // If a tunnel was already set up for transform I/O, shut it down before we re-route.
16931696 if (tunnel.is_tunnel_active ()) {
16941697 tunnel.kill_tunnel ();
16951698 }
1699+ // Drop transform VC table state because this path no longer drives transform reads.
16961700 if (transform_info.entry != nullptr ) {
16971701 vc_table.cleanup_entry (transform_info.entry );
16981702 transform_info.entry = nullptr ;
16991703 }
17001704 transform_info.vc = nullptr ;
1705+ // Some downstream paths still read client_response; seed it from transform_response when missing.
17011706 if (t_state.hdr_info .client_response .valid () == 0 && t_state.hdr_info .transform_response .valid ()) {
17021707 t_state.hdr_info .client_response .create (HTTPType::RESPONSE );
17031708 t_state.hdr_info .client_response .copy (&t_state.hdr_info .transform_response );
17041709 }
1710+ // The server session is not needed for internal body transfer if it was never tunneled.
17051711 if (server_entry != nullptr && server_entry->in_tunnel == false ) {
17061712 release_server_session ();
17071713 }
1714+ // Serve the plugin-provided body through the internal tunnel handler.
17081715 setup_internal_transfer (&HttpSM::tunnel_handler);
17091716 } else {
17101717 HttpTunnelProducer *p = setup_transfer_from_transform ();
0 commit comments