@@ -2512,19 +2512,26 @@ const HookMask Do_upstream_rsp_body::HOOKS{MaskFor({Hook::URSP})};
25122512Errata
25132513Do_upstream_rsp_body::invoke (Context &ctx)
25142514{
2515- // State allocated in transaction memory.
2515+ // / State data for the transform continuation.
2516+ // / @internal Due to ugliness in the plugin API where the final event for the @c Continuation
2517+ // / can arrive after the transaction is destroyed, the @c IOBuffer needs to get cleaned up at
2518+ // / transaction termination, not the final transform event. Therefore the destructor here does
2519+ // / the cleanup, so that it can be marked for cleanup in the @c Context.
25162520 struct State {
25172521 TextView _view; // /< Source view for body.
25182522 TSIOBuffer _tsio_buff = nullptr ; // /< Buffer used to write body.
2523+ // / Clean up the @c IOBuffer.
2524+ ~State () {
2525+ if (_tsio_buff) {
2526+ TSIOBufferDestroy (_tsio_buff);
2527+ }
2528+ }
25192529 };
25202530
25212531 auto static transform = [](TSCont contp, TSEvent ev_code, void *) -> int {
25222532
2523- if (TSVConnClosedGet (contp)) { // all done, time to clean up.
2524- auto state = static_cast <State*>(TSContDataGet (contp));
2525- if (state && state->_tsio_buff ) {
2526- TSIOBufferDestroy (state->_tsio_buff );
2527- }
2533+ if (TSVConnClosedGet (contp)) {
2534+ // IOBuffer is cleaned up at transaction close, not here.
25282535 TSContDestroy (contp);
25292536 return 0 ;
25302537 }
@@ -2587,6 +2594,7 @@ Do_upstream_rsp_body::invoke(Context &ctx)
25872594 // The view contents are in the transaction data, but the view in the feature is not.
25882595 // Put a copy in the transform @a state.
25892596 auto state = ctx.make <State>();
2597+ ctx.mark_for_cleanup (state);
25902598 auto cont = TSTransformCreate (transform, ctx._txn );
25912599 state->_view = *content;
25922600 TSContDataSet (cont, state);
0 commit comments