Copying ede-bug-report from branches in trunk

This commit is contained in:
Sanel Zukan
2009-07-03 14:23:51 +00:00
parent bfed428c37
commit 66e586f37f
138 changed files with 35380 additions and 0 deletions

View File

@@ -0,0 +1,311 @@
/*=============================================================================
curlMulti
===============================================================================
This is an extension to Curl's CURLM object. The extensions are:
1) It has a lock so multiple threads can use it simultaneously.
2) Its "select" file descriptor vectors are self-contained. CURLM
requires the user to maintain them separately.
=============================================================================*/
#include "xmlrpc_config.h"
#include <stdlib.h>
#if !MSVCRT
#include <sys/select.h>
#endif
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <curl/multi.h>
#include "mallocvar.h"
#include "xmlrpc-c/util.h"
#include "xmlrpc-c/string_int.h"
#include "curlversion.h"
#include "lock.h"
#include "lock_pthread.h"
#include "curlmulti.h"
static void
interpretCurlMultiError(const char ** const descriptionP,
CURLMcode const code) {
#if HAVE_CURL_STRERROR
*descriptionP = strdup(curl_multi_strerror(code));
#else
xmlrpc_asprintf(descriptionP, "Curl error code (CURLMcode) %d", code);
#endif
}
struct curlMulti {
CURLM * curlMultiP;
lock * lockP;
/* Hold this lock while accessing or using *curlMultiP. You're
using the multi manager whenever you're calling a Curl
library multi manager function.
*/
/* The following file descriptor sets are an integral part of the
CURLM object; Our curlMulti_fdset() routine binds them to the
CURLM object, and said object expects us to use them in a very
specific way, including doing a select() on them. It is very,
very messy.
*/
fd_set readFdSet;
fd_set writeFdSet;
fd_set exceptFdSet;
};
curlMulti *
curlMulti_create(void) {
curlMulti * retval;
curlMulti * curlMultiP;
MALLOCVAR(curlMultiP);
if (curlMultiP == NULL)
retval = NULL;
else {
curlMultiP->lockP = curlLock_create_pthread();
if (curlMultiP->lockP == NULL)
retval = NULL;
else {
curlMultiP->curlMultiP = curl_multi_init();
if (curlMultiP->curlMultiP == NULL)
retval = NULL;
else
retval = curlMultiP;
if (retval == NULL)
curlMultiP->lockP->destroy(curlMultiP->lockP);
}
if (retval == NULL)
free(curlMultiP);
}
return retval;
}
void
curlMulti_destroy(curlMulti * const curlMultiP) {
curl_multi_cleanup(curlMultiP->curlMultiP);
curlMultiP->lockP->destroy(curlMultiP->lockP);
free(curlMultiP);
}
void
curlMulti_perform(xmlrpc_env * const envP,
curlMulti * const curlMultiP,
bool * const immediateWorkToDoP,
int * const runningHandlesP) {
/*----------------------------------------------------------------------------
Do whatever work is ready to be done under the control of multi
manager 'curlMultiP'. E.g. if HTTP response data has recently arrived
from the network, process it as an HTTP response.
Iff this results in some work being finished from our point of view,
return *immediateWorkToDoP. (Caller can query the multi manager for
messages and find out what it is).
Return as *runningHandlesP the number of Curl easy handles under the
multi manager's control that are still running -- yet to finish.
-----------------------------------------------------------------------------*/
CURLMcode rc;
curlMultiP->lockP->acquire(curlMultiP->lockP);
rc = curl_multi_perform(curlMultiP->curlMultiP, runningHandlesP);
curlMultiP->lockP->release(curlMultiP->lockP);
if (rc == CURLM_CALL_MULTI_PERFORM) {
*immediateWorkToDoP = true;
} else {
*immediateWorkToDoP = false;
if (rc != CURLM_OK) {
const char * reason;
interpretCurlMultiError(&reason, rc);
xmlrpc_faultf(envP, "Impossible failure of curl_multi_perform(): "
"%s", reason);
xmlrpc_strfree(reason);
}
}
}
void
curlMulti_addHandle(xmlrpc_env * const envP,
curlMulti * const curlMultiP,
CURL * const curlSessionP) {
CURLMcode rc;
curlMultiP->lockP->acquire(curlMultiP->lockP);
rc = curl_multi_add_handle(curlMultiP->curlMultiP, curlSessionP);
curlMultiP->lockP->release(curlMultiP->lockP);
if (rc != CURLM_OK) {
const char * reason;
interpretCurlMultiError(&reason, rc);
xmlrpc_faultf(envP, "Could not add Curl session to the "
"curl multi manager. curl_multi_add_handle() "
"failed: %s", reason);
xmlrpc_strfree(reason);
}
}
void
curlMulti_removeHandle(curlMulti * const curlMultiP,
CURL * const curlSessionP) {
curlMultiP->lockP->acquire(curlMultiP->lockP);
curl_multi_remove_handle(curlMultiP->curlMultiP, curlSessionP);
curlMultiP->lockP->release(curlMultiP->lockP);
}
void
curlMulti_getMessage(curlMulti * const curlMultiP,
bool * const endOfMessagesP,
CURLMsg * const curlMsgP) {
/*----------------------------------------------------------------------------
Get the next message from the queue of things the Curl multi manager
wants to say to us.
Return the message as *curlMsgP.
Iff there are no messages in the queue, return *endOfMessagesP == true.
-----------------------------------------------------------------------------*/
int remainingMsgCount;
CURLMsg * privateCurlMsgP;
/* Note that this is a pointer into the multi manager's memory,
so we have to use it under lock.
*/
curlMultiP->lockP->acquire(curlMultiP->lockP);
privateCurlMsgP = curl_multi_info_read(curlMultiP->curlMultiP,
&remainingMsgCount);
if (privateCurlMsgP == NULL)
*endOfMessagesP = true;
else {
*endOfMessagesP = false;
*curlMsgP = *privateCurlMsgP;
}
curlMultiP->lockP->release(curlMultiP->lockP);
}
void
curlMulti_fdset(xmlrpc_env * const envP,
curlMulti * const curlMultiP,
fd_set * const readFdSetP,
fd_set * const writeFdSetP,
fd_set * const exceptFdSetP,
int * const maxFdP) {
/*----------------------------------------------------------------------------
Set the CURLM object's file descriptor sets to those in the
curlMulti object, update those file descriptor sets with the
current needs of the multi manager, and return the resulting values
of the file descriptor sets.
This is a bizarre operation, but is necessary because of the nonmodular
way in which the Curl multi interface works with respect to waiting
for work with select().
-----------------------------------------------------------------------------*/
CURLMcode rc;
curlMultiP->lockP->acquire(curlMultiP->lockP);
/* curl_multi_fdset() doesn't _set_ the fdsets. It adds to existing
ones (so you can easily do a select() on other fds and Curl
fds at the same time). So we have to clear first:
*/
FD_ZERO(&curlMultiP->readFdSet);
FD_ZERO(&curlMultiP->writeFdSet);
FD_ZERO(&curlMultiP->exceptFdSet);
/* WARNING: curl_multi_fdset() doesn't just update the fdsets pointed
to by its arguments. It makes the CURLM object remember those
pointers and refer back to them later! In fact, curl_multi_perform
expects its caller to have done a select() on those masks. No,
really. The man page even admits it.
Inspection of the Libcurl code in March 2007 indicates that
this isn't actually true -- curl_multi_fdset() updates your
fdset and doesn't remember the pointer at all. I.e. it's just
what you would expect. The man pages still says it's as
described above. My guess is that Libcurl was fixed at some
time and the man page not updated. In any case, we have to
work with old Libcurl if at all possible, so we still maintain
these fdsets as if they belong to the CURLM object.
*/
rc = curl_multi_fdset(curlMultiP->curlMultiP,
&curlMultiP->readFdSet,
&curlMultiP->writeFdSet,
&curlMultiP->exceptFdSet,
maxFdP);
*readFdSetP = curlMultiP->readFdSet;
*writeFdSetP = curlMultiP->writeFdSet;
*exceptFdSetP = curlMultiP->exceptFdSet;
curlMultiP->lockP->release(curlMultiP->lockP);
if (rc != CURLM_OK) {
const char * reason;
interpretCurlMultiError(&reason, rc);
xmlrpc_faultf(envP, "Impossible failure of curl_multi_fdset(): %s",
reason);
xmlrpc_strfree(reason);
}
}
void
curlMulti_updateFdSet(curlMulti * const curlMultiP,
fd_set const readFdSet,
fd_set const writeFdSet,
fd_set const exceptFdSet) {
/*----------------------------------------------------------------------------
curl_multi_perform() expects the file descriptor sets, which were bound
to the CURLM object via a prior curlMulti_fdset(), to contain the results
of a recent select(). This subroutine provides you a way to supply those.
-----------------------------------------------------------------------------*/
curlMultiP->readFdSet = readFdSet;
curlMultiP->writeFdSet = writeFdSet;
curlMultiP->exceptFdSet = exceptFdSet;
}

View File

@@ -0,0 +1,51 @@
#ifndef CURLMULTI_H_INCLUDED
#define CURLMULTI_H_INCLUDED
#include "bool.h"
#include "xmlrpc-c/util.h"
#include "curltransaction.h"
typedef struct curlMulti curlMulti;
curlMulti *
curlMulti_create(void);
void
curlMulti_destroy(curlMulti * const curlMultiP);
void
curlMulti_perform(xmlrpc_env * const envP,
curlMulti * const curlMultiP,
bool * const immediateWorkToDoP,
int * const runningHandlesP);
void
curlMulti_addHandle(xmlrpc_env * const envP,
curlMulti * const curlMultiP,
CURL * const curlSessionP);
void
curlMulti_removeHandle(curlMulti * const curlMultiP,
CURL * const curlSessionP);
void
curlMulti_getMessage(curlMulti * const curlMultiP,
bool * const endOfMessagesP,
CURLMsg * const curlMsgP);
void
curlMulti_fdset(xmlrpc_env * const envP,
curlMulti * const curlMultiP,
fd_set * const readFdSetP,
fd_set * const writeFdSetP,
fd_set * const exceptFdSetP,
int * const maxFdP);
void
curlMulti_updateFdSet(curlMulti * const curlMultiP,
fd_set const readFdSet,
fd_set const writeFdSet,
fd_set const exceptFdSet);
#endif

