• Home   /  
  • Archive by category "1"

Lua Socket Non Blocking Assignment

Introduction

The various , and configuration directives serve as gateways to the Lua API within the file. The Nginx Lua API described below can only be called within the user Lua code run in the context of these configuration directives.

The API is exposed to Lua in the form of two standard packages and . These packages are in the default global scope within ngx_lua and are always available within ngx_lua directives.

The packages can be introduced into external Lua modules like this:

Use of the package.seeall flag is strongly discouraged due to its various bad side-effects.

It is also possible to directly require the packages in external Lua modules:

The ability to require these packages was introduced in the release.

Network I/O operations in user code should only be done through the Nginx Lua API calls as the Nginx event loop may be blocked and performance drop off dramatically otherwise. Disk operations with relatively small amount of data can be done using the standard Lua library but huge file reading and writing should be avoided wherever possible as they may block the Nginx process significantly. Delegating all network and disk I/O operations to Nginx's subrequests (via the ngx.location.capture method and similar) is strongly recommended for maximum performance.

Back to TOC

ngx.arg

syntax:val = ngx.arg[index]

context:set_by_lua*, body_filter_by_lua*

When this is used in the context of the set_by_lua* directives, this table is read-only and holds the input arguments to the config directives:

Here is an example

that writes out , the sum of and .

When this table is used in the context of body_filter_by_lua*, the first element holds the input data chunk to the output filter code and the second element holds the boolean flag for the "eof" flag indicating the end of the whole output data stream.

The data chunk and "eof" flag passed to the downstream Nginx output filters can also be overridden by assigning values directly to the corresponding table elements. When setting or an empty Lua string value to , no data chunk will be passed to the downstream Nginx output filters at all.

Back to TOC

ngx.var.VARIABLE

syntax:ngx.var.VAR_NAME

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*

Read and write Nginx variable values.

Note that only already defined nginx variables can be written to. For example:

That is, nginx variables cannot be created on-the-fly.

Some special nginx variables like and can be assigned a value, many others are not, like , , and .

Nginx regex group capturing variables , , , and etc, can be read by this interface as well, by writing , , , and etc.

Setting to a value will unset the Nginx variable.

WARNING When reading from an Nginx variable, Nginx will allocate memory in the per-request memory pool which is freed only at request termination. So when you need to read from an Nginx variable repeatedly in your Lua code, cache the Nginx variable value to your own Lua variable, for example,

to prevent (temporary) memory leaking within the current request's lifetime. Another way of caching the result is to use the ngx.ctx table.

Undefined NGINX variables are evaluated to while uninitialized (but defined) NGINX variables are evaluated to an empty Lua string.

This API requires a relatively expensive metamethod call and it is recommended to avoid using it on hot code paths.

Back to TOC

Core constants

context:init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, *log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*

Note that only three of these constants are utilized by the Nginx API for Lua (i.e., ngx.exit accepts , , and as input).

The constant is a light userdata usually used to represent nil values in Lua tables etc and is similar to the lua-cjson library's constant. This constant was first introduced in the release.

The constant was first introduced in the release.

Back to TOC

HTTP method constants

context:init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*

These constants are usually used in ngx.location.capture and ngx.location.capture_multi method calls.

Back to TOC

HTTP status constants

context:init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*

Back to TOC

Nginx log level constants

context:init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*

These constants are usually used by the ngx.log method.

Back to TOC

print

syntax:print(...)

context:init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*

Writes argument values into the nginx file with the log level.

It is equivalent to

Lua arguments are accepted and result in literal strings while Lua booleans result in literal or strings. And the constant will yield the string output.

There is a hard coded byte limitation on error message lengths in the Nginx core. This limit includes trailing newlines and leading time stamps. If the message size exceeds this limit, Nginx will truncate the message text accordingly. This limit can be manually modified by editing the macro definition in the file in the Nginx source tree.

Back to TOC

ngx.ctx

context:init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*

This table can be used to store per-request Lua context data and has a life time identical to the current request (as with the Nginx variables).

Consider the following example,

Then will yield the output

That is, the entry persists across the rewrite, access, and content phases of a request.

Every request, including subrequests, has its own copy of the table. For example:

Then will give the output

Here, modification of the entry in the subrequest does not affect the one in the parent request. This is because they have two separate versions of .

Internal redirection will destroy the original request data (if any) and the new request will have an empty table. For instance,

Then will give

rather than the original value.

Arbitrary data values, including Lua closures and nested tables, can be inserted into this "magic" table. It also allows the registration of custom meta methods.

Overriding with a new Lua table is also supported, for example,

When being used in the context of init_worker_by_lua*, this table just has the same lifetime of the current Lua handler.

The lookup requires relatively expensive metamethod calls and it is much slower than explicitly passing per-request data along by your own function arguments. So do not abuse this API for saving your own function arguments because it usually has quite some performance impact.

Because of the metamethod magic, never "local" the table outside your Lua function scope on the Lua module level level due to worker-level data sharing. For example, the following is bad:

Use the following instead:

That is, let the caller pass the table explicitly via a function argument.

Back to TOC

ngx.location.capture

syntax:res = ngx.location.capture(uri, options?)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Issues a synchronous but still non-blocking Nginx Subrequest using .

Nginx's subrequests provide a powerful way to make non-blocking internal requests to other locations configured with disk file directory or any other nginx C modules like , , , , , and even ngx_lua itself and etc etc etc.

Also note that subrequests just mimic the HTTP interface but there is no extra HTTP/TCP traffic nor IPC involved. Everything works internally, efficiently, on the C level.

Subrequests are completely different from HTTP 301/302 redirection (via ngx.redirect) and internal redirection (via ngx.exec).

You should always read the request body (by either calling ngx.req.read_body or configuring lua_need_request_body on) before initiating a subrequest.

This API function (as well as ngx.location.capture_multi) always buffers the whole response body of the subrequest in memory. Thus, you should use cosockets and streaming processing instead if you have to handle large subrequest responses.

Here is a basic example:

Returns a Lua table with 4 slots: , , , and .

holds the response status code for the subrequest response.

holds all the response headers of the subrequest and it is a normal Lua table. For multi-value response headers, the value is a Lua (array) table that holds all the values in the order that they appear. For instance, if the subrequest response headers contain the following lines:

Then will be evaluated to the table value .

holds the subrequest's response body data, which might be truncated. You always need to check the boolean flag to see if contains truncated data. The data truncation here can only be caused by those unrecoverable errors in your subrequests like the cases that the remote end aborts the connection prematurely in the middle of the response body data stream or a read timeout happens when your subrequest is receiving the response body data from the remote.

URI query strings can be concatenated to URI itself, for instance,

Named locations like are not allowed due to a limitation in the nginx core. Use normal locations combined with the directive to prepare internal-only locations.

An optional option table can be fed as the second argument, which supports the options:

  • specify the subrequest's request method, which only accepts constants like .
  • specify the subrequest's request body (string value only).
  • specify the subrequest's URI query arguments (both string value and Lua tables are accepted)
  • specify a Lua table to be the ngx.ctx table for the subrequest. It can be the current request's ngx.ctx table, which effectively makes the parent and its subrequest to share exactly the same context table. This option was first introduced in the release.
  • take a Lua table which holds the values to set the specified Nginx variables in the subrequest as this option's value. This option was first introduced in the release.
  • specify whether to copy over all the Nginx variable values of the current request to the subrequest in question. modifications of the nginx variables in the subrequest will not affect the current (parent) request. This option was first introduced in the release.
  • specify whether to share all the Nginx variables of the subrequest with the current (parent) request. modifications of the Nginx variables in the subrequest will affect the current (parent) request. Enabling this option may lead to hard-to-debug issues due to bad side-effects and is considered bad and harmful. Only enable this option when you completely know what you are doing.
  • when set to true, the current (parent) request's request body will always be forwarded to the subrequest being created if the option is not specified. The request body read by either ngx.req.read_body() or lua_need_request_body on will be directly forwarded to the subrequest without copying the whole request body data when creating the subrequest (no matter the request body data is buffered in memory buffers or temporary files). By default, this option is and when the option is not specified, the request body of the current (parent) request is only forwarded when the subrequest takes the or request method.

Issuing a POST subrequest, for example, can be done as follows

See HTTP method constants methods other than POST. The option is by default.

The option can specify extra URI arguments, for instance,

is equivalent to

that is, this method will escape argument keys and values according to URI rules and concatenate them together into a complete query string. The format for the Lua table passed as the argument is identical to the format used in the ngx.encode_args method.

The option can also take plain query strings:

This is functionally identical to the previous examples.

The option controls whether to share nginx variables among the current request and its subrequests. If this option is set to , then the current request and associated subrequests will share the same Nginx variable scope. Hence, changes to Nginx variables made by a subrequest will affect the current request.

Care should be taken in using this option as variable scope sharing can have unexpected side effects. The , , or options are generally preferable instead.

This option is set to by default

Accessing location gives

The option provides a copy of the parent request's Nginx variables to subrequests when such subrequests are issued. Changes made to these variables by such subrequests will not affect the parent request or any other subrequests sharing the parent request's variables.

Request will give the output

Note that if both and are set to true, then takes precedence.

In addition to the two settings above, it is possible to specify values for variables in the subrequest using the option. These variables are set after the sharing or copying of variables has been evaluated, and provides a more efficient method of passing specific values to a subrequest over encoding them as URL arguments and unescaping them in the Nginx config file.

