static void tx_finish(ib_tx_t *tx) { if (!ib_flags_all(tx->flags, IB_TX_FREQ_FINISHED) ) { ib_state_notify_request_finished(tx->ib, tx); } if (!ib_flags_all(tx->flags, IB_TX_FRES_FINISHED) ) { ib_state_notify_response_finished(tx->ib, tx); } if (!ib_flags_all(tx->flags, IB_TX_FPOSTPROCESS)) { ib_state_notify_postprocess(tx->ib, tx); } if (!ib_flags_all(tx->flags, IB_TX_FLOGGING)) { ib_state_notify_logging(tx->ib, tx); } }
/** * A body filter to intercept response body and feed it to Ironbee, * and to buffer the data if required by Ironbee configuration. * * @param[in] r The nginx request object. * @param[in] in The data to filter. * @return status propagated from next filter, or OK/Error */ static ngx_int_t ironbee_body_out(ngx_http_request_t *r, ngx_chain_t *in) { ngx_log_t *prev_log; ngxib_req_ctx *ctx; ngx_chain_t *link; ib_status_t rc; ib_num_t num; ngx_int_t rv = NGX_OK; ib_txdata_t itxdata; if (r->internal) return ngx_http_next_body_filter(r, in); prev_log = ngxib_log(r->connection->log); ctx = ngx_http_get_module_ctx(r, ngx_ironbee_module); assert((ctx != NULL) && (ctx->tx != NULL)); ib_log_debug_tx(ctx->tx, "ironbee_body_out"); if (in == NULL) { /* FIXME: could this happen in circumstances when we should * notify Ironbee of end-of-response ? */ ib_log_debug_tx(ctx->tx, "ironbee_body_out: input was null"); cleanup_return(prev_log) ngx_http_next_body_filter(r, in); } ctx = ngx_http_get_module_ctx(r, ngx_ironbee_module); if (ctx->output_filter_done) { ib_log_debug_tx(ctx->tx, "ironbee_body_out: already done"); cleanup_return(prev_log) ngx_http_next_body_filter(r, in); } if (!ctx->output_filter_init) { ctx->output_filter_init = 1; if (ctx->internal_errordoc) { /* If it's our own errordoc, pass it straight through */ /* Should we log anything here? The error will already * have been logged. */ ctx->output_buffering = IOBUF_NOBUF; ctx->response_buf = NULL; ib_log_debug_tx(ctx->tx, "ironbee_body_out: in internal errordoc"); } else { /* Determine whether we're configured to buffer */ rc = ib_context_get(ctx->tx->ctx, "buffer_res", ib_ftype_num_out(&num), NULL); ib_log_debug_tx(ctx->tx, "ironbee_body_out: buffer_res is %d", (int)num); if (rc != IB_OK) ib_log_error_tx(ctx->tx, "Can't determine output buffer configuration!"); if (num == 0) { ib_log_debug_tx(ctx->tx, "ironbee_body_out: NOBUF"); ctx->output_buffering = IOBUF_NOBUF; ctx->response_buf = NULL; } else { /* If we're buffering, initialise the buffer */ ib_log_debug_tx(ctx->tx, "ironbee_body_out: BUFFER"); ctx->output_buffering = IOBUF_BUFFER; } } } ngx_regex_malloc_init(r->pool); for (link = in; link != NULL; link = link->next) { /* Feed the data to ironbee */ itxdata.data = link->buf->pos; itxdata.dlen = link->buf->last - link->buf->pos; ib_log_debug_tx(ctx->tx, "ironbee_body_out: %d bytes", (int)itxdata.dlen); if (itxdata.dlen > 0) { ib_state_notify_response_body_data(ironbee, ctx->tx, &itxdata); } /* If Ironbee just signalled an error, switch to discard data mode, * and dump anything we already have buffered, */ if ( (STATUS_IS_ERROR(ctx->status)) && !ctx->internal_errordoc && (ctx->output_buffering != IOBUF_DISCARD) ) { ib_log_debug_tx(ctx->tx, "ironbee_body_out: error %d", ctx->status); free_chain(r->pool, ctx->response_buf); ctx->response_buf = NULL; ctx->output_buffering = IOBUF_DISCARD; } else if (ctx->output_buffering == IOBUF_BUFFER) { /* Copy any data to our buffer */ if (ctx->response_buf == NULL) { ctx->response_buf = ngx_pcalloc(r->pool, sizeof(ngx_chain_t)); ctx->response_ptr = ctx->response_buf; } else { ctx->response_ptr->next = ngx_pcalloc(r->pool, sizeof(ngx_chain_t)); ctx->response_ptr = ctx->response_ptr->next; } /* Not sure if any data types need setaside, but let's be safe */ #if NO_COPY_REQUIRED ctx->response_ptr->buf = link->buf; #else if (itxdata.dlen > 0) { ctx->response_ptr->buf = ngx_create_temp_buf(r->pool, itxdata.dlen); memcpy(ctx->response_ptr->buf->pos, link->buf->pos, itxdata.dlen); ctx->response_ptr->buf->last += itxdata.dlen; } else { ctx->response_ptr->buf = ngx_palloc(r->pool, sizeof(ngx_buf_t)); memcpy(ctx->response_ptr->buf, link->buf, sizeof(ngx_buf_t)); } #endif } if (link->buf->last_buf) { ib_log_debug_tx(ctx->tx, "ironbee_body_out: last_buf"); ctx->output_filter_done = 1; } } if (ctx->output_buffering == IOBUF_NOBUF) { /* Normal operation - pass it down the chain */ ib_log_debug_tx(ctx->tx, "ironbee_body_out: passing on"); ctx->start_response = 1; rv = ngx_http_next_body_filter(r, in); } else if (ctx->output_buffering == IOBUF_BUFFER) { ib_log_debug_tx(ctx->tx, "ironbee_body_out: buffering"); if (ctx->output_filter_done) { /* We can pass on the buffered data all at once */ ib_log_debug_tx(ctx->tx, "ironbee_body_out: passing buffer"); ctx->start_response = 1; rv = ngx_http_next_body_filter(r, ctx->response_buf); } } else if (ctx->output_buffering == IOBUF_DISCARD) { ib_log_debug_tx(ctx->tx, "ironbee_body_out: discarding"); if (ctx->output_filter_done) { /* Pass a last bucket with no data */ //ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, // "ironbee_body_out: passing NULL last-buffer"); //ctx->response_buf = ngx_pcalloc(r->pool, sizeof(ngx_chain_t)); //ctx->response_buf->buf = ngx_calloc_buf(r->pool); //ctx->response_buf->buf->last_buf = ctx->response_buf->buf->last_in_chain = 1; //rv = ngx_http_next_body_filter(r, ctx->response_buf); /* FIXME: Is setting rv enough to serve error page */ rv = ctx->status; } } if (ctx->output_filter_done) { ib_log_debug_tx(ctx->tx, "ironbee_body_out: notify_postprocess"); rc = ib_state_notify_postprocess(ironbee, ctx->tx); if ((rv == NGX_OK) && (rc != IB_OK)) { rv = NGX_HTTP_INTERNAL_SERVER_ERROR; } rc = ib_state_notify_logging(ironbee, ctx->tx); if ((rv == NGX_OK) && (rc != IB_OK)) { rv = NGX_HTTP_INTERNAL_SERVER_ERROR; } } cleanup_return(prev_log) rv; }