View File

@@ -0,0 +1,621 @@
/*=============================================================================
curlTransaction
=============================================================================*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "mallocvar.h"
#include "xmlrpc-c/util.h"
#include "xmlrpc-c/string_int.h"
#include "xmlrpc-c/client.h"
#include "xmlrpc-c/client_int.h"
#include "version.h"
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include "curlversion.h"
#include "curltransaction.h"
struct curlTransaction {
/* This is all stuff that really ought to be in a Curl object, but
the Curl library is a little too simple for that. So we build
a layer on top of Curl, and define this "transaction," as an
object subordinate to a Curl "session." A Curl session has
zero or one transactions in progress. The Curl session
"private data" is a pointer to the CurlTransaction object for
the current transaction.
*/
CURL * curlSessionP;
/* Handle for the Curl session that hosts this transaction.
Note that only one transaction at a time can use a particular
Curl session, so this had better not be a session that some other
transaction is using simultaneously.
*/
curlt_finishFn * finish;
curlt_progressFn * progress;
void * userContextP;
/* Meaningful to our client; opaque to us */
CURLcode result;
/* Result of the transaction (succeeded, TCP connect failed, etc.).
A properly executed HTTP transaction (request & response) counts
as a successful transaction. When 'result' show success,
curl_easy_get_info() tells you whether the transaction succeeded
at the HTTP level.
*/
char curlError[CURL_ERROR_SIZE];
/* Error message from Curl */
struct curl_slist * headerList;
/* The HTTP headers for the transaction */
const char * serverUrl; /* malloc'ed - belongs to this object */
};
static void
addHeader(xmlrpc_env * const envP,
struct curl_slist ** const headerListP,
const char * const headerText) {
struct curl_slist * newHeaderList;
newHeaderList = curl_slist_append(*headerListP, headerText);
if (newHeaderList == NULL)
xmlrpc_faultf(envP,
"Could not add header '%s'. "
"curl_slist_append() failed.", headerText);
else
*headerListP = newHeaderList;
}
static void
addContentTypeHeader(xmlrpc_env * const envP,
struct curl_slist ** const headerListP) {
addHeader(envP, headerListP, "Content-Type: text/xml");
}
static void
addUserAgentHeader(xmlrpc_env * const envP,
struct curl_slist ** const headerListP,
const char * const userAgent) {
if (userAgent) {
/* Note: Curl has a CURLOPT_USERAGENT option that does some of this
work. We prefer to be totally in control, though, so we build
the header explicitly.
*/
curl_version_info_data * const curlInfoP =
curl_version_info(CURLVERSION_NOW);
char curlVersion[32];
const char * userAgentHeader;
snprintf(curlVersion, sizeof(curlVersion), "%u.%u.%u",
(curlInfoP->version_num >> 16) && 0xff,
(curlInfoP->version_num >> 8) && 0xff,
(curlInfoP->version_num >> 0) && 0xff
);
xmlrpc_asprintf(&userAgentHeader,
"User-Agent: %s Xmlrpc-c/%s Curl/%s",
userAgent, XMLRPC_C_VERSION, curlVersion);
if (userAgentHeader == xmlrpc_strsol)
xmlrpc_faultf(envP, "Couldn't allocate memory for "
"User-Agent header");
else {
addHeader(envP, headerListP, userAgentHeader);
xmlrpc_strfree(userAgentHeader);
}
}
}
static void
addAuthorizationHeader(xmlrpc_env * const envP,
struct curl_slist ** const headerListP,
const char * const hdrValue) {
const char * authorizationHeader;
xmlrpc_asprintf(&authorizationHeader, "Authorization: %s", hdrValue);
if (authorizationHeader == xmlrpc_strsol)
xmlrpc_faultf(envP, "Couldn't allocate memory for "
"Authorization header");
else {
addHeader(envP, headerListP, authorizationHeader);
xmlrpc_strfree(authorizationHeader);
}
}
static void
createCurlHeaderList(xmlrpc_env * const envP,
const char * const authHdrValue,
const char * const userAgent,
struct curl_slist ** const headerListP) {
struct curl_slist * headerList;
headerList = NULL; /* initial value - empty list */
addContentTypeHeader(envP, &headerList);
if (!envP->fault_occurred) {
addUserAgentHeader(envP, &headerList, userAgent);
if (!envP->fault_occurred) {
if (authHdrValue)
addAuthorizationHeader(envP, &headerList, authHdrValue);
}
}
if (envP->fault_occurred)
curl_slist_free_all(headerList);
else
*headerListP = headerList;
}
static size_t
collect(void * const ptr,
size_t const size,
size_t const nmemb,
FILE * const stream) {
/*----------------------------------------------------------------------------
This is a Curl output function. Curl calls this to deliver the
HTTP response body to the Curl client. Curl thinks it's writing to
a POSIX stream.
-----------------------------------------------------------------------------*/
xmlrpc_mem_block * const responseXmlP = (xmlrpc_mem_block *) stream;
char * const buffer = ptr;
size_t const length = nmemb * size;
size_t retval;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_mem_block_append(&env, responseXmlP, buffer, length);
if (env.fault_occurred)
retval = (size_t)-1;
else
/* Really? Shouldn't it be like fread() and return 'nmemb'? */
retval = length;
return retval;
}
static int
curlProgress(void * const contextP,
double const dltotal ATTR_UNUSED,
double const dlnow ATTR_UNUSED,
double const ultotal ATTR_UNUSED,
double const ulnow ATTR_UNUSED) {
/*----------------------------------------------------------------------------
This is a Curl "progress function." It's something various Curl
functions call every so often, including whenever something gets
interrupted by the process receiving, and catching, a signal.
There are two purposes of a Curl progress function: 1) lets us log
the progress of a long-running transaction such as a big download,
e.g. by displaying a progress bar somewhere. In Xmlrpc-c, we don't
implement this purpose. 2) allows us to tell the Curl function,
via our return code, that calls it that we don't want to wait
anymore for the operation to complete.
In Curl versions before March 2007, we get called once per second
and signals have no effect. In current Curl, we usually get called
immediately after a signal gets caught while Curl is waiting to
receive a response from the server. But Curl doesn't properly
synchronize with signals, so it may miss one and then we don't get
called until the next scheduled one-per-second call.
All we do is tell Caller it's time to give up if the transport's
client says it is via his "interrupt" flag.
This function is not as important as it once was. This module used
to use curl_easy_perform(), which can be interrupted only via this
progress function. But because of the above-mentioned failure of
Curl to properly synchronize signals (and Bryan's failure to get
Curl developers to accept code to fix it), we now use the Curl
"multi" facility instead and do our own pselect(). But
This function still normally gets called by curl_multi_perform(),
which the transport tries to call even when the user has requested
interruption, because we don't trust our ability to abort a running
Curl transaction. curl_multi_perform() reliably winds up a Curl
transaction when this function tells it to.
-----------------------------------------------------------------------------*/
curlTransaction * const curlTransactionP = contextP;
bool abort;
/* We require anyone setting us up as the Curl progress function to
supply a progress function:
*/
assert(curlTransactionP);
assert(curlTransactionP->progress);
curlTransactionP->progress(curlTransactionP->userContextP, &abort);
return abort;
}
static void
setupAuth(xmlrpc_env * const envP ATTR_UNUSED,
CURL * const curlSessionP,
const xmlrpc_server_info * const serverInfoP,
const char ** const authHdrValueP) {
/*----------------------------------------------------------------------------
Set the options in the Curl session 'curlSessionP' to set up the HTTP
authentication described by *serverInfoP.
But we have an odd special function for backward compatibility, because
this code dates to a time when libcurl did not have the ability to
handle authentication, but we provided such function nonetheless by
building our own Authorization: header. But we did this only for
HTTP basic authentication.
So the special function is this: if libcurl is too old to have
authorization options and *serverInfoP allows basic authentication,
return as *basicAuthHdrParamP an appropriate parameter for the
Authorization: Basic: HTTP header. Otherwise, return
*basicAuthHdrParamP == NULL.
-----------------------------------------------------------------------------*/
if (serverInfoP->allowedAuth.basic) {
CURLcode rc;
rc = curl_easy_setopt(curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
if (rc == CURLE_OK)
*authHdrValueP = NULL;
else {
*authHdrValueP = strdup(serverInfoP->basicAuthHdrValue);
if (*authHdrValueP == NULL)
xmlrpc_faultf(envP, "Unable to allocate memory for basic "
"authentication header");
}
} else
*authHdrValueP = NULL;
/* We don't worry if libcurl is too old for these other kinds of
authentication; they're only defined as _allowed_
authentication methods, for when client and server are capable
of using it, and unlike with basic authentication, we have no
historical commitment to consider an old libcurl as capable of
doing these.
*/
if (serverInfoP->userNamePw)
curl_easy_setopt(curlSessionP, CURLOPT_USERPWD,
serverInfoP->userNamePw);
if (serverInfoP->allowedAuth.digest)
curl_easy_setopt(
curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
if (serverInfoP->allowedAuth.gssnegotiate)
curl_easy_setopt(
curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE);
if (serverInfoP->allowedAuth.ntlm)
curl_easy_setopt(
curlSessionP, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
}
static void
setCurlTimeout(CURL * const curlSessionP ATTR_UNUSED,
unsigned int const timeout ATTR_UNUSED) {
#if HAVE_CURL_NOSIGNAL
unsigned int const timeoutMs = (timeout + 999)/1000;
curl_easy_setopt(curlSessionP, CURLOPT_NOSIGNAL, 1);
assert((long)timeoutMs == (int)timeoutMs);
/* Calling requirement */
curl_easy_setopt(curlSessionP, CURLOPT_TIMEOUT, (long)timeoutMs);
#else
abort();
#endif
}
static void
assertConstantsMatch(void) {
/*----------------------------------------------------------------------------
There are some constants that we define as part of the Xmlrpc-c
interface that are identical to constants in the Curl interface to
make curl option setting work. This function asserts such
formally.
-----------------------------------------------------------------------------*/
assert(XMLRPC_SSLVERSION_DEFAULT == CURL_SSLVERSION_DEFAULT);
assert(XMLRPC_SSLVERSION_TLSv1 == CURL_SSLVERSION_TLSv1);
assert(XMLRPC_SSLVERSION_SSLv2 == CURL_SSLVERSION_SSLv2);
assert(XMLRPC_SSLVERSION_SSLv3 == CURL_SSLVERSION_SSLv3);
}
static void
setupCurlSession(xmlrpc_env * const envP,
curlTransaction * const curlTransactionP,
xmlrpc_mem_block * const callXmlP,
xmlrpc_mem_block * const responseXmlP,
const xmlrpc_server_info * const serverInfoP,
const char * const userAgent,
const struct curlSetup * const curlSetupP) {
/*----------------------------------------------------------------------------
Set up the Curl session for the transaction *curlTransactionP so that
a subsequent curl_easy_perform() would perform said transaction.
The data curl_easy_perform() would send for that transaction would
be the contents of *callXmlP; the data curl_easy_perform() gets back
would go into *responseXmlP.
*serverInfoP tells what sort of authentication to set up. This is
an embarassment, as the xmlrpc_server_info type is part of the
Xmlrpc-c interface. Some day, we need to replace this with a type
(probably identical) not tied to Xmlrpc-c.
-----------------------------------------------------------------------------*/
CURL * const curlSessionP = curlTransactionP->curlSessionP;
assertConstantsMatch();
/* A Curl session is serial -- it processes zero or one transaction
at a time. We use the "private" attribute of the Curl session to
indicate which transaction it is presently processing. This is
important when the transaction finishes, because libcurl will just
tell us that something finished on a particular session, not that
a particular transaction finished.
*/
curl_easy_setopt(curlSessionP, CURLOPT_PRIVATE, curlTransactionP);
curl_easy_setopt(curlSessionP, CURLOPT_POST, 1);
curl_easy_setopt(curlSessionP, CURLOPT_URL, curlTransactionP->serverUrl);
XMLRPC_MEMBLOCK_APPEND(char, envP, callXmlP, "\0", 1);
if (!envP->fault_occurred) {
curl_easy_setopt(curlSessionP, CURLOPT_POSTFIELDS,
XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP));
curl_easy_setopt(curlSessionP, CURLOPT_WRITEFUNCTION, collect);
curl_easy_setopt(curlSessionP, CURLOPT_FILE, responseXmlP);
curl_easy_setopt(curlSessionP, CURLOPT_HEADER, 0);
curl_easy_setopt(curlSessionP, CURLOPT_ERRORBUFFER,
curlTransactionP->curlError);
if (curlTransactionP->progress) {
curl_easy_setopt(curlSessionP, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curlSessionP, CURLOPT_PROGRESSFUNCTION,
curlProgress);
curl_easy_setopt(curlSessionP, CURLOPT_PROGRESSDATA,
curlTransactionP);
} else
curl_easy_setopt(curlSessionP, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curlSessionP, CURLOPT_SSL_VERIFYPEER,
curlSetupP->sslVerifyPeer);
curl_easy_setopt(curlSessionP, CURLOPT_SSL_VERIFYHOST,
curlSetupP->sslVerifyHost ? 2 : 0);
if (curlSetupP->networkInterface)
curl_easy_setopt(curlSessionP, CURLOPT_INTERFACE,
curlSetupP->networkInterface);
if (curlSetupP->sslCert)
curl_easy_setopt(curlSessionP, CURLOPT_SSLCERT,
curlSetupP->sslCert);
if (curlSetupP->sslCertType)
curl_easy_setopt(curlSessionP, CURLOPT_SSLCERTTYPE,
curlSetupP->sslCertType);
if (curlSetupP->sslCertPasswd)
curl_easy_setopt(curlSessionP, CURLOPT_SSLCERTPASSWD,
curlSetupP->sslCertPasswd);
if (curlSetupP->sslKey)
curl_easy_setopt(curlSessionP, CURLOPT_SSLKEY,
curlSetupP->sslKey);
if (curlSetupP->sslKeyType)
curl_easy_setopt(curlSessionP, CURLOPT_SSLKEYTYPE,
curlSetupP->sslKeyType);
if (curlSetupP->sslKeyPasswd)
curl_easy_setopt(curlSessionP, CURLOPT_SSLKEYPASSWD,
curlSetupP->sslKeyPasswd);
if (curlSetupP->sslEngine)
curl_easy_setopt(curlSessionP, CURLOPT_SSLENGINE,
curlSetupP->sslEngine);
if (curlSetupP->sslEngineDefault)
/* 3rd argument seems to be required by some Curl */
curl_easy_setopt(curlSessionP, CURLOPT_SSLENGINE_DEFAULT, 1l);
if (curlSetupP->sslVersion != XMLRPC_SSLVERSION_DEFAULT)
curl_easy_setopt(curlSessionP, CURLOPT_SSLVERSION,
curlSetupP->sslVersion);
if (curlSetupP->caInfo)
curl_easy_setopt(curlSessionP, CURLOPT_CAINFO,
curlSetupP->caInfo);
if (curlSetupP->caPath)
curl_easy_setopt(curlSessionP, CURLOPT_CAPATH,
curlSetupP->caPath);
if (curlSetupP->randomFile)
curl_easy_setopt(curlSessionP, CURLOPT_RANDOM_FILE,
curlSetupP->randomFile);
if (curlSetupP->egdSocket)
curl_easy_setopt(curlSessionP, CURLOPT_EGDSOCKET,
curlSetupP->egdSocket);
if (curlSetupP->sslCipherList)
curl_easy_setopt(curlSessionP, CURLOPT_SSL_CIPHER_LIST,
curlSetupP->sslCipherList);
if (curlSetupP->timeout)
setCurlTimeout(curlSessionP, curlSetupP->timeout);
{
const char * authHdrValue;
/* NULL means we don't have to construct an explicit
Authorization: header. non-null means we have to
construct one with this as its value.
*/
setupAuth(envP, curlSessionP, serverInfoP, &authHdrValue);
if (!envP->fault_occurred) {
struct curl_slist * headerList;
createCurlHeaderList(envP, authHdrValue, userAgent,
&headerList);
if (!envP->fault_occurred) {
curl_easy_setopt(
curlSessionP, CURLOPT_HTTPHEADER, headerList);
curlTransactionP->headerList = headerList;
}
if (authHdrValue)
xmlrpc_strfree(authHdrValue);
}
}
}
}
void
curlTransaction_create(xmlrpc_env * const envP,
CURL * const curlSessionP,
const xmlrpc_server_info * const serverP,
xmlrpc_mem_block * const callXmlP,
xmlrpc_mem_block * const responseXmlP,
const char * const userAgent,
const struct curlSetup * const curlSetupStuffP,
void * const userContextP,
curlt_finishFn * const finish,
curlt_progressFn * const progress,
curlTransaction ** const curlTransactionPP) {
curlTransaction * curlTransactionP;
MALLOCVAR(curlTransactionP);
if (curlTransactionP == NULL)
xmlrpc_faultf(envP, "No memory to create Curl transaction.");
else {
curlTransactionP->finish = finish;
curlTransactionP->curlSessionP = curlSessionP;
curlTransactionP->userContextP = userContextP;
curlTransactionP->progress = progress;
curlTransactionP->serverUrl = strdup(serverP->serverUrl);
if (curlTransactionP->serverUrl == NULL)
xmlrpc_faultf(envP, "Out of memory to store server URL.");
else {
setupCurlSession(envP, curlTransactionP,
callXmlP, responseXmlP,
serverP, userAgent,
curlSetupStuffP);
if (envP->fault_occurred)
xmlrpc_strfree(curlTransactionP->serverUrl);
}
if (envP->fault_occurred)
free(curlTransactionP);
}
*curlTransactionPP = curlTransactionP;
}
void
curlTransaction_destroy(curlTransaction * const curlTransactionP) {
curl_slist_free_all(curlTransactionP->headerList);
xmlrpc_strfree(curlTransactionP->serverUrl);
free(curlTransactionP);
}
static void
interpretCurlEasyError(const char ** const descriptionP,
CURLcode const code) {
#if HAVE_CURL_STRERROR
*descriptionP = strdup(curl_easy_strerror(code));
#else
xmlrpc_asprintf(descriptionP, "Curl error code (CURLcode) %d", code);
#endif
}
void
curlTransaction_getError(curlTransaction * const curlTransactionP,
xmlrpc_env * const envP) {
if (curlTransactionP->result != CURLE_OK) {
/* We've seen Curl just return a null string for an explanation
(e.g. when TCP connect() fails because IP address doesn't exist).
*/
const char * explanation;
if (strlen(curlTransactionP->curlError) == 0)
interpretCurlEasyError(&explanation, curlTransactionP->result);
else
xmlrpc_asprintf(&explanation, "%s", curlTransactionP->curlError);
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_NETWORK_ERROR, "libcurl failed to execute the "
"HTTP POST transaction. %s", explanation);
xmlrpc_strfree(explanation);
} else {
CURLcode res;
long http_result;
res = curl_easy_getinfo(curlTransactionP->curlSessionP,
CURLINFO_HTTP_CODE, &http_result);
if (res != CURLE_OK)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Curl performed the HTTP POST request, but was "
"unable to say what the HTTP result code was. "
"curl_easy_getinfo(CURLINFO_HTTP_CODE) says: %s",
curlTransactionP->curlError);
else {
if (http_result != 200)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_NETWORK_ERROR,
"HTTP response code is %ld, not 200",
http_result);
}
}
}
void
curlTransaction_finish(xmlrpc_env * const envP,
curlTransaction * const curlTransactionP,
CURLcode const result) {
curlTransactionP->result = result;
if (curlTransactionP->finish)
curlTransactionP->finish(envP, curlTransactionP->userContextP);
}
CURL *
curlTransaction_curlSession(curlTransaction * const curlTransactionP) {
return curlTransactionP->curlSessionP;
}