Accessing will yield the output

The option can be used to specify a custom Lua table to serve as the ngx.ctx table for the subrequest.

Then request gives

It is also possible to use this option to share the same ngx.ctx table between the current (parent) request and the subrequest:

Request yields the output

Note that subrequests issued by ngx.location.capture inherit all the request headers of the current request by default and that this may have unexpected side effects on the subrequest responses. For example, when using the standard module to serve subrequests, an "Accept-Encoding: gzip" header in the main request may result in gzipped responses that cannot be handled properly in Lua code. Original request headers should be ignored by setting proxy_pass_request_headers to in subrequest locations.

When the option is not specified and the option is false (the default value), the and subrequests will inherit the request bodies of the parent request (if any).

There is a hard-coded upper limit on the number of concurrent subrequests possible for every main request. In older versions of Nginx, the limit was concurrent subrequests and in more recent versions, Nginx onwards, this was increased to concurrent subrequests. When this limit is exceeded, the following error message is added to the file:

The limit can be manually modified if required by editing the definition of the macro in the file in the Nginx source tree.

Please also refer to restrictions on capturing locations configured by subrequest directives of other modules.

Back to TOC

ngx.location.capture_multi

syntax:res1, res2, ... = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, ... })

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Just like ngx.location.capture, but supports multiple subrequests running in parallel.

This function issues several parallel subrequests specified by the input table and returns their results in the same order. For example,

This function will not return until all the subrequests terminate. The total latency is the longest latency of the individual subrequests rather than the sum.

Lua tables can be used for both requests and responses when the number of subrequests to be issued is not known in advance:

The ngx.location.capture function is just a special form of this function. Logically speaking, the ngx.location.capture can be implemented like this

Please also refer to restrictions on capturing locations configured by subrequest directives of other modules.

Back to TOC

ngx.status

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*

Read and write the current request's response status. This should be called before sending out the response headers.

Setting after the response header is sent out has no effect but leaving an error message in your nginx's error log file:

Back to TOC

syntax:ngx.header.HEADER = VALUE

syntax:value = ngx.header.HEADER

context:rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*

Set, add to, or clear the current request's response header that is to be sent.

Underscores () in the header names will be replaced by hyphens () by default. This transformation can be turned off via the lua_transform_underscores_in_response_headers directive.

The header names are matched case-insensitively.

Multi-value headers can be set this way:

will yield

in the response headers.

Only Lua tables are accepted (Only the last element in the table will take effect for standard headers such as that only accept a single value).

is equivalent to

Setting a slot to effectively removes it from the response headers:

The same applies to assigning an empty table:

Setting after sending out response headers (either explicitly with ngx.send_headers or implicitly with ngx.print and similar) will throw out a Lua exception.

Reading will return the value of the response header named .

Underscores () in the header names will also be replaced by dashes () and the header names will be matched case-insensitively. If the response header is not present at all, will be returned.

This is particularly useful in the context of header_filter_by_lua*, for example,

For multi-value headers, all of the values of header will be collected in order and returned as a Lua table. For example, response headers

will result in

to be returned when reading .

Note that is not a normal Lua table and as such, it is not possible to iterate through it using the Lua function.

For reading request headers, use the ngx.req.get_headers function instead.

Back to TOC

syntax:headers = ngx.resp.get_headers(max_headers?, raw?)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*

Returns a Lua table holding all the current response headers for the current request.

This function has the same signature as ngx.req.get_headers except getting response headers instead of request headers.

This API was first introduced in the release.

Back to TOC

ngx.req.is_internal

syntax:is_internal = ngx.req.is_internal()

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*

Returns a boolean indicating whether the current request is an "internal request", i.e., a request initiated from inside the current nginx server instead of from the client side.

Subrequests are all internal requests and so are requests after internal redirects.

This API was first introduced in the release.

Back to TOC

ngx.req.start_time

syntax:secs = ngx.req.start_time()

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*

Returns a floating-point number representing the timestamp (including milliseconds as the decimal part) when the current request was created.

The following example emulates the variable value (provided by ngx_http_log_module) in pure Lua:

This function was first introduced in the release.

See also ngx.now and ngx.update_time.

Back to TOC

ngx.req.http_version

syntax:num = ngx.req.http_version()

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*

Returns the HTTP version number for the current request as a Lua number.

Current possible values are 2.0, 1.0, 1.1, and 0.9. Returns for unrecognized values.

This method was first introduced in the release.

Back to TOC

syntax:str = ngx.req.raw_header(no_request_line?)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*

Returns the original raw HTTP protocol header received by the Nginx server.

By default, the request line and trailing terminator will also be included. For example,

gives something like this:

You can specify the optional argument as a value to exclude the request line from the result. For example,

outputs something like this:

This method was first introduced in the release.

This method does not work in HTTP/2 requests yet.

