AnyEvent-YACurl
view release on metacpan or search on metacpan
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#define MY_CXT_KEY "AnyEvent::YACurl::_guts" XS_VERSION
#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);
char *str_copy;
Newx(str_copy, len+1, char);
strncpy(str_copy, str, len);
str_copy[len] = 0x00;
result = curl_easy_setopt(request->easy, option, str_copy);
Safefree(str_copy);
break;
}
/* Longs */
#include "curlopt-long.inc"
{
long param = SvIV(parameter);
result = curl_easy_setopt(request->easy, option, param);
break;
}
/* off_t's */
#include "curlopt-off-t.inc"
{
curl_off_t param = SvIV(parameter);
result = curl_easy_setopt(request->easy, option, param);
break;
}
/* string lists */
#include "curlopt-slist.inc"
{
if (!SvROK(parameter) || SvTYPE(SvRV(parameter)) != SVt_PVAV) {
croak("Cannot convert %s to ARRAY reference", SvPV_nolen(parameter));
} else {
struct curl_slist *list = slist_from_av(aTHX_ NULL, (AV*)SvRV(parameter));
result = curl_easy_setopt(request->easy, option, list);
Renew(request->slists, request->slists_count+1, struct curl_slist*);
request->slists[request->slists_count] = list;
request->slists_count++;
}
break;
}
#ifdef CURL_BLOB_COPY
/* blobs */
#include "curlopt-blob.inc"
{
struct curl_blob blob;
blob.data = SvPV(parameter, blob.len);
blob.flags = CURL_BLOB_COPY;
result = curl_easy_setopt(request->easy, option, &blob);
break;
}
#endif
/* File handles */
case CURLOPT_STDERR:
{
if (request->redirected_stderr) {
fclose(request->redirected_stderr);
}
request->redirected_stderr = fdopen(dup(SvIV(parameter)), "a");
if (!request->redirected_stderr) {
croak("Cannot set CURLOPT_STDERR: fdopen failed");
}
result = curl_easy_setopt(request->easy, option, request->redirected_stderr);
break;
}
/* Special functions */
case CURLOPT_WRITEFUNCTION:
case CURLOPT_HEADERFUNCTION:
case CURLOPT_READFUNCTION:
case CURLOPT_DEBUGFUNCTION:
case CURLOPT_TRAILERFUNCTION:
{
SV* fn_copy = newSVsv(parameter);
av_push(request->held_references, fn_copy);
switch (option) {
case CURLOPT_WRITEFUNCTION: {
result = curl_easy_setopt(request->easy, CURLOPT_WRITEFUNCTION, mcurl_write_callback);
result = curl_easy_setopt(request->easy, CURLOPT_WRITEDATA, fn_copy);
break;
}
case CURLOPT_HEADERFUNCTION: {
result = curl_easy_setopt(request->easy, CURLOPT_HEADERFUNCTION, mcurl_write_callback);
result = curl_easy_setopt(request->easy, CURLOPT_HEADERDATA, fn_copy);
break;
}
case CURLOPT_READFUNCTION: {
result = curl_easy_setopt(request->easy, CURLOPT_READFUNCTION, mcurl_read_callback);
result = curl_easy_setopt(request->easy, CURLOPT_READDATA, fn_copy);
break;
}
case CURLOPT_DEBUGFUNCTION: {
result = curl_easy_setopt(request->easy, CURLOPT_DEBUGFUNCTION, mcurl_debug_callback);
result = curl_easy_setopt(request->easy, CURLOPT_DEBUGDATA, fn_copy);
break;
}
case CURLOPT_TRAILERFUNCTION: {
result = curl_easy_setopt(request->easy, CURLOPT_TRAILERFUNCTION, mcurl_trailer_callback);
result = curl_easy_setopt(request->easy, CURLOPT_TRAILERDATA, fn_copy);
break;
}
default: { result = CURLE_OK; } /* To keep compilers quiet */
}
break;
}
/* Post fields are a bit special because of how they are copied (and can contain zero-bytes) */
case CURLOPT_POSTFIELDS:
{
STRLEN pvlen;
char *pv = SvPV(parameter, pvlen);
result = curl_easy_setopt(request->easy, CURLOPT_POSTFIELDSIZE, pvlen);
result = curl_easy_setopt(request->easy, CURLOPT_COPYPOSTFIELDS, pv);
break;
}
/* MIME posts are specified as an arrayref of hashes with a 'name' and a 'value' or 'file' */
case CURLOPT_MIMEPOST:
{
if (!SvROK(parameter) || SvTYPE(SvRV(parameter)) != SVt_PVAV) {
croak("Cannot convert %s to ARRAY reference", SvPV_nolen(parameter));
if (opt == CURLINFO_PRIVATE) {
/* These would be meaningless to access, so don't bother */
croak("Refusing access to private CURL data");
} else if ((opt & CURLINFO_TYPEMASK) == CURLINFO_STRING) {
char *result;
CURLcode ccode = curl_easy_getinfo(response->easy, opt, &result);
if (ccode != CURLE_OK) {
croak("%s", curl_easy_strerror(ccode));
}
RETVAL = newSVpv(result, 0);
} else if ((opt & CURLINFO_TYPEMASK) == CURLINFO_LONG) {
long result;
CURLcode ccode = curl_easy_getinfo(response->easy, opt, &result);
if (ccode != CURLE_OK) {
croak("%s", curl_easy_strerror(ccode));
}
RETVAL = newSViv(result);
} else if ((opt & CURLINFO_TYPEMASK) == CURLINFO_OFF_T) {
curl_off_t result;
CURLcode ccode = curl_easy_getinfo(response->easy, opt, &result);
if (ccode != CURLE_OK) {
croak("%s", curl_easy_strerror(ccode));
}
RETVAL = newSViv(result);
} else if ((opt & CURLINFO_TYPEMASK) == CURLINFO_DOUBLE) {
double result;
CURLcode ccode = curl_easy_getinfo(response->easy, opt, &result);
if (ccode != CURLE_OK) {
croak("%s", curl_easy_strerror(ccode));
}
RETVAL = newSVnv(result);
} else if (opt_from_str) {
croak("Don't know what to do with curl's %d (%s)", opt, SvPV_nolen(option));
} else {
croak("Don't know what to do with curl's %d", opt);
}
OUTPUT:
RETVAL
void
DESTROY(self)
SV* self
CODE:
AnyEvent__YACurl__Response *response = sv_to_response(aTHX_ self);
if (response->easy) {
curl_easy_cleanup(response->easy);
}
if (response->mimepost) {
curl_mime_free(response->mimepost);
}
if (response->held_references) {
SvREFCNT_dec(response->held_references);
}
if (response->redirected_stderr) {
fclose(response->redirected_stderr);
}
if (response->slists) {
int i;
for (i = 0; i < response->slists_count; i++) {
curl_slist_free_all(response->slists[i]);
}
Safefree(response->slists);
}
if (response->callback) {
SvREFCNT_dec(response->callback);
}
Safefree(response);
( run in 2.204 seconds using v1.01-cache-2.11-cpan-5b529ec07f3 )