View File

@@ -0,0 +1,106 @@
#ifndef CURLTRANSACTION_H_INCLUDED
#define CURLTRANSACTION_H_INCLUDED
#include "bool.h"
#include "xmlrpc-c/util.h"
#include "xmlrpc-c/client.h"
#include <curl/curl.h>
typedef struct curlTransaction curlTransaction;
typedef void curlt_finishFn(xmlrpc_env * const, void * const);
typedef void curlt_progressFn(void * const, bool * const);
struct curlSetup {
/* This is all client transport properties that are implemented as
simple Curl session properties (i.e. the transport basically just
passes them through to Curl without looking at them).
People occasionally want to replace all this with something where
the Xmlrpc-c user simply does the curl_easy_setopt() call and this
code need not know about all these options. Unfortunately, that's
a significant modularity violation. Either the Xmlrpc-c user
controls the Curl object or he doesn't. If he does, then he
shouldn't use libxmlrpc_client -- he should just copy some of this
code into his own program. If he doesn't, then he should never see
the Curl library.
Speaking of modularity: the only reason this is a separate struct
is to make the code easier to manage. Ideally, the fact that these
particular properties of the transport are implemented by simple
Curl session setup would be known only at the lowest level code
that does that setup.
*/
const char * networkInterface;
/* This identifies the network interface on the local side to
use for the session. It is an ASCIIZ string in the form
that the Curl recognizes for setting its CURLOPT_INTERFACE
option (also the --interface option of the Curl program).
E.g. "9.1.72.189" or "giraffe-data.com" or "eth0".
It isn't necessarily valid, but it does have a terminating NUL.
NULL means we have no preference.
*/
bool sslVerifyPeer;
/* In an SSL connection, we should authenticate the server's SSL
certificate -- refuse to talk to him if it isn't authentic.
This is equivalent to Curl's CURLOPT_SSL_VERIFY_PEER option.
*/
bool sslVerifyHost;
/* In an SSL connection, we should verify that the server's
certificate (independently of whether the certificate is
authentic) indicates the host name that is in the URL we
are using for the server.
*/
const char * sslCert;
const char * sslCertType;
const char * sslCertPasswd;
const char * sslKey;
const char * sslKeyType;
const char * sslKeyPasswd;
const char * sslEngine;
bool sslEngineDefault;
unsigned int sslVersion;
const char * caInfo;
const char * caPath;
const char * randomFile;
const char * egdSocket;
const char * sslCipherList;
unsigned int timeout;
/* 0 = no Curl timeout. This is in milliseconds. */
};
void
curlTransaction_create(xmlrpc_env * const envP,
CURL * const curlSessionP,
const xmlrpc_server_info * const serverP,
xmlrpc_mem_block * const callXmlP,
xmlrpc_mem_block * const responseXmlP,
const char * const userAgent,
const struct curlSetup * const curlSetupStuffP,
void * const userContextP,
curlt_finishFn * const finish,
curlt_progressFn * const progress,
curlTransaction ** const curlTransactionPP);
void
curlTransaction_destroy(curlTransaction * const curlTransactionP);
void
curlTransaction_finish(xmlrpc_env * const envP,
curlTransaction * const curlTransactionP,
CURLcode const result);
void
curlTransaction_getError(curlTransaction * const curlTransactionP,
xmlrpc_env * const envP);
CURL *
curlTransaction_curlSession(curlTransaction * const curlTransactionP);
#endif