Back to TOC

ngx.req.get_method

syntax:method_name = ngx.req.get_method()

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, balancer_by_lua*

Retrieves the current request's request method name. Strings like and are returned instead of numerical method constants.

If the current request is an Nginx subrequest, then the subrequest's method name will be returned.

This method was first introduced in the release.

See also ngx.req.set_method.

Back to TOC

ngx.req.set_method

syntax:ngx.req.set_method(method_id)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*

Overrides the current request's request method with the argument. Currently only numerical method constants are supported, like and .

If the current request is an Nginx subrequest, then the subrequest's method will be overridden.

This method was first introduced in the release.

See also ngx.req.get_method.

Back to TOC

ngx.req.set_uri

syntax:ngx.req.set_uri(uri, jump?)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*

Rewrite the current request's (parsed) URI by the argument. The argument must be a Lua string and cannot be of zero length, or a Lua exception will be thrown.

The optional boolean argument can trigger location rematch (or location jump) as ngx_http_rewrite_module's rewrite directive, that is, when is (default to ), this function will never return and it will tell Nginx to try re-searching locations with the new URI value at the later phase and jumping to the new location.

Location jump will not be triggered otherwise, and only the current request's URI will be modified, which is also the default behavior. This function will return but with no returned values when the argument is or absent altogether.

For example, the following nginx config snippet

can be coded in Lua like this:

Similarly, Nginx config

can be coded in Lua as

or equivalently,

The argument can only be set to in rewrite_by_lua*. Use of jump in other contexts is prohibited and will throw out a Lua exception.

A more sophisticated example involving regex substitutions is as follows

which is functionally equivalent to

Note that it is not possible to use this interface to rewrite URI arguments and that ngx.req.set_uri_args should be used for this instead. For instance, Nginx config

can be coded as

or

This interface was first introduced in the release.

Back to TOC

ngx.req.set_uri_args

syntax:ngx.req.set_uri_args(args)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*

Rewrite the current request's URI query arguments by the argument. The argument can be either a Lua string, as in

or a Lua table holding the query arguments' key-value pairs, as in

where in the latter case, this method will escape argument keys and values according to the URI escaping rule.

Multi-value arguments are also supported:

which will result in a query string like .

This interface was first introduced in the release.

See also ngx.req.set_uri.

Back to TOC

ngx.req.get_uri_args

syntax:args = ngx.req.get_uri_args(max_args?)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*

Returns a Lua table holding all the current request URL query arguments.

Then will yield the response body

Multiple occurrences of an argument key will result in a table value holding all the values for that key in order.

Keys and values are unescaped according to URI escaping rules. In the settings above, will yield:

Arguments without the parts are treated as boolean arguments. will yield:

That is, they will take Lua boolean values . However, they are different from arguments taking empty string values. will give something like

Empty key arguments are discarded. will yield an empty output for instance.

Updating query arguments via the nginx variable (or in Lua) at runtime is also supported:

Here the table will always look like

regardless of the actual request query string.

Note that a maximum of 100 request arguments are parsed by default (including those with the same name) and that additional request arguments are silently discarded to guard against potential denial of service attacks.

However, the optional function argument can be used to override this limit:

This argument can be set to zero to remove the limit and to process all request arguments received:

Removing the cap is strongly discouraged.

Back to TOC

ngx.req.get_post_args

syntax:args, err = ngx.req.get_post_args(max_args?)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*

Returns a Lua table holding all the current request POST query arguments (of the MIME type ). Call ngx.req.read_body to read the request body first or turn on the lua_need_request_body directive to avoid errors.

Then

will yield the response body like

Multiple occurrences of an argument key will result in a table value holding all of the values for that key in order.

Keys and values will be unescaped according to URI escaping rules.

With the settings above,

will yield:

Arguments without the parts are treated as boolean arguments. with the request body will yield:

That is, they will take Lua boolean values . However, they are different from arguments taking empty string values. with request body will return something like

Empty key arguments are discarded. with body will yield empty outputs for instance.

Note that a maximum of 100 request arguments are parsed by default (including those with the same name) and that additional request arguments are silently discarded to guard against potential denial of service attacks.

However, the optional function argument can be used to override this limit:

This argument can be set to zero to remove the limit and to process all request arguments received:

Removing the cap is strongly discouraged.

Back to TOC

syntax:headers = ngx.req.get_headers(max_headers?, raw?)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*

Returns a Lua table holding all the current request headers.

To read an individual header:

Note that the ngx.var.HEADER API call, which uses core $http_HEADER variables, may be more preferable for reading individual request headers.

For multiple instances of request headers such as:

the value of will be a Lua (array) table such as:

Note that a maximum of 100 request headers are parsed by default (including those with the same name) and that additional request headers are silently discarded to guard against potential denial of service attacks.

However, the optional function argument can be used to override this limit:

This argument can be set to zero to remove the limit and to process all request headers received:

Removing the cap is strongly discouraged.

Since the release, all the header names in the Lua table returned are converted to the pure lower-case form by default, unless the argument is set to (default to ).

Also, by default, an metamethod is added to the resulting Lua table and will normalize the keys to a pure lowercase form with all underscores converted to dashes in case of a lookup miss. For example, if a request header is present, then the following invocations will all pick up the value of this header correctly:

The metamethod will not be added when the argument is set to .

Back to TOC

syntax:ngx.req.set_header(header_name, header_value)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*

Set the current request's request header named to value , overriding any existing ones.

By default, all the subrequests subsequently initiated by ngx.location.capture and ngx.location.capture_multi will inherit the new header.

Here is an example of setting the header:

The can take an array list of values, for example,

will produce two new request headers:

and old headers will be overridden if there is any.

When the argument is , the request header will be removed. So

is equivalent to

Back to TOC

syntax:ngx.req.clear_header(header_name)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*

Clears the current request's request header named . None of the current request's existing subrequests will be affected but subsequently initiated subrequests will inherit the change by default.

Back to TOC

ngx.req.read_body

syntax:ngx.req.read_body()

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Reads the client request body synchronously without blocking the Nginx event loop.

If the request body is already read previously by turning on lua_need_request_body or by using other modules, then this function does not run and returns immediately.

If the request body has already been explicitly discarded, either by the ngx.req.discard_body function or other modules, this function does not run and returns immediately.

In case of errors, such as connection errors while reading the data, this method will throw out a Lua exception or terminate the current request with a 500 status code immediately.

The request body data read using this function can be retrieved later via ngx.req.get_body_data or, alternatively, the temporary file name for the body data cached to disk using ngx.req.get_body_file. This depends on

  1. whether the current request body is already larger than the client_body_buffer_size,
  2. and whether client_body_in_file_only has been switched on.

In cases where current request may have a request body and the request body data is not required, The ngx.req.discard_body function must be used to explicitly discard the request body to avoid breaking things under HTTP 1.1 keepalive or HTTP 1.1 pipelining.

This function was first introduced in the release.

Back to TOC

ngx.req.discard_body

syntax:ngx.req.discard_body()

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Explicitly discard the request body, i.e., read the data on the connection and throw it away immediately (without using the request body by any means).

This function is an asynchronous call and returns immediately.

If the request body has already been read, this function does nothing and returns immediately.

This function was first introduced in the release.

See also ngx.req.read_body.

Back to TOC

ngx.req.get_body_data

syntax:data = ngx.req.get_body_data()

context:rewrite_by_lua*, access_by_lua*, content_by_lua*, log_by_lua*

Retrieves in-memory request body data. It returns a Lua string rather than a Lua table holding all the parsed query arguments. Use the ngx.req.get_post_args function instead if a Lua table is required.

This function returns if

  1. the request body has not been read,
  2. the request body has been read into disk temporary files,
  3. or the request body has zero size.

If the request body has not been read yet, call ngx.req.read_body first (or turned on lua_need_request_body to force this module to read the request body. This is not recommended however).

If the request body has been read into disk files, try calling the ngx.req.get_body_file function instead.

To force in-memory request bodies, try setting client_body_buffer_size to the same size value in client_max_body_size.

Note that calling this function instead of using or is more efficient because it can save one dynamic memory allocation and one data copy.

This function was first introduced in the release.

See also ngx.req.get_body_file.

Back to TOC

ngx.req.get_body_file

syntax:file_name = ngx.req.get_body_file()

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Retrieves the file name for the in-file request body data. Returns if the request body has not been read or has been read into memory.

The returned file is read only and is usually cleaned up by Nginx's memory pool. It should not be manually modified, renamed, or removed in Lua code.

If the request body has not been read yet, call ngx.req.read_body first (or turned on lua_need_request_body to force this module to read the request body. This is not recommended however).

If the request body has been read into memory, try calling the ngx.req.get_body_data function instead.

To force in-file request bodies, try turning on client_body_in_file_only.

This function was first introduced in the release.

See also ngx.req.get_body_data.

Back to TOC

ngx.req.set_body_data

syntax:ngx.req.set_body_data(data)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Set the current request's request body using the in-memory data specified by the argument.

If the current request's request body has not been read, then it will be properly discarded. When the current request's request body has been read into memory or buffered into a disk file, then the old request body's memory will be freed or the disk file will be cleaned up immediately, respectively.

This function was first introduced in the release.

See also ngx.req.set_body_file.

Back to TOC

ngx.req.set_body_file

syntax:ngx.req.set_body_file(file_name, auto_clean?)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Set the current request's request body using the in-file data specified by the argument.

If the optional argument is given a value, then this file will be removed at request completion or the next time this function or ngx.req.set_body_data are called in the same request. The is default to .

Please ensure that the file specified by the argument exists and is readable by an Nginx worker process by setting its permission properly to avoid Lua exception errors.

If the current request's request body has not been read, then it will be properly discarded. When the current request's request body has been read into memory or buffered into a disk file, then the old request body's memory will be freed or the disk file will be cleaned up immediately, respectively.

This function was first introduced in the release.

See also ngx.req.set_body_data.

Back to TOC

ngx.req.init_body

syntax:ngx.req.init_body(buffer_size?)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*

Creates a new blank request body for the current request and inializes the buffer for later request body data writing via the ngx.req.append_body and ngx.req.finish_body APIs.

If the argument is specified, then its value will be used for the size of the memory buffer for body writing with ngx.req.append_body. If the argument is omitted, then the value specified by the standard client_body_buffer_size directive will be used instead.

When the data can no longer be hold in the memory buffer for the request body, then the data will be flushed onto a temporary file just like the standard request body reader in the Nginx core.

It is important to always call the ngx.req.finish_body after all the data has been appended onto the current request body. Also, when this function is used together with ngx.req.socket, it is required to call ngx.req.socketbefore this function, or you will get the "request body already exists" error message.

The usage of this function is often like this:

This function can be used with ngx.req.append_body, ngx.req.finish_body, and ngx.req.socket to implement efficient input filters in pure Lua (in the context of rewrite_by_lua* or access_by_lua*), which can be used with other Nginx content handler or upstream modules like ngx_http_proxy_module and ngx_http_fastcgi_module.

This function was first introduced in the release.

Back to TOC

ngx.req.append_body

syntax:ngx.req.append_body(data_chunk)

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*

Append new data chunk specified by the argument onto the existing request body created by the ngx.req.init_body call.

When the data can no longer be hold in the memory buffer for the request body, then the data will be flushed onto a temporary file just like the standard request body reader in the Nginx core.

It is important to always call the ngx.req.finish_body after all the data has been appended onto the current request body.

This function can be used with ngx.req.init_body, ngx.req.finish_body, and ngx.req.socket to implement efficient input filters in pure Lua (in the context of rewrite_by_lua* or access_by_lua*), which can be used with other Nginx content handler or upstream modules like ngx_http_proxy_module and ngx_http_fastcgi_module.

This function was first introduced in the release.

See also ngx.req.init_body.

Back to TOC

ngx.req.finish_body

syntax:ngx.req.finish_body()

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*

Completes the construction process of the new request body created by the ngx.req.init_body and ngx.req.append_body calls.

This function can be used with ngx.req.init_body, ngx.req.append_body, and ngx.req.socket to implement efficient input filters in pure Lua (in the context of rewrite_by_lua* or access_by_lua*), which can be used with other Nginx content handler or upstream modules like ngx_http_proxy_module and ngx_http_fastcgi_module.

This function was first introduced in the release.

See also ngx.req.init_body.

Back to TOC

ngx.req.socket

syntax:tcpsock, err = ngx.req.socket()

syntax:tcpsock, err = ngx.req.socket(raw)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Returns a read-only cosocket object that wraps the downstream connection. Only receive and receiveuntil methods are supported on this object.

In case of error, will be returned as well as a string describing the error.

The socket object returned by this method is usually used to read the current request's body in a streaming fashion. Do not turn on the lua_need_request_body directive, and do not mix this call with ngx.req.read_body and ngx.req.discard_body.

If any request body data has been pre-read into the Nginx core request header buffer, the resulting cosocket object will take care of this to avoid potential data loss resulting from such pre-reading. Chunked request bodies are not yet supported in this API.

Since the release, this function accepts an optional boolean argument. When this argument is , this function returns a full-duplex cosocket object wrapping around the raw downstream connection socket, upon which you can call the receive, receiveuntil, and send methods.

When the argument is , it is required that no pending data from any previous ngx.say, ngx.print, or ngx.send_headers calls exists. So if you have these downstream output calls previously, you should call ngx.flush(true) before calling to ensure that there is no pending output data. If the request body has not been read yet, then this "raw socket" can also be used to read the request body.

You can use the "raw request socket" returned by to implement fancy protocols like WebSocket, or just emit your own raw HTTP response header or body data. You can refer to the lua-resty-websocket library for a real world example.

This function was first introduced in the release.

Back to TOC

ngx.exec

syntax:ngx.exec(uri, args?)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Does an internal redirect to with and is similar to the echo_exec directive of the echo-nginx-module.

The optional second can be used to specify extra URI query arguments, for example:

Alternatively, a Lua table can be passed for the argument for ngx_lua to carry out URI escaping and string concatenation.

The result is exactly the same as the previous example.

