Hi All,
Can I use CURL in multi threaded application?
My Requirement is as follows:
I have to post the data to the server. suppose if the connection is broken
with the server, i need to store the data and post it when it comes online.
So this activity will happen on back ground (on a separate thread). My main
thread will keep trying to post the data normally. My other thread will
check the connection with the host and post the data.
To test this scenario, I have written the sample code. To simulate this
scenirio, during running of the application i am pulling my ethernet cable
so that i can simulate the connection break down case. Both threads giving
successful response till i pulled the cable. Once i pull the cable and
connect it back, one of the threads which is checking the connection is
keep getting CURLE_COULDNT_RESOLVE_HOST error even though other thread is
successful in posting the data to the host.
Please refer to attached sample code(curlLibraryTest.c):
In the Sample code, we have main thread and waitForHostConnection thread.
Main thread will keep posting the data to the host at regular intervals.
waitForHostConnection thread will keep checking the host connection.
Both threads are working fine and giving succesful response till i pulled
the ethernet cable, once i pulled and put the cable back,
waitForHostConnection thread is giving CURLE_COULDNT_RESOLVE_HOST error.
When i am doing both things posting and checking connection in the same
thread(main) then it works fine even after pulling and connecting the cable
back. When i move connection checking part to the separate thread then the
problem starts.
Could anyone help me to resolve this.
Thank you for your time and help.
Regards
Praveen
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <syslog.h>
#include <pthread.h>
#define LOGS "/mnt/flash/logs/usr1/curlTestApp.log"
#define TRUE 1
#define FALSE 0
# define SUCCESS 0
# define FAILURE -1
typedef enum
{
PRIMARY_URL = 0,
SECONDARY_URL
}
HOST_URL_ENUM;
static pthread_t threadID;
#define CA_CERT_FILE "ca-bundle.crt"
FILE* fptr = NULL;
FILE* fptr2 = NULL;
/*
* ----------------------------------------------------------------------------
* Some certificate information:
*
* PEM Format: .pem, .crt, .cer and .key
*
* DER Format:
* ----------------------------------------------------------------------------
*/
static struct curl_slist * headers = NULL;
char szMsg[256] = "";
typedef struct
{
int iTotSize;
int iWritten;
char * szMsg;
}
RESP_DATA_STYPE, * RESP_DATA_PTYPE;
static int initSSICURLHandle(CURL **, char*);
static void commonInit(CURL *, int);
static int sendDataToHost(CURL *, char *, int, char **, int *);
static size_t saveResponse(void *, size_t, size_t, void *);
static int initializeRespData(RESP_DATA_PTYPE);
static void* waitForHostConnection(void *);
static int chkConn(CURL *);
int main(int argc, char *argv[])
{
int iRetVal = SUCCESS;
int rv = SUCCESS;
int iCnt = 0;
char* szHostUrl = "https://sample.com";
CURL * curlHandle1 = NULL;
CURL * curlHandle2 = NULL;
char req[2560] = "Sample Data";
int reqSize = strlen(req);
char* resp = NULL;
int respSize = 0;
int bIsHostWentOffline = FALSE;
fptr = fopen (LOGS, "w");
/* Add data to the headers */
headers = curl_slist_append(headers, "Content-Type:text/xml");
fprintf(fptr, "%s: Entered..\n", __FUNCTION__);
fprintf (fptr, "%s: Initializing the curl handle for the first one\n", __FUNCTION__);
fclose(fptr);
rv = initSSICURLHandle(&curlHandle1, szHostUrl);
rv = pthread_create(&threadID, NULL, waitForHostConnection, &bIsHostWentOffline);
if(rv != SUCCESS)
{
fprintf(fptr, "%s: FAILED to start waitForHostConnection thread",
__FUNCTION__);
}
while(1)
{
fptr = fopen (LOGS, "a");
fprintf (fptr, "%s: Posting data to first Curl\n", __FUNCTION__, rv);
/*
* CURL Handler was initialized successfully
*/
fprintf(fptr, "Request: %s\n", req);
rv = sendDataToHost(curlHandle1, req, reqSize, &resp, &respSize);
if(rv != SUCCESS)
{
fprintf(fptr, "%s: Failed to post data to server\n", __FUNCTION__);
curl_easy_cleanup(curlHandle1);
curlHandle1 = NULL;
rv = initSSICURLHandle(&curlHandle1, szHostUrl);
if(rv == SUCCESS)
{
fprintf(fptr, "%s: Timed out Error from PWC\n", __FUNCTION__);
fprintf (fptr,"%s: Reinitialized the connection\n", __FUNCTION__);
}
}
if (resp != NULL)
{
fprintf (fptr, "Response Received: %s \n", resp);
free(resp);
resp = NULL;
}
sleep(3);
fclose(fptr);
}
fprintf(fptr, "%s: Returning..\n", __FUNCTION__);
return rv;
}
/*
* ============================================================================
* Function Name: initSSICURLHandle
*
* Description :
*
* Input Params :
*
* Output Params: SUCCESS / FAILURE
* ============================================================================
*/
static int initSSICURLHandle(CURL ** handle, char* pszHostUrl)
{
int rv = SUCCESS;
char szTmpURL[100] = "";
CURL * locHandle = NULL;
int certValdReq = TRUE;
fprintf(fptr, "%s: --- enter ---\n", __FUNCTION__);
while(1)
{
/* Initialize the handle using the CURL library */
locHandle = curl_easy_init();
if(locHandle == NULL)
{
fprintf(fptr, "%s: Curl initialization for handle failed\n",
__FUNCTION__);
rv = FAILURE;
break;
}
curl_easy_setopt(locHandle, CURLOPT_URL, pszHostUrl);
fprintf(fptr, "%s: Setting PWC URL = [%s]\n", __FUNCTION__, pszHostUrl);
/* Set the connection timeout */
curl_easy_setopt(locHandle, CURLOPT_CONNECTTIMEOUT, 10);
/* Set the total timeout */
curl_easy_setopt(locHandle, CURLOPT_TIMEOUT, 20);
/* Set the other common features */
commonInit(locHandle, certValdReq);
*handle = locHandle;
break;
}
fprintf(fptr, "%s: Returning [%d]\n", __FUNCTION__, rv);
return rv;
}
/*
* ============================================================================
* Function Name: commonInit
*
* Description : This function contains the code for common initialization of
* all the three handles needed for communication with the PWC
*
* Input Params : CURL * handle -> curl handle that needs to be configured
*
* Output Params: void
* ============================================================================
*/
static void commonInit(CURL * handle, int certValdReq)
{
fprintf(fptr, "%s: --- enter ---\n", __FUNCTION__);
/* Set the NO SIGNAL option, This option is very important to set in case
* of multi-threaded applications like ours, because otherwise the libcurl
* uses signal handling for the communication and that causes the threads
* to go crazy and even crash. Fix done for issue 1659 (Church of LDS AmDocs
* Case# 130826-3983) */
curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
/* Set the HTTP post option */
curl_easy_setopt(handle, CURLOPT_POST, 1L);
/* Add the headers */
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
/*#ifdef DEBUG
/* Add the debug function
curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curlDbgFunc);
#endif*/
if (certValdReq == TRUE)
{
fprintf(fptr, "%s: Url starts with https. Doing certificate validation\n", __FUNCTION__);
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 2L);
}
/* Set the CA certificate */
curl_easy_setopt(handle, CURLOPT_CAINFO, CA_CERT_FILE);
/* Set the write function */
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, saveResponse);
/* Set the detection as TRUE for HTTP errors */
curl_easy_setopt(handle, CURLOPT_FAILONERROR, TRUE);
fprintf(fptr, "%s: --- returning ---\n", __FUNCTION__);
return;
}
/*
* ============================================================================
* Function Name: sendDataToHost
*
* Description : This function sends the XML data to the PWC server, using the
* appropriate CURL handle.
*
* Input Params :
* PWC_URL_TYPE_ENUM urlType -> constant telling which handle to
* use
* unsigned char * data -> the XML data that needs to be sent
* int size -> size of the data
*
* Output Params: SUCCESS / FAILURE
* ============================================================================
*/
static int sendDataToHost(CURL * curlHandle, char * req, int reqSize,
char ** resp, int * respSize)
{
int rv = SUCCESS;
char szCurlErrBuf[4096] = "";
RESP_DATA_STYPE stRespData;
#ifdef DEVDEBUG
char fptr[4096+256] = "";
#elif DEBUG
char fptr[256] = "";
#endif
fprintf(fptr, "%s: --- enter ---\n", __FUNCTION__);
while(1)
{
/* Initialize the response data */
rv = initializeRespData(&stRespData);
if(rv != SUCCESS)
{
fprintf(fptr, "%s: Initialization of response data FAILED\n"
, __FUNCTION__);
break;
}
/* Set some curl library options before sending the data */
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &stRespData);
curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, (void *) req);
curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDSIZE, reqSize);
curl_easy_setopt(curlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
fprintf(fptr, "%s: Posting the data to the server\n", __FUNCTION__);
/* post the data to the server */
rv = curl_easy_perform(curlHandle);
fprintf(fptr, "%s: curl_easy_perform done, rv = %d\n", __FUNCTION__, rv);
if(rv == CURLE_OK)
{
*resp = stRespData.szMsg;
*respSize = stRespData.iWritten;
fprintf(fptr, "Data Posted Succesfully\n");
fprintf(fptr, "%s: Response Len = [%d]\n", __FUNCTION__,
stRespData.iWritten);
}
else
{
switch(rv)
{
case CURLE_UNSUPPORTED_PROTOCOL: /* 1 */
/*
* The URL you passed to libcurl used a protocol that this libcurl does not support.
* The support might be a compile-time option that you didn't use,
* it can be a misspelled protocol string or just a protocol libcurl has no code for.
*/
fprintf(fptr, "%s: protocol not supported by library\n",
__FUNCTION__);
break;
case CURLE_URL_MALFORMAT: /* 3 */
/*
* The URL was not properly formatted.
*/
fprintf(fptr, "%s: URL not properly formatted\n",
__FUNCTION__);
break;
case CURLE_COULDNT_RESOLVE_HOST: /* 6 */
/*
* Couldn't resolve host. The given remote host was not resolved.
*/
fprintf(fptr, "%s: Couldnt resolve server's address\n", __FUNCTION__);
break;
case CURLE_COULDNT_CONNECT: /* 7 */
/*
* Failed to connect() to host or proxy.
*/
fprintf(fptr, "%s: Failed to connect server\n",
__FUNCTION__);
break;
case CURLE_HTTP_RETURNED_ERROR: /* 22 */
/*
* This is returned if CURLOPT_FAILONERROR is set TRUE
* and the HTTP server returns an error code that is >= 400.
*/
fprintf(fptr, "%s: Got HTTP error while posting\n",
__FUNCTION__);
break;
case CURLE_WRITE_ERROR: /* 23 */
/*
* An error occurred when writing received data to a local file,
* or an error was returned to libcurl from a write callback.
*/
fprintf(fptr, "%s: Failed to save the response data\n",
__FUNCTION__);
rv = FAILURE;
break;
case CURLE_OUT_OF_MEMORY: /* 27 */
/*
* A memory allocation request failed.
* This is serious badness and things are severely screwed up if this ever occurs.
*/
fprintf(fptr, "%s: Facing memory shortage \n",
__FUNCTION__);
rv = FAILURE;
break;
case CURLE_OPERATION_TIMEDOUT: /* 28 */
/*
* Operation timeout.
* The specified time-out period was reached according to the conditions.
*/
fprintf(fptr, "%s: Timeout happened while receiving\n",
__FUNCTION__);
break;
case CURLE_SSL_CONNECT_ERROR: /* 35 */
/*
* A problem occurred somewhere in the SSL/TLS handshake.
*
*/
fprintf(fptr, "%s: SSL/TLS Handshake FAILED\n",
__FUNCTION__);
break;
case CURLE_PEER_FAILED_VERIFICATION: /* 51 */
/*
* The remote server's SSL certificate or SSH md5 fingerprint was deemed not OK.
*/
fprintf(fptr, "%s: Server's SSL certificate verification FAILED\n",
__FUNCTION__);
break;
case CURLE_SEND_ERROR: /* 55 */
/*
* Failed sending network data.
*/
fprintf(fptr, "%s: Failed to post the request data\n", __FUNCTION__);
break;
case CURLE_RECV_ERROR: /* 56 */
/*
* Failure with receiving network data.
*/
fprintf(fptr, "%s: Failed to receive the response\n",
__FUNCTION__);
break;
case CURLE_SSL_CACERT: /* 60 */
/*
* Peer certificate cannot be authenticated with known CA certificates.
*/
fprintf(fptr,
"%s: Couldnt authenticate peer certificate\n",
__FUNCTION__);
break;
case CURLE_SSL_CACERT_BADFILE: /* 77 */
/*
* Problem with reading the SSL CA cert (path? access rights?)
*/
fprintf(fptr, "%s: Cant find SSL CA sertificate [%s]\n",
__FUNCTION__, CA_CERT_FILE);
break;
default:
fprintf(fptr, "%s: Error [%d] occured while posting\n",
__FUNCTION__, rv);
break;
}
// rv = FAILURE;
/* Deallocate the allocated memory */
if(stRespData.szMsg != NULL)
{
free(stRespData.szMsg);
stRespData.szMsg = NULL;
}
fprintf(fptr, "%s: Lib Curl Error [%s]\n",__FUNCTION__, szCurlErrBuf);
}
break;
}
fprintf(fptr, "%s: Returning [%d]\n", __FUNCTION__, rv);
return rv;
}
/*
* ============================================================================
* Function Name: saveResponse
*
* Description : This function performs the job of saving the XML response
* coming from the server into a buffer provided as parameter.
* This function is passed to the CURL library, and the
* library would call this function, not us.
*
* Input Params :
* void * ptr -> pointer to the data that needs to be copied
* size_t size-> size of one element of data
* size_t cnt -> count of data members
* void * des -> our buffer where the response is saved
*
* Output Params: totLen -> length of data saved
* ============================================================================
*/
static size_t saveResponse(void * ptr, size_t size, size_t cnt, void * des)
{
int rv = SUCCESS;
int iSizeLeft = 0;
int totLen = 0;
int iReqdLen = 0;
char * cTmpPtr = NULL;
RESP_DATA_PTYPE respPtr = NULL;
fprintf(fptr, "%s: --- enter ---\n", __FUNCTION__);
while(1)
{
respPtr = (RESP_DATA_PTYPE) des;
totLen = size * cnt;
iSizeLeft = respPtr->iTotSize - respPtr->iWritten;
if(totLen > iSizeLeft)
{
iReqdLen = (respPtr->iTotSize + totLen - iSizeLeft) + 1/* one extra buffer for NULL at the end for safety purpose*/;
cTmpPtr = (char *) realloc(respPtr->szMsg, iReqdLen);
if(cTmpPtr == NULL)
{
fprintf(fptr, "%s: Reallocation of memory FAILED\n",
__FUNCTION__);
rv = FAILURE;
break;
}
memset(cTmpPtr + respPtr->iWritten, 0x00, iReqdLen - respPtr->iWritten);
respPtr->iTotSize = iReqdLen;
respPtr->szMsg = cTmpPtr;
}
memcpy(respPtr->szMsg + respPtr->iWritten, ptr, totLen);
respPtr->iWritten += totLen;
rv = totLen;
break;
}
fprintf(fptr, "%s: Returning [%d]\n", __FUNCTION__, rv);
return rv;
}
/*
* ============================================================================
* Function Name: initializeRespData
*
* Description : This function is used for the initial setting of the response
* data structure which would then be used for storing XML
* response retrieved from the server being contacted.
*
* Input Params : RESP_DATA_PTYPE stRespPtr -> pointer to response structure
*
* Output Params: SUCCESS / FAILURE
* ============================================================================
*/
static int initializeRespData(RESP_DATA_PTYPE stRespPtr)
{
int rv = SUCCESS;
char * cTmpPtr = NULL;
#ifdef DEBUG
char fptr[128] = "";
#endif
fprintf(fptr, "%s: --- enter ---\n", __FUNCTION__);
cTmpPtr = (char *) malloc(4096 * sizeof(char));
if(cTmpPtr != NULL)
{
/* Initialize the memory */
memset(cTmpPtr, 0x00, 4096 * sizeof(char));
memset(stRespPtr, 0x00, sizeof(RESP_DATA_STYPE));
/* Assign the data */
stRespPtr->iTotSize = 4096;
stRespPtr->iWritten = 0;
stRespPtr->szMsg = cTmpPtr;
}
else
{
fprintf(fptr, "%s: Memory allocation FAILED\n", __FUNCTION__);
rv = FAILURE;
}
fprintf(fptr, "%s: Returning [%d]\n", __FUNCTION__, rv);
return rv;
}
/*
* ============================================================================
* Function Name: waitForHostConnection
*
* Description :
*
* Input Params :
*
* Output Params:
* ============================================================================
*/
static void* waitForHostConnection(void *arg)
{
int iRetVal = SUCCESS;
int *bHostWentOffline;
bHostWentOffline = (int*)arg;
//
/* if(iSAFInterval == 0)
{
iSAFInterval = getSAFPingInterval();
fprintf(fptr, "%s: SAF Host connection check Interval is %d", __FUNCTION__, iSAFInterval);
lSAFIntervalMillisecs = iSAFInterval * 60 * 1000;
}
*/
/* fprintf(fptr, "%s: SAF Host connection check waiting time is %ld in millisecs", __FUNCTION__, lSAFIntervalMillisecs);
*/
while(1)
{
fptr2 = fopen("./flash/threadlogs.txt", "a");
if(isHostConnected() == TRUE)
{
fprintf(fptr2, "%s: Host connection is available\n",__FUNCTION__);
break;
}
fprintf(fptr2, "%s: Host connection is not available, checking once more...\n",__FUNCTION__);
//Set veriable to indicate host went offline.
*bHostWentOffline = TRUE;
sleep(3); //Will check the connection part after the given interval of time
fclose(fptr2);
}
// fprintf(fptr2, "%s: Returning [%d]\n", __FUNCTION__, iRetVal);
}
/*
* ============================================================================
* Function Name: isHostConnected
*
* Description :
*
* Input Params : none
*
* Output Params: TRUE / FALSE
* ============================================================================
*/
int isHostConnected()
{
int bRv = TRUE;
fprintf(fptr2, "%s: --- enter ---\n", __FUNCTION__);
if(SUCCESS == checkHostConn())
{
bRv = TRUE;
}
else
{
bRv = FALSE;
}
fprintf(fptr2, "%s: Returning [%s]\n", __FUNCTION__,
(bRv == TRUE)? "TRUE" : "FALSE");
return bRv;
}
/*
* ============================================================================
* Function Name: checkHostConn
*
* Description :
*
* Input Params :
*
* Output Params: SUCCESS / FAILURE
* ============================================================================
*/
int checkHostConn()
{
int rv = SUCCESS;
int iCnt = 0;
static int host = PRIMARY_URL;
CURL * curlHandle = NULL;
char* szHostUrl = "https://sample.com";
#ifdef DEBUG
char szDbgMsg[512] = "";
#endif
fprintf(fptr2, "%s: --- enter ---\n", __FUNCTION__);
while(iCnt < 2)
{
rv = initSSICURLHandle(&curlHandle, szHostUrl);
if(rv == SUCCESS)
{
/* Curl handler initialized successfully
* Checking connection */
rv = chkConn(curlHandle);
if(rv != SUCCESS)
{
fprintf(fptr2, "%s: FAILED to check Host connection\n",
__FUNCTION__);
}
else
{
fprintf(fptr2, "%s: Host connection available\n", __FUNCTION__);
}
}
if(rv == SUCCESS)
{
fprintf(fptr2, "%s: Breaking from the loop\n", __FUNCTION__);
break;
}
/* If the first host is not working then try the next host */
host ^= 1;
iCnt++;
fprintf(fptr2, "%s: Trying again..\n", __FUNCTION__);
}
fprintf(fptr2, "%s: Returning [%d]\n", __FUNCTION__, rv);
return rv;
}
/*
* ============================================================================
* Function Name: chkConn
*
* Description :
*
* Input Params :
*
*
* Output Params: SUCCESS / FAILURE
* ============================================================================
*/
static int chkConn(CURL * curlHandle)
{
int rv = SUCCESS;
long lCode = 0L;
#ifdef DEBUG
char szDbgMsg[256] = "";
#endif
fprintf(fptr2, "%s: --- enter ---\n", __FUNCTION__);
while(1)
{
curl_easy_setopt(curlHandle, CURLOPT_POST, 0L);
curl_easy_setopt(curlHandle, CURLOPT_NOBODY, 1);
curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, 1);
rv = curl_easy_perform(curlHandle);
if(rv == CURLE_OK)
{
fprintf(fptr2, "%s: Perform SUCCESS\n", __FUNCTION__);
}
else
{
fprintf(fptr2, "%s: Return Value from PERFORM = [%d]\n",
__FUNCTION__, rv);
}
rv = curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &lCode);
fprintf(fptr2, "%s: Return Value RESPONSE CODE = [%d], [%ld]\n",
__FUNCTION__, rv, lCode);
if(rv == 0 && lCode == 200)
{
fprintf(fptr2, "%s: Got Successful respcode from host\n",
__FUNCTION__);
rv = SUCCESS;
}
else
{
fprintf(fptr2, "%s: Didnt get the good resp code from host\n"
, __FUNCTION__);
rv = FAILURE;
}
break;
}
if(curlHandle != NULL)
{
curl_easy_cleanup(curlHandle);
}
fprintf(fptr2, "%s: Returning [%d]\n", __FUNCTION__, rv);
return rv;
}
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html