mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
Copying ede-bug-report from branches in trunk
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user