0

I am trying to write a nginx module which will filter out GET requests for any resource whose size is greater than say 1000KB. I followed the tutorials, the module compiles and installs fine. However I think the module is not getting invoked on an incoming request. The module consists of header and body filters which will check the outgoing requests content size, if greater than 1000KB it will clear the response body and redirect to a different port which I will be throttling in firewall. I used some logging statements but I cannot see any thing in logs because of which I think its not getting invoked. I am attaching full source

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>




typedef struct {
    ngx_str_t     download_filter;

} ngx_http_download_conf_t;


static void *ngx_http_download_create_conf(ngx_conf_t *cf);
static ngx_int_t ngx_http_download_filter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_download_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_download_body_filter(ngx_http_request_t *r, ngx_chain_t *in);




//static char *ngx_http_download_module( ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//static u_char ngx_download_filter_module[] = "download filter";

static ngx_command_t ngx_download_filter_module_commands[] = {
  {
    ngx_string("download_filter"),
    NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
    ngx_conf_set_str_slot,
    NGX_HTTP_LOC_CONF_OFFSET,
    offsetof(ngx_http_download_conf_t, download_filter),
    NULL },
    ngx_null_command
};

static ngx_http_module_t  ngx_http_download_filter_module_ctx = {
  NULL,                            /* preconfiguration */
  ngx_http_download_filter_init,   /* postconfiguration */

  NULL,                           /* create main configuration */
  NULL,                           /* init main configuration */

  NULL,                           /* create server configuration */
  NULL,                           /* merge server configuration */

  ngx_http_download_create_conf,  /* create location configuration */
  NULL                            /* merge location configuration */
};


ngx_module_t ngx_http_download_filter_module = {
  NGX_MODULE_V1,
  &ngx_http_download_filter_module_ctx, /* module context */
  ngx_download_filter_module_commands,                          /* module directives */
  NGX_HTTP_MODULE,               /* module type */
  NULL,                          /* init master */
  NULL,                          /* init module */
  NULL,                          /* init process */
  NULL,                          /* init thread */
  NULL,                          /* exit thread */
  NULL,                          /* exit process */
  NULL,                          /* exit master */
  NGX_MODULE_V1_PADDING
};

static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;



static ngx_int_t
ngx_http_download_filter_init(ngx_conf_t *cf)
{
  printf("hello world: inside init function\n");


  ngx_http_next_header_filter = ngx_http_top_header_filter;
  ngx_http_top_header_filter = ngx_http_download_header_filter;

  ngx_http_next_body_filter = ngx_http_top_body_filter;
  ngx_http_top_body_filter = ngx_http_download_body_filter;

  return NGX_OK;
}

static
ngx_int_t ngx_http_download_header_filter( ngx_http_request_t *r)
{


  ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "inside the header filter");
  printf("inside header filter hello world\n");

  ngx_str_t new_location= ngx_string("http://localhost:5000/flake.jpg");

  if ( r->headers_out.content_length_n > 1000 && r->headers_out.status == NGX_HTTP_OK )
  {
      r->headers_out.status = NGX_HTTP_MOVED_TEMPORARILY;
      r->headers_out.content_type.len = 0;
      r->headers_out.location->value.data = new_location.data;
      r->headers_out.location->value.len = new_location.len;
      ngx_http_clear_content_length(r);
      ngx_http_clear_accept_ranges(r);

  }

 r->headers_out.status = NGX_HTTP_MOVED_TEMPORARILY;


  return ngx_http_next_header_filter(r);
}

static
ngx_int_t ngx_http_download_body_filter ( ngx_http_request_t *r, ngx_chain_t *in )
{

  if ( r->headers_out.content_length_n > 1000 && r->headers_out.status == NGX_HTTP_OK )
  { 
    r->discard_body = 0;
    return ngx_http_next_body_filter(r, in);
  }

  return ngx_http_next_body_filter(r, in);

}

static void *
ngx_http_download_create_conf(ngx_conf_t *cf)
{
    ngx_http_download_conf_t  *conf;
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_download_conf_t));
    if (conf == NULL) {
        return NULL;
    }

    /*
     * set by ngx_pcalloc():
     *
     *     conf->before_body = { 0, NULL };
     *     conf->after_body = { 0, NULL };
     *     conf->types = { NULL };
     *     conf->types_keys = NULL;
     */

    return conf;
}

My nginx conf uses the download_filter directive. Following is a snippet.

http {
    include       mime.types;
    default_type  application/octet-stream;

   log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                     '"$http_user_agent" "$http_x_forwarded_for"';



     access_log  /tmp/log/access.log  main;
       error_log   /tmp/log/error.log info;
        sendfile        on;
        #tcp_nopush     on;

        #keepalive_timeout  0;
        keepalive_timeout  65;


        download_filter;

The module initializes as I see "hello world, inside init function" string when I start nginx.

Anything that I am missing which is stopping my filters? I am not using a handler here as I do not need to serve anything and let nginx do its work and use filters right before it is being served. Do I need a handler? If yes how can I make a handler which just serves the content and let filters do its work.

4
  • What's the exact point of this ? (btw content length is in bytes so here you limit to 1KB). Oct 21, 2014 at 16:29
  • I want to limit the download rate of some large files from my server, say any pdf file greater than 5 MB I want it to serve that request from a diffrent port. On that port I can restrict the download rate through firewall. This will help in preserving bandwidth in a wireless private network. This will also not affect other resource downloads of smaller size. The size in the code is just an example, I am first trying to make it work. Its just like a hello world.
    – dev0z
    Oct 21, 2014 at 16:34
  • Nginx can already do that : read the doc. Nginx is event driven and uses kernel internals to do that so it won't affect processing of other requests (epoll, sendfile, aio) and limit user space interactions to a minimum. Oct 21, 2014 at 16:35
  • Cool, and it can be used in http and server context also which makes it generic for all requests. I knew about limit_rate but never paid attention to limit_rate_after size. Besides this, I am still curious why isn't my filters invoking? Do I still need a handler? Just out of curiosity I am asking this
    – dev0z
    Oct 21, 2014 at 16:46

0

You must log in to answer this question.

Browse other questions tagged .