Matt
Matt

Reputation: 23749

Using Lua in nginx to pass a request to FastCGI

Using nginx compiled with Lua support, how can we make a sort of sub-request to a FastCGI handler, much like nginx's fastcgi_pass directive?

What I'd like to do is something like this:

location = / {
    access_by_lua '
        res = ngx_fastcgi.pass("backend")
    ';
}

(Obviously, this doesn't work.)

I'm pouring over HttpLuaModule where I see mention ngx_fastcgi and ngx.location.capture, which, evidently, makes

non-blocking internal requests to other locations configured with disk file directory or any other nginx C modules like ... ngx_fastcgi, ...

But then following the link of ngx_fastcgi takes me to HttpFastcgiModule which explains only nginx directives, not Lua-scriptable commands. Is ngx.location.capture the right function to use? (These requests, by the way, will be to localhost, just on a different port, like 9000 or 9001.)

How can I use Lua in nginx to forward a request, or make a sub-request, to a FastCGI endpoint?

Upvotes: 2

Views: 3230

Answers (1)

Jonathan Oliver
Jonathan Oliver

Reputation: 5267

Use the ngx.location.capture() method to perform a subrequest to a predefined location block. Then, from within the location block, perform the external, FastCGI request. Because the subrequest itself isn't actually a network operation, but is performed purely within nginx C-based environment, there's very little overhead. Further, because the FastCGI request and other "proxy_pass"-type requests are event-based, nginx can operate as an efficient intermediary.

As an example, you could have the following:

location / {
  access_by_lua '
    response = ngx.location.capture("/my-subrequest-handler")
    if response.status == 404 then
       return ngx.exit(401) -- can't find/authenticate user, refuse request
    end

    ngx.say(response.status)
  ';

  # other nginx config stuff here as necessary--perhaps another fastcgi_pass
  # depending upon the status code of the response above... 
}

location = /my-subrequest-handler {
  internal; # this location block can only be seen by nginx subrequests
  fastcgi_pass localhost:9000; # or some named "upstream"
  fastcgi_pass_request_body       off; # send client request body upstream?
  fastcgi_pass_request_headers    off; # send client request headers upstream?
  fastcgi_connect_timeout         100ms; # optional; control backend timeouts
  fastcgi_send_timeout            100ms; # same
  fastcgi_read_timeout            100ms; # same
  fastcgi_keep_conn               on; # keep request alive
  include                         fastcgi_params;
}

In the above example, even though I'm performing a subrequest to "/my-subrequest-handler", the actual URL passed to the FastCGI process is the one requested by the HTTP client calling into nginx in the first place.

Note that that ngx.location.capture is a synchronous, but non-blocking operation which means that your code execution stops until a response is received, but the nginx worker is free to perform other operations in the meantime.

There are some really cool things that you can do with Lua to modify the request and response at any point in the nginx pipeline. For example, you could change the original request by adding headers, removing headers, even transforming the body. Perhaps the caller wants to work with XML, but the upstream application only understands JSON, we can convert to/from JSON when calling the upstream application.

Lua is not built into nginx by default. Instead it's a 3rd party module that must be compiled in. There's a flavor of nginx called OpenResty that builds in Lua+LuaJIT along with a few other modules that you may or may not need.

Upvotes: 5

Related Questions