lua-resty-core

repository·master·Indexed 21 days ago

https://github.com/openresty/lua-resty-core

A high-performance Lua library providing an FFI-based implementation of the Nginx API for Lua, designed for better speed and LuaJIT compatibility than the standard Lua C API. It includes modules such as ngx.balancer for dynamic upstream load balancing, ngx.base64 for URL-safe encoding, ngx.errlog for capturing and managing Nginx error logs, and ngx.ocsp for OCSP stapling.

Tokens
21.8K
Snippets
62
Records
92
Agent score
69%

What's inside lua-resty-core

  1. Overview of lua-resty-core

    master

    What is lua-resty-core?

    lua-resty-core is a high-performance, FFI-based Lua API for the ngx_http_lua_module and ngx_stream_lua_module. It reimplements parts of the standard Nginx API for Lua using LuaJIT FFI.

    Key Benefits

    • Performance: Because it uses LuaJIT FFI, the API can be JIT-compiled, making it significantly faster than the standard Lua C API used by the default ngx_lua module.
    • Safety and Completeness: Provides a more robust and complete implementation of Nginx APIs.
    • Compatibility: It installs the new FFI-based API into the existing ngx.* and ndk.* namespaces, meaning it acts as a drop-in replacement for many standard functions.

    Usage Note

    Since OpenResty 1.15.8.1, this library is automatically loaded by default. It is highly recommended to use the version bundled with your OpenResty release to avoid compatibility issues.

  2. Control NGINX upstream SSL handshakes with ngx.proxyssl

    master

    The ngx.proxyssl module provides Lua API functions to control the SSL handshake process for upstream connections. It is specifically designed to be used within the proxy_ssl_certificate_by_lua* and proxy_ssl_verify_by_lua* directives of the ngx_lua module.

    To use the module, require it in your Lua code:

    local proxy_ssl = require "ngx.proxyssl"
    server {
        listen 443 ssl;
        server_name   test.com;
    
        proxy_ssl_certificate_by_lua_block {
            local proxy_ssl = require "ngx.proxyssl"
    
            local ver, err = proxy_ssl.get_tls1_version_str()
            if not ver then
                ngx.log(ngx.ERR, "failed to get TLS1 version: ", err)
                return
            end
            ngx.log(ngx.INFO, "got TLS1 version: ", ver)
        }
    
        location / {
            root html;
        }
    }
  3. Use the ngx.req module for HTTP request handling

    master

    The ngx.req module provides a Lua API for handling HTTP requests within OpenResty. All methods in this module are static (module-level), meaning you do not need to instantiate an object to use them; you simply require the module and call the methods directly.

    local ngx_req = require "ngx.req"
  4. Use resty.core.time for monotonic time operations

    master

    The resty.core.time module provides high-performance utility functions for time operations. It uses Nginx's cached time instead of making system calls (unlike Lua's standard os.time or date libraries), making it highly efficient for measuring elapsed time.

    Values returned by monotonic_time() and monotonic_msec() represent the elapsed time since the machine boot, which should correspond to the values found in /proc/uptime.

    location = /t {
        content_by_lua_block {
            local time = require "resty.core.time"
            ngx.say(time.monotonic_time())
            ngx.say(time.monotonic_msec())
        }
    }
  5. Spawn and communicate with OS processes using `ngx.pipe`

    master

    ngx.pipe allows you to spawn OS processes and communicate with them via stdin, stdout, and stderr in a non-blocking fashion. It is production-ready and designed for POSIX-compliant systems.

    Key Characteristics

    • Non-blocking: Uses Nginx's event mechanism and OpenResty's Lua coroutine scheduler to ensure communication does not block the OS thread.
    • Safety: If a process instance is collected by the garbage collector while still alive, it is automatically killed via SIGKILL.
    • Limitations:
      • Does not support non-POSIX systems (like Windows).
      • Communication APIs cannot be used in phases that do not support yielding (e.g., init_worker_by_lua* or log_by_lua*).
      • If not using the OpenResty bundle, you must apply the socket_cloexec patch to standard Nginx core.
    location = /t {
        content_by_lua_block {
            local ngx_pipe = require "ngx.pipe"
            local select = select
    
            local function count_char(...) 
                -- Spawns 'wc -c' to count bytes from stdin
                local proc = ngx_pipe.spawn({'wc', '-c'})
                local n = select('#', ...)
                for i = 1, n do
                    local arg = select(i, ...)
                    local bytes, err = proc:write(arg)
                    if not bytes then
                        ngx.say(err)
                        return
                    end
                end
    
                -- Close stdin so 'wc' can finish processing and output
                local ok, err = proc:shutdown('stdin')
                if not ok then
                    ngx.say(err)
                    return
                end
    
                local data, err = proc:stdout_read_line()
                if not data then
                    ngx.say(err)
                    return
                end
    
                ngx.say(data)
            end
    
            count_char(("1234"):rep(2048))
        }
    }
  6. Use ngx.resp for HTTP response handling

    master

    The ngx.resp module provides a Lua API for managing HTTP responses in OpenResty. It offers methods to add headers, set status codes with reason phrases, and bypass RFC 9110 conditional header checks. All methods in this module are static and can be called directly from the module without creating an instance.

    local ngx_resp = require "ngx.resp"
    
    -- Add a header
    ngx_resp.add_header("Foo", "bar")
    
    -- Set status and reason
    ngx_resp.set_status(531, "user defined error")
  7. Use ngx.balancer for dynamic upstream load balancing

    master

    The ngx.balancer module allows you to define highly dynamic NGINX load balancers for existing upstream modules like ngx_http_proxy_module, ngx_http_fastcgi_module, and ngx_stream_proxy_module. It enables per-request selection of backend peers from a dynamic list.

    Implementation Context

    All methods must be called within the balancer_by_lua* phase (e.g., balancer_by_lua_block or balancer_by_lua_file).

    http {
        upstream backend {
            server 0.0.0.1; # placeholder
    
            balancer_by_lua_block {
                local balancer = require "ngx.balancer"
                local host = "127.0.0.2"
                local port = 8080
    
                local ok, err = balancer.set_current_peer(host, port)
                if not ok then
                    ngx.log(ngx.ERR, "failed to set the current peer: ", err)
                    return ngx.exit(500)
                end
            }
        }
    
        server {
            listen 80;
            location / {
                proxy_pass http://backend/fake;
            }
        }
    }
  8. Use ngx.ssl to control downstream SSL handshakes

    master

    The ngx.ssl module provides a Lua API for controlling the SSL handshake process. It is primarily used in contexts like ssl_certificate_by_lua* to implement lazy loading and caching of SSL certificate chains and private keys. This is particularly useful for web servers serving a very large number of HTTPS sites.

    local ssl = require "ngx.ssl"
  9. Implement distributed SSL session caching with ngx.ssl.session

    master

    The ngx.ssl.session module allows you to implement distributed SSL session caching. By storing and retrieving serialized SSL session data in an external store (like Redis or Memcached), you can allow different NGINX workers or even different servers to resume SSL sessions. This avoids expensive full SSL handshakes, significantly reducing CPU usage.

    To implement this, you must use two NGINX directives from the lua-nginx-module:

    1. ssl_session_fetch_by_lua*: Used to look up a session by its ID from your cache.
    2. ssl_session_store_by_lua*: Used to save the session data to your cache after a handshake completes.

    Workflow:

    • Fetch: Call get_session_id() to get the ID, look it up in your cache, and if found, call set_serialized_session(session) to resume the session.
    • Store: Call get_session_id() and get_serialized_session() to retrieve the current session data, then save it to your cache (ideally using ngx.timer.at to avoid blocking the handshake process).
    # Example pattern for distributed caching
    
    ssl_session_fetch_by_lua_block {
        local ssl_sess = require "ngx.ssl.session"
        local sess_id, err = ssl_sess.get_session_id()
        if sess_id then
            local sess = my_lookup_ssl_session_by_id(sess_id) -- User implemented
            if sess then
                ssl_sess.set_serialized_session(sess)
            end
        end
    }
    
    ssl_session_store_by_lua_block {
        local ssl_sess = require "ngx.ssl.session"
        local sess_id, err = ssl_sess.get_session_id()
        local sess, err = ssl_sess.get_serialized_session()
        if sess_id and sess then
            -- Use a timer to save asynchronously
            ngx.timer.at(0, function(premature, sess_id, sess) 
                my_save_ssl_session_by_id(sess_id, sess) 
            end, sess_id, sess)
        end
    }
  10. How ngx.semaphore works for thread synchronization

    master

    The ngx.semaphore module provides an efficient, pure userland semaphore API for synchronizing OpenResty "light threads" (created via ngx.thread.spawn, ngx.timer.at, etc.). It is designed to work with the NGINX event model without blocking operating system threads or requiring constant polling.

    Scope of Synchronization

    • Same Context: Synchronizing threads within the same request or execution flow.
    • Different Contexts: Synchronizing threads across different requests or timers, provided they reside in the same NGINX worker process and lua_code_cache is enabled (default).

    Note: For synchronization across different NGINX worker processes (inter-process), use lua-resty-lock instead.

    Requirements

    • LuaJIT FFI feature must be enabled.
    local semaphore = require "ngx.semaphore"
    local sema = semaphore.new(n) -- n is initial resources
  11. Manage Nginx processes using ngx.process

    master

    The ngx.process module allows Lua code to interact with and manage Nginx process roles. It provides utilities to identify if the current context is a master, worker, or privileged agent, and allows for enabling privileged agents or signaling graceful exits.

    Typical Workflow Example:

    1. Use init_by_lua_block to enable the privileged agent.
    2. Use init_worker_by_lua_block or content_by_lua_block to perform role-specific logic using type().
    3. Retrieve the master PID for process management tasks.
    # Example nginx.conf usage
    init_by_lua_block {
        local process = require "ngx.process"
        process.enable_privileged_agent()
    }
    
    server {
        location = /status {
            content_by_lua_block {
                local process = require "ngx.process"
                ngx.say("type: ", process.type())
                ngx.say("master pid: ", process.get_master_pid() or "-")
            }
        }
    }