AnyEvent-YACurl

 view release on metacpan or  search on metacpan

YACurl.xs  view on Meta::CPAN


#include <curl/curl.h>
#include "libcurl-symbols.h"

typedef struct {
    SV *watchset_fn;
    SV *timerset_fn;
    HV *curlopt;
} my_cxt_t;

typedef struct {
    CURLM *multi;
    SV *weak_self_ref;

    int needs_invoke_timeout;
    int needs_read_info;
    int last_running;
} AnyEvent__YACurl;

typedef struct {
    SV *self_rv;
    CURL *easy;
    curl_mime *mimepost;

    AV *held_references;
    FILE *redirected_stderr;
    int slists_count;
    struct curl_slist **slists;
    char errbuf[CURL_ERROR_SIZE];

    SV *callback;
} AnyEvent__YACurl__Response;

START_MY_CXT

struct curl_slist *slist_from_av(pTHX_ struct curl_slist *list, AV *input);

void maybe_warn_eval(pTHX)
{
    SV *error = ERRSV;
    if (SvTRUE(error)) {
        warn("Error in callback: %s", SvPV_nolen(error));
    }
}

int mcurl_socket_callback(CURL* easy,
                          curl_socket_t s,
                          int what,
                          void* userp,
                          void* socketp)
{
    dTHX;
    dMY_CXT;
    dSP;

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);
    EXTEND(SP, 3);
    PUSHs((SV*)userp); /* XXX This is a weakened reference, will it ever be undef? */
    PUSHs(sv_2mortal(newSViv(s)));
    PUSHs(sv_2mortal(newSViv(what)));
    PUTBACK;

    call_sv(MY_CXT.watchset_fn, G_DISCARD | G_VOID);

    FREETMPS;
    LEAVE;

    return 0;
}

int mcurl_timer_callback(CURLM* multi,
                         long timeout_ms,
                         void *userp)
{
    dTHX;

    if (timeout_ms == 0) {
        /* We short-circuit timeout_ms==0, as we're very likely to call do_post_work shortly
         * after reaching this code path. A timer of 0sec in AnyEvent would almost always turn
         * into a 1ms wait, which is unnecessary and slow. Same goes for AE::postpone. */
        IV tmp = SvIV((SV*)SvRV((SV*)userp));
        AnyEvent__YACurl *client = INT2PTR(AnyEvent__YACurl*, tmp);

        client->needs_invoke_timeout = 1;
        return 0;
    }

    dMY_CXT;
    dSP;

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);
    EXTEND(SP, 2);
    PUSHs((SV*)userp); /* XXX This is a weakened reference, will it ever be undef? */
    PUSHs(sv_2mortal(newSViv(timeout_ms)));
    PUTBACK;

    call_sv(MY_CXT.timerset_fn, G_DISCARD | G_VOID);

    FREETMPS;
    LEAVE;

    return 0;
}

/* write callback: used for WRITEFUNCTION and HEADERFUNCTION */
size_t mcurl_write_callback(char *ptr,
                           size_t size,
                           size_t nmemb,
                           void *userdata)
{
    dTHX;
    dSP;

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);
    EXTEND(SP, 1);
    PUSHs(sv_2mortal(newSVpvn(ptr, size*nmemb)));
    PUTBACK;

    call_sv((SV*)userdata, G_DISCARD | G_VOID | G_EVAL);

    SPAGAIN;
    maybe_warn_eval(aTHX);
    PUTBACK;

    FREETMPS;
    LEAVE;

    return size * nmemb;
}

size_t mcurl_read_callback(char *buffer,
                           size_t size,
                           size_t nitems,
                           void *userdata)
{
    size_t result;

    dTHX;
    dSP;

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);
    EXTEND(SP, 1);
    PUSHs(sv_2mortal(newSViv(size * nitems)));
    PUTBACK;

    call_sv((SV*)userdata, G_SCALAR | G_EVAL);

