cnginxnginx-reverse-proxynginx-module

Nginx module writing: how to forward request to server?


I'm using nginx as a reverse proxy and I have been trying to write an nginx module that would process incoming requests, and if it likes certain HTTP headers present in the request, nginx would allow the request to reach the protected server (behind the nginx proxy). Now, I've successfully implemented the header processing, but I am stuck at figuring out how to forward the request to the server.

So far, I have looked into sub-requests but none of the code I try (or copied from existing modules such as ngx_http_addition_filter_module!) seems to work. Either I get stuck in a loop where 100+ sub-requests get fired or nothing happens at all. The code I have been trying to use:

static ngx_int_t ngx_http_my_own_handler(ngx_http_request_t *r)
{
    // some request processing here
    // ...


    // now issue the sub-request
    ngx_http_request_t *sr;
    ngx_http_post_subrequest_t *ps;

    ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (ps == NULL) {
        return NGX_ERROR;
    }

    ps->handler = ngx_http_foo_subrequest_done;
    ps->data = "foo";

    // re-use the request URI to try to forward it
    return ngx_http_subrequest(r, &r->uri, &r->args, &sr, ps, NGX_HTTP_SUBREQUEST_CLONE);
}

And the ngx_http_foo_subrequest_done handler looks like this:

ngx_int_t ngx_http_foo_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
    char *msg = (char *) data;
    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "done subrequest r:%p msg:%s rc:%i", r, msg, rc);
    return rc;
}

Please advise what I am doing wrong!


Solution

  • The proxying doesn't work as you would expect... I was surprised too!

    The URI needs to be changed in a string that corresponds to a location /... in your configuration file. Then the proxy_... definitions will include the real full destination.

    Since the path is transform in a variable, you can include the domain name. So for example, your URI could be:

    http://example.com/images/bunny.png
    

    In your module, transform that in a path such as:

    /example.com/images/bunny.png
    

    Then in your nginx.conf, include a location:

    location /example.com {
        proxy_pass http://example.com;
    }
    

    As I mentioned, you can make the example.com part a variable and use it in your proxy_pass, which can be very useful if you have many destination domains. For just 1 to 5, it's probably easier to handle each one with its own location definition.