View File

@@ -0,0 +1,20 @@
#ifndef CURLVERSION_H_INCLUDED
#define CURLVERSION_H_INCLUDED
#define CMAJOR LIBCURL_VERSION_MAJOR
#define CMINOR LIBCURL_VERSION_MINOR
#if CMAJOR > 7 || (CMAJOR == 7 && CMINOR >= 10)
#define HAVE_CURL_NOSIGNAL 1
#else
#define HAVE_CURL_NOSIGNAL 0
#endif
#if CMAJOR > 7 || (CMAJOR == 7 && CMINOR >= 12)
#define HAVE_CURL_STRERROR 1
#else
#define HAVE_CURL_STRERROR 0
#endif
#undef CMAJOR
#undef CMINOR
#endif

View File

@@ -0,0 +1,24 @@
#ifndef CURL_LOCK_H_INCLUDED
#define CURL_LOCK_H_INCLUDED
#include <pthread.h>
typedef struct lock lock;
typedef void lockAcquireFn(lock *);
typedef void lockReleaseFn(lock *);
typedef void lockDestroyFn(lock *);
struct lock {
/* To finish the job of making an abstract lock class that can use locks
other than pthread mutexes, we need to replace 'theLock' with a
"void * implementationP" and make curlLock_create_pthread() malloc
the mutex.
*/
pthread_mutex_t theLock;
lockAcquireFn * acquire;
lockReleaseFn * release;
lockDestroyFn * destroy;
};
#endif

View File

@@ -0,0 +1,49 @@
#include <stdlib.h>
#include "mallocvar.h"
#include "pthreadx.h"
#include "lock.h"
#include "lock_pthread.h"
static lockAcquireFn acquire;
static void
acquire(struct lock * const lockP) {
pthread_mutex_lock(&lockP->theLock);
}
static lockReleaseFn release;
static void
release(struct lock * const lockP) {
pthread_mutex_unlock(&lockP->theLock);
}
static lockDestroyFn destroy;
static void
destroy(struct lock * const lockP) {
pthread_mutex_destroy(&lockP->theLock);
free(lockP);
}
struct lock *
curlLock_create_pthread(void) {
struct lock * lockP;
MALLOCVAR(lockP);
if (lockP) {
pthread_mutex_init(&lockP->theLock, NULL);
lockP->acquire = &acquire;
lockP->release = &release;
lockP->destroy = &destroy;
}
return lockP;
}

View File

@@ -0,0 +1,9 @@
#ifndef CURL_LOCK_PTHREAD_H_INCLUDED
#define CURL_LOCK_PTHREAD_H_INCLUDED
#include "lock.h"
lock *
curlLock_create_pthread(void);
#endif

File diff suppressed because it is too large Load Diff