The format for the Lua table passed as the argument is identical to the format used in the ngx.encode_args method.

Named locations are also supported but the second argument will be ignored if present and the querystring for the new target is inherited from the referring location (if any).

will return "hello" and not "goodbye" in the example below

Note that the method is different from ngx.redirect in that it is purely an internal redirect and that no new external HTTP traffic is involved.

Also note that this method call terminates the processing of the current request and that it must be called before ngx.send_headers or explicit response body outputs by either ngx.print or ngx.say.

It is recommended that a coding style that combines this method call with the statement, i.e., be adopted when this method call is used in contexts other than header_filter_by_lua* to reinforce the fact that the request processing is being terminated.

Back to TOC

ngx.redirect

syntax:ngx.redirect(uri, status?)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Issue an or redirection to .

The optional parameter specifies the HTTP status code to be used. The following status codes are supported right now:

It is () by default.

Here is an example assuming the current server name is and that it is listening on port 1984:

which is equivalent to

Redirecting arbitrary external URLs is also supported, for example:

We can also use the numerical code directly as the second argument:

This method is similar to the rewrite directive with the modifier in the standard ngx_http_rewrite_module, for example, this snippet

is equivalent to the following Lua code

while

is equivalent to

URI arguments can be specified as well, for example:

Note that this method call terminates the processing of the current request and that it must be called before ngx.send_headers or explicit response body outputs by either ngx.print or ngx.say.

It is recommended that a coding style that combines this method call with the statement, i.e., be adopted when this method call is used in contexts other than header_filter_by_lua* to reinforce the fact that the request processing is being terminated.

Back to TOC

syntax:ok, err = ngx.send_headers()

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Explicitly send out the response headers.

Since this function returns on success, or returns and a string describing the error otherwise.

Note that there is normally no need to manually send out response headers as ngx_lua will automatically send headers out before content is output with ngx.say or ngx.print or when content_by_lua* exits normally.

Back to TOC

syntax:value = ngx.headers_sent

context:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*

Returns if the response headers have been sent (by ngx_lua), and otherwise.

This API was first introduced in ngx_lua v0.3.1rc6.

Back to TOC

ngx.print

syntax:ok, err = ngx.print(...)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Emits arguments concatenated to the HTTP client (as response body). If response headers have not been sent, this function will send headers out first and then output body data.

Since this function returns on success, or returns and a string describing the error otherwise.

Lua values will output strings and Lua boolean values will output and literal strings respectively.

Nested arrays of strings are permitted and the elements in the arrays will be sent one by one:

will yield the output

Non-array table arguments will cause a Lua exception to be thrown.

The constant will yield the string output.

This is an asynchronous call and will return immediately without waiting for all the data to be written into the system send buffer. To run in synchronous mode, call after calling

Creating an application¶

Further we walk you through key programming practices that will give you a good start in writing Lua applications for Tarantool. For an adventure, this is a story of implementing… a real microservice based on Tarantool! We implement a backend for a simplified version of Pokémon Go, a location-based augmented reality game released in mid-2016. In this game, players use a mobile device’s GPS capability to locate, capture, battle and train virtual monsters called “pokémon”, who appear on the screen as if they were in the same real-world location as the player.

To stay within the walk-through format, let’s narrow the original gameplay as follows. We have a map with pokémon spawn locations. Next, we have multiple players who can send catch-a-pokémon requests to the server (which runs our Tarantool microservice). The server replies whether the pokémon is caught or not, increases the player’s pokémon counter if yes, and triggers the respawn-a-pokémon method that spawns a new pokémon at the same location in a while.

We leave client-side applications outside the scope of this story. Yet we promise a mini-demo in the end to simulate real users and give us some fun. :-)

First, what would be the best way to deliver our microservice?

Modules, rocks and applications¶

To make our game logic available to other developers and Lua applications, let’s put it into a Lua module.

A module (called “rock” in Lua) is an optional library which enhances Tarantool functionality. So, we can install our logic as a module in Tarantool and use it from any Tarantool application or module. Like applications, modules in Tarantool can be written in Lua (rocks), C or C++.

Modules are good for two things:

  • easier code management (reuse, packaging, versioning), and
  • hot code reload without restarting the Tarantool instance.

Technically, a module is a file with source code that exports its functions in an API. For example, here is a Lua module named that exports one function named :

To launch the function – from another module, from a Lua application, or from Tarantool itself, – we need to save this module as a file, then load this module with the directive and call the exported function.

For example, here’s a Lua application that uses function from module:

A thing to remember here is that the directive takes load paths to Lua modules from the variable. This is a semicolon-separated string, where a question mark is used to interpolate the module name. By default, this variable contains system-wide Lua paths and the working directory. But if we put our modules inside a specific folder (e.g. ), we need to add this folder to before any calls to :

For our microservice, a simple and convenient solution would be to put all methods in a Lua module (say ) and to write a Lua application (say ) that initializes the gaming environment and starts the game loop.

Now let’s get down to implementation details. In our game, we need three entities:

  • map, which is an array of pokémons with coordinates of respawn locations; in this version of the game, let a location be a rectangle identified with two points, upper-left and lower-right;
  • player, which has an ID, a name, and coordinates of the player’s location point;
  • pokémon, which has the same fields as the player, plus a status (active/inactive, that is present on the map or not) and a catch probability (well, let’s give our pokémons a chance to escape :-) )

We’ll store these entities as tuples in Tarantool spaces. But to deliver our backend application as a microservice, the good practice would be to send/receive our data in the universal JSON format, thus using Tarantool as a document storage.

localexports={}exports.myfun=function(input_string)print('Hello',input_string)endreturnexports
-- loading the modulelocalmymodule=require('mymodule')-- calling myfun() from within test() functionlocaltest=function()mymodule.myfun()end
package.path='scripts/?.lua;'..package.path

Avro schemas¶

To store JSON data as tuples, we will apply a savvy practice which reduces data footprint and ensures all stored documents are valid. We will use Tarantool module avro-schema which checks the schema of a JSON document and converts it to a Tarantool tuple. The tuple will contain only field values, and thus take a lot less space than the original document. In avro-schema terms, converting JSON documents to tuples is “flattening”, and restoring the original documents is “unflattening”. The usage is quite straightforward:

  1. For each entity, we need to define a schema in Apache Avro schema syntax, where we list the entity’s fields with their names and Avro data types.
  2. At initialization, we call that creates objects in memory for all schema entities, and that generates flatten/unflatten methods for each entity.
  3. Further on, we just call flatten/unflatten methods for a respective entity on receiving/sending the entity’s data.

Here’s what our schema definitions for the player and pokémon entities look like:

And here’s how we create and compile our entities at initialization:

As for the map entity, it would be an overkill to introduce a schema for it, because we have only one map in the game, it has very few fields, and – which is most important – we use the map only inside our logic, never exposing it to external users.

Next, we need methods to implement the game logic. To simulate object-oriented programming in our Lua code, let’s store all Lua functions and shared variables in a single local variable (let’s name it as ). This will allow us to address functions or variables from within our module as or . Like this:

In OOP terms, we can now regard local variables inside as object fields, and local functions as object methods.

Note

In this manual, Lua examples use local variables. Use global variables with caution, since the module’s users may be unaware of them.

To enable/disable the use of undeclared global variables in your Lua code, use Tarantool’s strict module.

So, our game module will have the following methods:

  • to calculate whether the pokémon was caught (besides the coordinates of both the player and pokémon, this method will apply a probability factor, so not every pokémon within the player’s reach will be caught);
  • to add missing pokémons to the map, say, every 60 seconds (we assume that a frightened pokémon runs away, so we remove a pokémon from the map on any catch attempt and add it back to the map in a while);
  • to log information about caught pokémons (like “Player 1 caught pokémon A”);
  • to initialize the game (it will create database spaces, create and compile avro schemas, and launch ).

Besides, it would be convenient to have methods for working with Tarantool storage. For example:

  • to add a pokémon to the database, and
  • to populate the map with all pokémons stored in Tarantool.

We’ll need these two methods primarily when initializing our game, but we can also call them later, for example to test our code.

localschema={player={type="record",name="player_schema",fields={{name="id",type="long"},{name="name",type="string"},{name="location",type={type="record",name="player_location",fields={{name="x",type="double"},{name="y",type="double"}}}}}},pokemon={type="record",name="pokemon_schema",fields={{name="id",type="long"},{name="status",type="string"},{name="name",type="string"},{name="chance",type="double"},{name="location",type={type="record",name="pokemon_location",fields={{name="x",type="double"},{name="y",type="double"}}}}}}}
-- load avro-schema module with require()localavro=require('avro_schema')-- create modelslocalok_m,pokemon=avro.create(schema.pokemon)localok_p,player=avro.create(schema.player)ifok_mandok_pthen-- compile modelslocalok_cm,compiled_pokemon=avro.compile(pokemon)localok_cp,compiled_player=avro.compile(player)ifok_cmandok_cpthen-- start the game<...>elselog.error('Schema compilation failed')endelselog.info('Schema creation failed')endreturnfalse
localgame={-- a local variablenum_players=0,-- a method that prints a local variablehello=function(self)print('Hello! Your player number is '..self.num_players..'.')end,-- a method that calls another method and returns a local variablesign_in=function(self)self.num_players=self.num_players+1self:hello()returnself.num_playersend}

Bootstrapping a database¶

Let’s discuss game initialization. In

One thought on “Lua Socket Non Blocking Assignment

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *