2: header already sent

Roman Arutyunyan arut at nginx.com
Sat Jul 14 21:36:40 UTC 2018


Hi Dk Jack,

On Fri, Jul 13, 2018 at 01:18:40PM -0700, Dk Jack wrote:
> Sorry, I made a typo in my earlier email. Please read the following
> sentence:
> 
> "When I send the response in the *content* phase handler, I am am seeing
> 'header already sent' messages in the error log."
> 
> as
> 
> "When I send the response in the *access* phase handler, I am am seeing
> 'header already sent' messages in the error log."

That's because you do not finalize the request after you send a response in
the access handler.  A content handler (standard static or another one) is
still called at the content phase.  It tries to send a second response and
obviously gets this error.

Finalize the request in the access phase after you send the response with
this call:

    ngx_http_finalize_request(r, rc)

where rc is what ngx_http_output_filter() returned.  Then return NGX_DONE
from the access handler to prevent further request processing.

If you want to return a standard error page, simply return its HTTP status code
from your access handler instead.

> On Thu, Jul 12, 2018 at 1:29 PM Dk Jack <dnj0496 at gmail.com> wrote:
> 
> > Hi,
> > Sorry for sending this again. I haven't been able to resolve my issue.
> > I've read several modules for example and gone over several docs etc. but
> > with no success so far.
> >
> > In my module, I need to either drop the request or allow the request. When
> > I drop the request, I need to send custom response and status. The custom
> > response and status don't come from the config file. When I send the
> > response in the content phase handler, I am am seeing 'header already sent'
> > messages in the error log. How can prevent further processing of the
> > request after my handler is called for the terminal case?
> >
> > Since my module needs to examine all requests irrespective of the uri, I
> > tried registering a content phase handler and send the custom response in
> > that handler. However, my content phase handler is never invoked. I suspect
> > some other content handler is overriding my content handler.  Is there a
> > way I can prevent that i.e. for a request, can I force only my content
> > handler to be called.
> >
> > Please let me know what I am doing wrong? I just need to send/perform
> > custom response/actions when a request matches my criteria. I've include a
> > skeleton of my code.
> > Any inputs are greatly appreciated. Thanks.
> >
> > regards,
> > Dk.
> >
> > http_mod_send_response(ngx_http_request_t *r, ngx_uint_t custom_status,
> > char *body, ngx_int_t blen)
> > {
> >   ngx_int_t rc;
> >   ngx_log_t *log = r->connection->log;
> >   ngx_buf_t *buf = ngx_create_temp_buf(r->pool, (blen+16)); // pad.
> >
> >   if (NULL == buf) {
> >     ngx_log_error(NGX_LOG_ERR, log, 0, "%s: Failed to allocate buffer",
> > __FUNCTION__);
> >     return NGX_ERROR;
> >   }
> >
> >   buf->last = ngx_copy(buf->start, (unsigned char*) data, dlen);
> >   rc = ngx_http_discard_request_body(r);
> >   if (rc != NGX_OK) {
> >     ngx_log_error(NGX_LOG_ERR, log, 0, "%s: Discard req. body failed.
> > rc=%i", __FUNCTION__, rc);
> >     return rc;
> >   }
> >
> >   r->headers_out.status = custom_status;
> >   r->headers_out.content_length_n = buf->last - buf->pos;
> >   r->headers_out.content_type.len = sizeof("text/plain") - 1;
> >   r->headers_out.content_type.data = (u_char *) "text/plain";
> >
> >   rc = ngx_http_send_header(r);
> >   if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
> >     ngx_log_error(NGX_LOG_ERR, log, 0, "%s: Send header failed. rc=%i",
> > __FUNCTION__, rc);
> >     return rc;
> >   }
> >
> >   ngx_chain_t *out_chain = ngx_alloc_chain_link(r->pool);
> >   if (NULL == out_chain) {
> >     ngx_log_error(NGX_LOG_ERR, log, 0, "%s: Buffer chain alloc failed",
> > __FUNCTION__);
> >     return NGX_ERROR;
> >   }
> >
> >   out_chain->buf = buf;
> >   out_chain->next = NULL;
> >   buf->last_buf = 1;
> >   buf->last_in_chain = 1;
> >
> >   return ngx_http_output_filter(r, out_chain);
> > }
> >
> > typedef struct {
> >   unsigned done:1;
> > } ngx_http_mod_ctx_t;
> >
> > int
> > http_module_process_request(ngx_http_request_t *r, ngx_uint_t *status,
> > char *body, ngx_uint_t *blen)
> > {
> >   if (/* request matches criteria */) {
> >     /* other boiler plate code */
> >     *status = get_custom_status();
> >     *body = get_custom_body();
> >     *blen = ngx_strlen(body);
> >     return *status;    // this can be different from custom status.
> >   }
> >
> >   return NGX_DECLINED;
> > }
> >
> > static ngx_int_t
> > ngx_http_request_handler(ngx_http_request_t *r)
> > {
> >   ngx_http_mod_ctx_t *ctx = ngx_http_get_module_ctx(r, nginx_http_module);
> >
> >   if (ctx) {
> >     ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "duplicate
> >     invokation");
> >     return NGX_DECLINED;
> >   } else {
> >     ctx = ngx_palloc(r->connection->pool, sizeof(ngx_http_mod_ctx_t));
> >     if (ctx == NULL) {
> >       ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
> >       "Out of memory. Cannot allocate context");
> >       return NGX_ERROR;
> >     }
> >
> >     ctx->done = 0;
> >     ngx_http_set_ctx(r, ctx, nginx_http_module);
> >   }
> >
> >   ngx_int_t rc = 0;
> >   char custom_body[512];
> >   ngx_uint_t blen;
> >   ngx_uint_t custom_status;
> >   if (!ctx->done) {
> >     rc = http_module_process_request(r, &custom_status, custom_body,
> > &blen);
> >   }
> >
> >   ctx->done = 1;
> >   if ((rc != 0) && (rc != NGX_DECLINED)) {
> >     return http_mod_send_response(r, custom_status, custom_body, blen);
> >     /* alternate implementation, send response in content handler.
> >       ngx_buf_t *buf = ngx_create_temp_buf(r->pool, blen);
> >       buf->last = ngx_copy(buf->start, (unsigned char*) data, dlen);
> >       ctx->custom_body = buf;
> >       ctx->rcode = custom_status;
> >      */
> >   }
> >
> >   return NGX_DECLINED;
> > }
> >
> > static ngx_int_t
> > http_module_content_handler(ngx_http_request_t *r)
> > {
> >   ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s: invoked",
> > __FUNCTION__);
> >   ngx_http_ss_ctx_t *ctx = ngx_http_get_module_ctx(r,
> > nginx_mitigator_module);
> >
> >   if (ctx && ctx->content) {
> >     ctx->content = 0;
> >     return http_mod_send_response(r, ctx->rcode, ctx->custom_body);
> >   } else {
> >     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
> >                   "%s: ctx = %p, content = %d", __FUNCTION__, ctx, ctx ?
> > ctx->content : -1);
> >   }
> >
> >   return NGX_DECLINED;
> > }
> >
> > static ngx_int_t
> > ngx_http_module_init (ngx_conf_t *cf)
> > {
> >   ngx_http_core_main_conf_t *cmcf = ngx_http_conf_get_module_main_conf(cf,
> > ngx_http_core_module);
> >
> >   if (!cmcf) {
> >     ngx_log_error(NGX_LOG_ERR, cf->log, 0, "Failed to retrieve module main
> > conf");
> >     return NGX_ERROR;
> >   }
> >
> >   ngx_http_handler_pt *hptr =
> > ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
> >   if (hptr == NULL) {
> >     ngx_log_error(NGX_LOG_ERR, cf->log, 0, "Could not retrieve access
> > phase handler");
> >     return NGX_ERROR;
> >   }
> >
> >   *hptr = ngx_http_request_handler;
> >   ngx_log_error(NGX_LOG_INFO, cf->log, 0, "[init] Installed request
> > handler");
> >
> >   ngx_http_handler_pt *cptr =
> > ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
> >   if (cptr == NULL) {
> >     ngx_log_error(NGX_LOG_ERR, cf->log, 0, "Could not retrieve access
> > phase handler");
> >     return NGX_ERROR;
> >   }
> >
> >   *cptr = ngx_http_request_handler;
> >   ngx_log_error(NGX_LOG_INFO, cf->log, 0, "[init] Installed request
> > handler");
> >
> >   return NGX_OK;
> > }
> >
> >
> >

> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx-devel


-- 
Roman Arutyunyan


More information about the nginx-devel mailing list