Fetch

 view release on metacpan or  search on metacpan

include/fetch/fetch_abi.h  view on Meta::CPAN

 * append, and a consumer requires abi_version >= the version it was written
 * against. NOT ==. An equality check makes every release of this dist a
 * breaking change for everything that consumes it: Reverse::Proxy 0.04 used
 * one, and when 0.14 shipped ABI 2 - a single appended member, with every
 * earlier field at its original offset - the dist stopped loading on every
 * platform and perl it was tested on.
 *
 * Perl headers (EXTERN.h / perl.h / XSUB.h) must be included before this file
 * so SV, AV, HV, STRLEN and pTHX are defined. */

#define FETCH_ABI_VERSION 3

/* one outbound request header; lengths are explicit (a value may be binary) */
typedef struct fetch_hdr {
    const char *name; STRLEN nlen;
    const char *val;  STRLEN vlen;
} fetch_hdr;

/* Called once when the upstream request settles, to shape the value the
 * returned future resolves to (for a proxy: a PSGI [ status, \@hdrs, \@body ]).
 *   ok = 1 -> status/headers/body describe the upstream response; headers is
 *             the flat [k,v,...] AV and body the content SV, both borrowed;
 *             err = NULL.
 *   ok = 0 -> transport failure/cancel; status = 0, headers = NULL,
 *             body = NULL, err = the failure SV (borrowed) or NULL.
 * Must return a new (+1-owned) SV; the caller settles the future with it and
 * then drops the reference. Must not croak. */
typedef SV *(*fetch_map_cb)(pTHX_ int ok, int status, AV *headers, SV *body,
                            SV *err, void *ud);

/* Streaming callbacks (see request_stream); ud is the consumer's context.
 * on_headers fires once with the status and the flat [k,v,...] header AV;
 * on_body fires per body chunk (chunk borrowed); on_done fires once at the end
 * (ok=1 success; ok=0 with err the failure SV or NULL). */
typedef void (*fetch_on_headers)(pTHX_ int status, AV *headers, void *ud);
typedef void (*fetch_on_body)(pTHX_ const char *chunk, STRLEN len, void *ud);
typedef void (*fetch_on_done)(pTHX_ int ok, SV *err, void *ud);

/* v2 outbound observer. `start` fires once per HOP - a redirect chain is
 * several requests and each is observed - with the merged header list still
 * MUTABLE: push a name and a value onto `headers` and the request carries
 * them. That is the point of the hook; a tracing layer has to be able to add
 * a `traceparent` to a request the application wrote.
 * It returns an opaque token, handed back to `done` when that same hop
 * settles, so a consumer can correlate the two without a lookup table.
 * `done` fires exactly ONCE per start, with exactly one of res (the response)
 * and err (the failure) non-NULL - including a timeout, a DNS failure and a
 * refused connection, which are the endings an instrumented client most wants
 * and the easiest to leak. Both are borrowed. Neither may croak. */
typedef void *(*fetch_obs_start_cb)(pTHX_ const char *method, STRLEN mlen,
                                    const char *url, STRLEN ulen,
                                    AV *headers, void *ud);
typedef void  (*fetch_obs_done_cb)(pTHX_ void *token, SV *res, SV *err,
                                   void *ud);

typedef struct fetch_abi {
    int abi_version;                 /* == FETCH_ABI_VERSION */

    /* Construct a Fetch user agent from flat key/value SV pairs (the same
     * options Fetch->new takes: loop, pool_size, tls_verify, timeout, agent,
     * headers, cookie_jar, keep_alive, max_redirects, simple_response). kv has
     * nkv SVs (nkv even). Returns the blessed Fetch UA SV (+1 owned). */
    SV *(*ua_new)(pTHX_ SV **kv, int nkv);

    /* Issue one HTTP request on $ua_sv (a Fetch object), building the request
     * entirely from C (no Perl option hash on the hot path). method and url are
     * NUL-terminated; body may be NULL. max_redirects < 0 means "use the UA's
     * own default". When the request settles, `map` shapes the result. Returns
     * the derived future SV (a Fetch::Future, +1 owned by the caller) - hand it
     * to an awaiting server or call ->get on it. */
    SV *(*request)(pTHX_ SV *ua_sv,
                   const char *method, const char *url,
                   const fetch_hdr *hdrs, int nhdrs,
                   const char *body, STRLEN blen,
                   double timeout, int max_redirects,
                   fetch_map_cb map, void *ud);

    /* Extract parts from an already-resolved response hashref (a blessed
     * Fetch::Response or the raw simple_response hash), no method dispatch.
     * Any out-ptr may be NULL; *headers and *body are borrowed. For the
     * blocking (non-Hyperman) path. */
    void (*res_parts)(pTHX_ SV *res, int *status, AV **headers, SV **body);

    /* Like request, but streams the response instead of buffering: on_headers
     * fires once up front, on_body per chunk, on_done at completion. Returns
     * the request future SV (+1 owned) - keep it alive until on_done fires.
     * max_redirects < 0 uses the UA default. */
    SV *(*request_stream)(pTHX_ SV *ua_sv, const char *method, const char *url,
                          const fetch_hdr *hdrs, int nhdrs,
                          const char *body, STRLEN blen,
                          double timeout, int max_redirects,
                          fetch_on_headers on_headers, fetch_on_body on_body,
                          fetch_on_done on_done, void *ud);

    /* Raw blocking upstream connection for an Upgrade/WebSocket tunnel. TLS
     * (tls=1) reuses Fetch's client SSL_CTX, so the consumer tunnels to a
     * wss/https upstream without linking OpenSSL. All pure C (no pTHX):
     *   tunnel_connect -> opaque handle (NULL on failure)
     *   tunnel_fd      -> underlying fd, for select()
     *   tunnel_read    -> bytes (>0), 0 = EOF, -1 = error
     *   tunnel_write_all -> 0 all written, -1 = error
     *   tunnel_pending -> bytes buffered in TLS (drain before select); 0 plain
     *   tunnel_close   -> shut down and free the handle */
    void *(*tunnel_connect)(const char *host, int port, int tls, int verify);
    int   (*tunnel_fd)(void *conn);
    IV    (*tunnel_read)(void *conn, char *buf, IV len);
    IV    (*tunnel_write_all)(void *conn, const char *buf, IV len);
    int   (*tunnel_pending)(void *conn);
    void  (*tunnel_close)(void *conn);

    /* ---- v2: observe outbound requests ---------------------------------- *
     * Fetch had no way to see a request go out. `headers` is a per-request
     * option the CALLER sets, which is no use to anything wanting to annotate
     * a request the application wrote, or to be told how it ended.
     *
     * Registration is process-global rather than per agent: an agent is a
     * per-worker object and a process may hold several, while an observer is
     * a property of the process. Register at boot. There is no
     * deregistration. Returns 1, or 0 when the table is full. */
    int (*on_request)(pTHX_ fetch_obs_start_cb start, fetch_obs_done_cb done,
                      void *ud);

    /* ---- v3: upgrade a plaintext tunnel to TLS (SMTP STARTTLS) ---------- *
     * The handshake tunnel_connect performs when tls=1, on a handle opened
     * with tls=0, after the application protocol has negotiated the upgrade.
     * The same SSL_CTX, SNI and hostname verification as tunnel_connect.
     * 0 on success; -1 when the handle is NULL, already TLS, TLS is
     * unavailable in this build, or the handshake fails - after a failed
     * handshake the socket is in an undefined state and the caller closes it.
     * Pure C, no pTHX, like the rest of the tunnel. */
    int (*tunnel_starttls)(void *conn, const char *host, int verify);
} fetch_abi;

/* How many outbound observers Fetch will hold. Fixed, so registration
 * allocates nothing and an uninstrumented request pays one branch. */
#define FETCH_ABI_MAX_OBSERVERS 8

#endif /* FETCH_ABI_H */



( run in 1.490 second using v1.01-cache-2.11-cpan-14f38c9f855 )