YACurl.xs  view on Meta::CPAN

                        curl_mime_filedata(part, filename);
                    }
                }

                if (!have_value) {
                    croak("MIMEPOST: one of 'value' or 'file' is required, together with 'name'");
                }
            }

            /* If this fails, we'll still free the mimepost properly later */
            result = curl_easy_setopt(request->easy, CURLOPT_MIMEPOST, request->mimepost);

            break;
        }

        /* Don't know... */
        default:
        {
            croak("Not sure what to do with CURL option %d", option);
            break;
        }
    }

    return result;
}

MODULE = AnyEvent::YACurl       PACKAGE = AnyEvent::YACurl

PROTOTYPES: DISABLE

BOOT:
{
    /* XXX: Needs a CLONE */

    MY_CXT_INIT;
    MY_CXT.watchset_fn = NULL;
    MY_CXT.timerset_fn = NULL;
    MY_CXT.curlopt = newHV();
    fill_hv_with_constants(aTHX_ MY_CXT.curlopt);

    curl_global_init(CURL_GLOBAL_ALL);
}

void
new(class, args)
        char *class
        HV *args
    PPCODE:
        dMY_CXT;

        (void)class;
        AnyEvent__YACurl *client;

        Newxz(client, 1, AnyEvent__YACurl);

        ST(0) = sv_newmortal();
        sv_setref_pv(ST(0), "AnyEvent::YACurl", (void*)client);

        /* XXX When we destroy the client, do we pass undefs to the timer/watch functions? */
        client->weak_self_ref = newSVsv(ST(0));
        sv_rvweaken(client->weak_self_ref);

        client->multi = curl_multi_init();
        curl_multi_setopt(client->multi, CURLMOPT_SOCKETFUNCTION, mcurl_socket_callback);
        curl_multi_setopt(client->multi, CURLMOPT_TIMERFUNCTION, mcurl_timer_callback);
        curl_multi_setopt(client->multi, CURLMOPT_SOCKETDATA, (void*)client->weak_self_ref);
        curl_multi_setopt(client->multi, CURLMOPT_TIMERDATA, (void*)client->weak_self_ref);

        {
            hv_iterinit(args);
            HE *iterentry;
            while ((iterentry = hv_iternext(args)) != NULL) {
                long opt;
                int opt_from_str;
                SV *key = HeSVKEY_force(iterentry);
                opt = option_from_sv_or_croak(aTHX_ aMY_CXT_ key, HeHASH(iterentry), &opt_from_str);

                switch (opt) {
                    /* Longs */
                    case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
                    case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
                    case CURLMOPT_MAX_HOST_CONNECTIONS:
                    case CURLMOPT_MAX_PIPELINE_LENGTH:
                    case CURLMOPT_MAX_TOTAL_CONNECTIONS:
                    case CURLMOPT_MAXCONNECTS:
                    case CURLMOPT_PIPELINING:
                    {
                        long value = SvIV(HeVAL(iterentry));
                        CURLMcode mcode = curl_multi_setopt(client->multi, opt, value);
                        if (mcode != CURLM_OK) {
                            croak("Failed to set %d (%s): %s", opt, SvPV_nolen(key), curl_multi_strerror(mcode));
                        }
                        break;
                    }

                    /* String arrays */
                    case CURLMOPT_PIPELINING_SITE_BL:
                    case CURLMOPT_PIPELINING_SERVER_BL:
                    {
                        char **strings;
                        if (!SvROK(HeVAL(iterentry)) || SvTYPE(SvRV(HeVAL(iterentry))) != SVt_PVAV) {
                            croak("%d (%s): cannot convert value to ARRAYREF", opt, SvPV_nolen(key));
                        }

                        AV *array = (AV*)SvRV(HeVAL(iterentry));
                        int arraylen = av_len(array) + 1;

                        Newxz(strings, arraylen+1, char*);

                        int i;
                        for (i = 0; i < arraylen; i++) {
                            char *strcopy, *pv;
                            STRLEN pvlen;
                            pv = SvPV(*av_fetch(array, i, TRUE), pvlen);

                            Newxz(strcopy, pvlen+1, char);
                            Copy(pv, strcopy, pvlen, char);
                            strings[i] = strcopy;
                        }

                        CURLMcode mcode = curl_multi_setopt(client->multi, opt, strings);



( run in 1.414 second using v1.01-cache-2.11-cpan-c221a9de4ec )