Fetch

 view release on metacpan or  search on metacpan

xs/fetch.xs  view on Meta::CPAN

MODULE = Fetch		PACKAGE = Fetch

PROTOTYPES: DISABLE

# ---- user agent ----------------------------------------------------------

# Fetch->new(%args): loop, headers, agent, tls_verify, max_redirects, timeout,
# keep_alive, cookie_jar, pool_size. The object is a blessed IV over an ft_ua.
SV *
new(class, ...)
    SV *class
    CODE:
    {
        const char *cls = (SvROK(class) && SvOBJECT(SvRV(class)))
                        ? HvNAME(SvSTASH(SvRV(class))) : SvPV_nolen(class);
        RETVAL = ft_ua_new(aTHX_ cls, &ST(1), items - 1);   /* shared with the ABI */
    }
    OUTPUT:
        RETVAL

# $ua->clone(%overrides): another agent over this one's connection pool and
# loop. For a per-request cookie jar, where a fresh agent would cost the pool.
SV *
clone(self, ...)
    SV *self
    CODE:
        RETVAL = ft_ua_clone(aTHX_ self, &ST(1), items - 1);
    OUTPUT:
        RETVAL

void
DESTROY(self)
    SV *self
    CODE:
    {
        ft_ua *ua = ft_ua_of(aTHX_ self);
        if (ua) {
            /* Free the pool before the loop: parked connections stay armed for
             * READ (see ft_conn_park), so freeing them unwatches on the loop -
             * which must still be alive. A native loop's ft_loop* is owned by
             * ua->loop, so releasing it first would leave the pool's connections
             * unwatching a freed loop (use-after-free). */
            if (ua->pool)       SvREFCNT_dec(ua->pool);
            if (ua->loop)       SvREFCNT_dec(ua->loop);
            if (ua->headers)    SvREFCNT_dec(ua->headers);
            if (ua->agent)      SvREFCNT_dec(ua->agent);
            if (ua->cookie_jar) SvREFCNT_dec(ua->cookie_jar);
            Safefree(ua);
        }
    }

SV *
loop(self)
    SV *self
    CODE:
        RETVAL = newSVsv(ft_ua_of(aTHX_ self)->loop);
    OUTPUT:
        RETVAL

SV *
cookie_jar(self)
    SV *self
    CODE:
    {
        ft_ua *ua = ft_ua_of(aTHX_ self);
        RETVAL = (ua->cookie_jar && SvOK(ua->cookie_jar))
               ? newSVsv(ua->cookie_jar) : newSV(0);
    }
    OUTPUT:
        RETVAL

# $ua->request($method, $url, %opt) -> Fetch::Future
SV *
request(self, method, url, ...)
    SV         *self
    const char *method
    const char *url
    CODE:
        RETVAL = ft_dispatch(aTHX_ self, method, url, &ST(3), items - 3);
    OUTPUT:
        RETVAL

# verb helpers: $ua->get($url, %opt) etc.
SV *
get(self, url, ...)
    SV         *self
    const char *url
    ALIAS:
        head   = 1
        delete = 2
        post   = 3
        put    = 4
    CODE:
    {
        static const char *const M[5] = { "GET", "HEAD", "DELETE", "POST", "PUT" };
        RETVAL = ft_dispatch(aTHX_ self, M[ix], url, &ST(2), items - 2);
    }
    OUTPUT:
        RETVAL

# ---- introspection / internals ------------------------------------------

# True if built with OpenSSL (https support).
int
_tls_available()
    CODE:
        RETVAL = FT_TLS_AVAILABLE;
    OUTPUT:
        RETVAL

# True if built with nghttp2 (HTTP/2 support; needs TLS/ALPN too).
int
_h2_available()
    CODE:
        RETVAL = FT_H2_AVAILABLE;
    OUTPUT:
        RETVAL

# Start an HTTP/1.1 request on $loop to host:port with the pre-built request
# bytes; returns a pending Fetch::Future resolving to a Fetch::Response.
SV *
_request(loop, pool, host, port, req, tls, verify, timeout, method, scheme, authority, path, headers, body, on_body)
    SV         *loop
    SV         *pool
    const char *host
    const char *port
    SV         *req
    int         tls
    int         verify
    double      timeout
    SV         *method
    SV         *scheme
    SV         *authority
    SV         *path
    SV         *headers
    SV         *body
    SV         *on_body
    CODE:
    {
        /* A native Standalone loop is a blessed IV holding a ft_loop*; any
         * other object is a foreign adapter (IO::Async/AnyEvent/Hyperman)
         * whose _ft_arm method the C core calls to arm interest. */
        ft_loop    *l = NULL;
        SV         *lsv = NULL;
        ft_pool    *pl = ft_pool_from_sv(aTHX_ pool);
        STRLEN      len;
        const char *bytes;
        if (sv_isobject(loop) && sv_derived_from(loop, "Fetch::Loop::Standalone"))
            l = ft_loop_from_sv(aTHX_ loop);
        else
            lsv = loop;
        bytes = SvPV(req, len);
        RETVAL = ft_h1_start(aTHX_ l, lsv, pl, host, port, bytes, len, tls, verify,
                             timeout, method, scheme, authority, path, headers,
                             body, on_body, NULL);
        hmf_pin_loop(aTHX_ RETVAL, loop);   /* only this loop can resolve it */
    }
    OUTPUT:
        RETVAL

# Create a keep-alive connection pool (opaque handle); DESTROY frees it and
# every connection still parked in it.
SV *
_pool_new(max)
    int max
    CODE:
    {
        ft_pool *p = ft_pool_new(max);
        if (!p) croak("Fetch: out of memory");
        RETVAL = sv_bless(newRV_noinc(newSViv(PTR2IV(p))),
                          gv_stashpv("Fetch::_Pool", GV_ADD));
    }
    OUTPUT:
        RETVAL



( run in 2.029 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )