Skip to content

set_timeout/set_timeouts: validate inputs and define the connect-stage timeout budget #24

Open

Description

@shreemaan-abhishek

P2 follow-up from the review of #22 (#22 (comment)).

client:set_timeout(t) / client:set_timeouts(connect, send, read) are wired through but their semantics are underspecified and untested.

Gaps

1. No validation.

client.set_timeouts (lib/resty/ngx_http_ffi_client.lua:467) stores whatever it is given: self.connect_timeout = connect_timeout or self.connect_timeout. A non-number reaches the FFI call and raises a cdata conversion error from inside the library; a negative number is converted into ngx_msec_t (unsigned) and silently becomes a huge timeout. On the C side ngx_http_ffi_client_ffi_set_timeouts() (src/ngx_http_ffi_client_request.c:1817) ignores any value <= 0, so set_timeout(0) is a silent no-op rather than "no timeout" or an error.

2. The connect deadline is per-stage, not one budget, and DNS is outside it.

  • DNS: rctx->timeout = clcf->resolver_timeout (src/ngx_http_ffi_client_request.c:641) — bounded by the nginx resolver_timeout directive, not by connect_timeout.
  • TCP connect: ngx_add_timer(c->write, op->connect_timeout) (src/ngx_http_ffi_client_request.c:321).
  • TLS handshake: a fresh ngx_add_timer(c->read, op->connect_timeout) (src/ngx_http_ffi_client_request.c:1484).

So a caller asking for a 1s connect timeout can wait resolver_timeout + 1s + 1s before connect() returns. Whether that is intended (per-stage budgets, matching cosocket) or a bug (one shared deadline) needs to be decided and documented — ai-transport maps timeouts to 504, so the wall-clock a caller can observe matters.

3. No stall tests.

There is no test that actually stalls a connect, a TLS handshake, a send, or a read and asserts the timeout fires within the configured budget with the expected error string.

Proposal

  • Validate arguments in set_timeouts: reject non-numbers and negatives with a clear error; define 0 (either "unlimited" or rejected, but not a silent no-op).
  • Decide and document the connect-stage model: one shared connect deadline covering DNS + TCP + TLS, or explicit per-stage budgets with DNS bounded by resolver_timeout.
  • Add real stall tests for connect, TLS handshake, send, and read, asserting both the elapsed budget and the error string (timeout / timed out, matching the lua-resty-http strings ai-transport maps to 504).

Acceptance

  • Documented timeout contract in the README.
  • Test::Nginx cases covering each stalled stage.
  • Green on both parser backends.

Related: #16 (framing error paths). Part of #18.

Activity

  1. coderabbitai commented on Jul 27, 2026

    @coderabbitai
    🔗 Related PRs

    api7/rfcs#115 - RFC: bring ngx_http_ffi_client to parity with lua-resty-http [merged]
    #19 - feat: add TLS (https, SNI, ssl_verify) to the request path [merged]
    api7/aisix#809 - fix(azure): apply stream_timeout per chunk, not as a whole-response ceiling [merged]
    #21 - feat: resolve hostnames through the nginx resolver [merged]
    #22 - feat: stateful client object with buffered and streaming reads [open]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions