1 /* ****************************************************************
3 * Copyright 2016 Samsung Electronics All Rights Reserved.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 ******************************************************************/
25 #include "CoapHttpParser.h"
26 #include "oic_malloc.h"
27 #include "oic_string.h"
28 #include "uarraylist.h"
32 #include <curl/curl.h>
36 #if !defined(_MSC_VER)
38 #endif //!defined(_MSC_VER)
39 #include <sys/types.h>
42 #include <sys/select.h>
43 #endif //!defined(_WIN32)
46 #define TAG "CHP_PARSER"
48 #define DEFAULT_USER_AGENT "IoTivity"
49 #define MAX_PAYLOAD_SIZE (1048576U) // 1 MB
54 CHPResponseCallback cb;
56 /* libcurl will not cache request payload when creating a easy handle hence we need to cache */
59 /* To track multiple read_callbacks from curl */
61 /* To track multiple write_callbacks from curl */
65 /* libcurl does not copy header options passed to a request */
66 struct curl_slist *list;
69 /* A curl mutihandle is not threadsafe so we require mutexes to add new easy
70 * handles to multihandle.
72 static CURLM *g_multiHandle;
73 static int g_activeConnections;
75 /* Mutex code is taken from CA.
76 * General utility functions shall be placed in common location
77 * so that all modules can use them.
79 static pthread_mutex_t g_multiHandleMutex;
81 /* Fds used to signal threads to stop */
82 static int g_shutdownFds[2];
84 static bool g_terminateParser;
87 * Fds used to signal fdset to be refreshed.
88 * When a new easy_handle is added to multi_handle,
89 * existing fd_set has to be updated.
91 static int g_refreshFds[2];
94 * Thread handle for curl multi_handle processing.
96 static pthread_t g_multiHandleThread;
98 static void CHPParserLockMutex();
99 static void CHPParserUnlockMutex();
101 static void CHPParserResetHeaderOptions(u_arraylist_t** headerOptions)
103 VERIFY_NON_NULL_VOID(headerOptions, TAG, "headerOptions is NULL");
105 HttpHeaderOption_t *option = NULL;
106 while (NULL != (option = u_arraylist_remove(*headerOptions, 0)))
110 u_arraylist_free(headerOptions);
113 static void CHPFreeContext(CHPContext_t *ctxt)
115 VERIFY_NON_NULL_VOID(ctxt, TAG, "ctxt is NULL");
118 curl_slist_free_all(ctxt->list);
123 curl_easy_cleanup(ctxt->easyHandle);
126 CHPParserResetHeaderOptions(&(ctxt->resp.headerOptions));
127 OICFree(ctxt->resp.payload);
128 OICFree(ctxt->payload);
132 static void *CHPParserExecuteMultiHandle(void* data)
134 OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
137 * These fd sets will be fetched from curl multi handle and monitored to execute
138 * curl_multi_perform()
144 struct timeval timeout;
147 int activeEasyHandle;
151 /* When active connections exist, curl_multi_perform() shall be called within
152 * curlMultiTimeout seconds regardless of whether select returned successfull or not.
154 long curlMultiTimeout;
155 while (!g_terminateParser)
157 // Required everytime before calling curl_multi_fdset()
164 // Get currently active transfer fds from curl
165 CHPParserLockMutex();
166 curl_multi_fdset(g_multiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
167 curl_multi_timeout(g_multiHandle, &curlMultiTimeout);
168 activeCon = g_activeConnections;
169 CHPParserUnlockMutex();
171 // A 0 represent curl_multi_perform() shall be called.
172 if(curlMultiTimeout < 0)
174 curlMultiTimeout = 1000;
179 /* Nothing to monitor from libcurl.
180 * This mean that no active sockets exist either because
181 * there are no transfer taking place or sockets are not in a
182 * state that could be monitored (connecting, retry etc.)
186 // wait until something received on shutdown or refresh fd
188 curlMultiTimeout = -1;
192 // libcurl recommend doing this.
194 // dont select() and directly call curl_multi_perform()
201 FD_SET(g_shutdownFds[0], &fdread);
202 if (maxfd < g_shutdownFds[0])
204 maxfd = g_shutdownFds[0];
207 FD_SET(g_refreshFds[0], &fdread);
208 if (maxfd < g_refreshFds[0])
210 maxfd = g_refreshFds[0];
213 if(curlMultiTimeout == -1)
219 timeout.tv_sec = curlMultiTimeout / 1000;
225 retValue = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, tv);
228 OIC_LOG_V(ERROR, TAG, "Error in select. %s", strerror(errno));
232 // Some sockets are available for operations, check if shutdown or refresh fds are
233 // among them. In any case, go ahead and call curl_multi_perform()
236 if (FD_ISSET(g_shutdownFds[0], &fdread))
238 OIC_LOG(ERROR, TAG, "Shutdown requested. multi_handle returning");
241 else if(FD_ISSET(g_refreshFds[0], &fdread))
244 ssize_t len = read(g_refreshFds[0], buf, sizeof(buf));
246 // new easy handles added, call multi_perform and refresh fds.
247 OIC_LOG(ERROR, TAG, "New easy handle added");
253 CHPParserLockMutex();
256 ret = curl_multi_perform(g_multiHandle, &activeEasyHandle);
257 struct CURLMsg *cmsg;
262 cmsg = curl_multi_info_read(g_multiHandle, &cmsgq);
263 if(cmsg && (cmsg->msg == CURLMSG_DONE))
265 CURL *easyHandle = cmsg->easy_handle;
266 g_activeConnections--;
267 curl_multi_remove_handle(g_multiHandle, easyHandle);
271 char *contentType = NULL;
274 curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &ptr);
275 curl_easy_getinfo(easyHandle, CURLINFO_EFFECTIVE_URL, &uri);
276 curl_easy_getinfo(easyHandle, CURLINFO_RESPONSE_CODE, &responseCode);
277 curl_easy_getinfo(easyHandle, CURLINFO_CONTENT_TYPE, &contentType);
279 ptr->resp.status = responseCode;
280 OICStrcpy(ptr->resp.dataFormat, sizeof(ptr->resp.dataFormat), contentType);
281 OIC_LOG_V(DEBUG, TAG, "Transfer completed %d uri: %s, %s", g_activeConnections,
283 ptr->cb(&(ptr->resp), ptr->context);
286 } while(cmsg && !g_terminateParser);
287 }while (ret == CURLM_CALL_MULTI_PERFORM && !g_terminateParser);
288 CHPParserUnlockMutex();
291 if (g_terminateParser)
293 OIC_LOG_V(DEBUG, TAG, "Shutdown request received.");
294 // g_shutdownFds[1] will be already closed.
295 close(g_shutdownFds[0]);
296 close(g_refreshFds[0]);
297 close(g_refreshFds[1]);
298 g_shutdownFds[0] = -1;
299 g_shutdownFds[1] = -1;
300 g_refreshFds[0] = -1;
301 g_refreshFds[1] = -1;
304 OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
308 OCStackResult CHPParserInitializePipe(int fds[2])
310 OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
314 #if defined(HAVE_PIPE2)
315 ret = pipe2(fds, O_CLOEXEC);
320 ret = fcntl(fds[0], F_GETFD);
323 ret = fcntl(fds[0], F_SETFD, ret|FD_CLOEXEC);
327 ret = fcntl(fds[1], F_GETFD);
331 ret = fcntl(fds[1], F_SETFD, ret|FD_CLOEXEC);
344 OIC_LOG_V(ERROR, TAG, "FD initialization failed: %s", strerror(errno));
345 return OC_STACK_ERROR;
347 OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
351 static OCStackResult CHPParserInitializeMutex()
353 // create the mutex with the attributes set
354 int ret = pthread_mutex_init(&g_multiHandleMutex, PTHREAD_MUTEX_DEFAULT);
357 OIC_LOG_V(ERROR, TAG, "%s Failed to initialize mutex !", __func__);
358 return OC_STACK_ERROR;
363 static OCStackResult CHPParserTerminateMutex()
365 int ret = pthread_mutex_destroy(&g_multiHandleMutex);
368 OIC_LOG_V(ERROR, TAG, "%s Failed to free mutex !", __func__);
369 return OC_STACK_ERROR;
374 static void CHPParserLockMutex()
376 int ret = pthread_mutex_lock(&g_multiHandleMutex);
379 OIC_LOG_V(ERROR, TAG, "Pthread Mutex lock failed: %d", ret);
383 static void CHPParserUnlockMutex()
385 int ret = pthread_mutex_unlock(&g_multiHandleMutex);
388 OIC_LOG_V(ERROR, TAG, "Pthread Mutex unlock failed: %d", ret);
392 static OCStackResult CHPParserInitializeMultiHandle()
394 CHPParserLockMutex();
397 OIC_LOG(ERROR, TAG, "Multi handle already initialized.");
398 CHPParserUnlockMutex();
402 g_multiHandle = curl_multi_init();
405 OIC_LOG(ERROR, TAG, "Failed to create multi handle.");
406 CHPParserUnlockMutex();
407 return OC_STACK_ERROR;
410 CHPParserUnlockMutex();
414 static OCStackResult CHPParserTerminateMultiHandle()
416 CHPParserLockMutex();
419 OIC_LOG(ERROR, TAG, "Multi handle not initialized.");
420 CHPParserUnlockMutex();
424 curl_multi_cleanup(g_multiHandle);
425 g_multiHandle = NULL;
426 CHPParserUnlockMutex();
430 OCStackResult CHPParserInitialize()
432 OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
434 OCStackResult ret = CHPParserInitializeMutex();
435 if(ret != OC_STACK_OK)
440 ret = CHPParserInitializeMultiHandle();
441 if(ret != OC_STACK_OK)
443 OIC_LOG_V(ERROR, TAG, "Failed to intialize multi handle: %d", ret);
444 CHPParserTerminate();
448 ret = CHPParserInitializePipe(g_shutdownFds);
449 if(ret != OC_STACK_OK)
451 OIC_LOG_V(ERROR, TAG, "Failed to intialize shutdown fds: %d", ret);
452 CHPParserTerminate();
456 ret = CHPParserInitializePipe(g_refreshFds);
457 if(ret != OC_STACK_OK)
459 OIC_LOG_V(ERROR, TAG, "Failed to intialize refresh fds: %d", ret);
460 CHPParserTerminate();
464 // Launch multi_handle processor thread
465 int result = pthread_create(&g_multiHandleThread, NULL, CHPParserExecuteMultiHandle, NULL);
468 OIC_LOG_V(ERROR, TAG, "Thread start failed with error %d", result);
469 CHPParserTerminate();
470 return OC_STACK_ERROR;
473 g_terminateParser = false;
474 CHPParserLockMutex();
475 g_activeConnections = 0;
476 CHPParserUnlockMutex();
477 OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
481 OCStackResult CHPParserTerminate()
483 OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
484 g_terminateParser = true;
485 if (g_shutdownFds[1] != -1)
487 // Signal multi_handle thread to come out
488 close(g_shutdownFds[1]);
490 pthread_join(g_multiHandleThread, NULL);
492 OCStackResult ret = CHPParserTerminateMultiHandle();
493 if(ret != OC_STACK_OK)
495 OIC_LOG_V(ERROR, TAG, "Multi handle termination failed: %d", ret);
498 CHPParserLockMutex();
499 g_activeConnections = 0;
500 CHPParserUnlockMutex();
502 ret = CHPParserTerminateMutex();
503 if(ret != OC_STACK_OK)
505 OIC_LOG_V(ERROR, TAG, "mutex termination failed: %d", ret);
507 OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
511 static size_t CHPEasyHandleWriteCb(char *buffer, size_t size, size_t num, void *context)
513 size_t dataToWrite = size * num;
516 // Empty payload received. Ignore.
520 if(!context || !buffer || g_terminateParser)
522 OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
526 CHPContext_t* ctx = context;
527 HttpResponse_t *resp = &(ctx->resp);
529 if(ctx->writeOffset + dataToWrite > MAX_PAYLOAD_SIZE)
531 OIC_LOG_V(ERROR, TAG, "%s Payload limit exceeded", __func__);
532 resp->payloadLength = 0;
533 ctx->writeOffset = 0;
534 OICFree(resp->payload);
535 resp->payload = NULL;
541 resp->payload = OICMalloc(dataToWrite);
544 OIC_LOG_V(ERROR, TAG, "%s Out of memory!", __func__);
551 void *newPayload = OICRealloc(resp->payload, ctx->writeOffset + dataToWrite);
554 OIC_LOG_V(ERROR, TAG, "Realloc failed! Current: %u Extra: %u", ctx->writeOffset,
556 resp->payloadLength = 0;
557 ctx->writeOffset = 0;
558 OICFree(resp->payload);
559 resp->payload = NULL;
562 resp->payload = newPayload;
565 memcpy(resp->payload + ctx->writeOffset, buffer, dataToWrite);
566 ctx->writeOffset += dataToWrite;
567 resp->payloadLength = ctx->writeOffset;
569 OIC_LOG_V(DEBUG, TAG, "%s OUT %u : %u", __func__, resp->payloadLength, dataToWrite);
573 static size_t CHPEasyHandleReadCb(char *buffer, size_t size, size_t num, void *context)
575 if(!context || !buffer || g_terminateParser)
577 OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
578 return CURL_READFUNC_ABORT;
581 CHPContext_t *ctx = context;
582 size_t remainingLen = ctx->payloadLength - ctx->readOffset;
583 size_t toTransfer = size * num > remainingLen ? remainingLen : size * num;
584 memcpy(buffer, ctx->payload + ctx->readOffset, toTransfer);
585 ctx->readOffset += toTransfer;
589 static size_t CHPEasyHandleHeaderCb(char *buffer, size_t size, size_t num, void *context)
591 size_t dataToWrite = size * num;
592 if(!buffer || !dataToWrite || !context || g_terminateParser)
594 OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
598 /* curl will call this function for each line in response header including status line
599 * and for each http response that it might have received from http server for ex: redirect,
600 * proxy handshakes etc. All these intermediary responses are not useful for us but there
601 * isn't any mechanism to track which one is going to be final.
602 * Hence here we process each response and assume that the relevant one will be the final
606 /* Start of a response is tracked by presence of status line starting with "HTTP/"
607 * This also acts as a reset for everything else (payload, header options) as we are processing
611 CHPContext_t *ctx = context;
612 HttpResponse_t *resp = &(ctx->resp);
615 if (strncmp("HTTP/", buffer, 5) == 0)
617 OIC_LOG(ERROR, TAG, "New header received");
618 resp->payloadLength = 0;
619 ctx->writeOffset = 0;
620 OICFree(resp->payload);
621 resp->payload = NULL;
622 CHPParserResetHeaderOptions(&(resp->headerOptions));
623 // This is a status line. We are only interested in header options.
629 // A header line can have CR LF NULL and spaces at end. Make endOfHeader point to last
630 // character in header value
631 char* endOfHeader = buffer + dataToWrite;
632 while ((endOfHeader > buffer) && (*endOfHeader == '\r' || *endOfHeader == '\n'
633 || *endOfHeader == ' ' || *endOfHeader == '\0'))
638 /* curl might not send the buffer NULL terminated and copying each header is too much overhead
639 * hence use mem family of function to search */
640 char* ptr = (char*) memchr(buffer, ':', dataToWrite);
641 // There is a colon and its not the first character
642 if(ptr && ptr != buffer && ptr <= endOfHeader)
644 size_t headerFieldLen = ptr - buffer;
645 size_t headerValueLen;
646 char* headerValuePtr;
648 /* Skip any white spaces */
650 while(ptr <= endOfHeader && *ptr == ' ')
655 if(ptr > endOfHeader)
658 headerValuePtr = NULL;
662 // endOfHeader is pointing to last header value character hence +1
663 headerValueLen = endOfHeader - ptr + 1;
664 headerValuePtr = ptr;
667 if (!(resp->headerOptions))
669 // First header callback, assign storage for header options
670 resp->headerOptions = u_arraylist_create();
671 if (!(resp->headerOptions))
673 OIC_LOG(ERROR, TAG, "Memory failed!");
678 HttpHeaderOption_t *option = OICCalloc(1, sizeof(HttpHeaderOption_t));
681 OIC_LOG(ERROR, TAG, "Memory failed!");
685 headerFieldLen = headerFieldLen > (sizeof(option->optionName) - 1) ?
686 (sizeof(option->optionName) - 1): headerFieldLen;
687 memcpy(option->optionName, buffer, headerFieldLen);
688 option->optionName[headerFieldLen] = '\0';
692 headerValueLen = headerValueLen > (sizeof(option->optionData) - 1) ?
693 (sizeof(option->optionData) - 1): headerValueLen;
694 memcpy(option->optionData, headerValuePtr, headerValueLen);
695 option->optionData[headerValueLen] = '\0';
698 OIC_LOG_V(DEBUG, TAG, "%s:: %s: %s", __func__, option->optionName, option->optionData);
699 // Add to header option list
700 if(!u_arraylist_add(resp->headerOptions, option))
702 OIC_LOG(ERROR, TAG, "u_arraylist_add failed!");
708 // ignore else as this might be CRLF header lines.
712 static OCStackResult CHPInitializeEasyHandle(CURL** easyHandle, HttpRequest_t *req,
713 CHPContext_t* handleContext)
715 OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
716 VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
717 VERIFY_NON_NULL_RET(easyHandle, TAG, "easyHandle", OC_STACK_INVALID_PARAM);
718 VERIFY_NON_NULL_RET(handleContext, TAG, "handleContext", OC_STACK_INVALID_PARAM);
720 CURL *e = curl_easy_init();
723 OIC_LOG(ERROR, TAG, "easy init failed!");
724 return OC_STACK_ERROR;
727 /* Set http resource uri */
728 curl_easy_setopt(e, CURLOPT_URL, req->resourceUri);
729 /* Default protocol when scheme is not available in uri */
730 // curl version 7.22 don't support this option.
731 //curl_easy_setopt(e, CURLOPT_DEFAULT_PROTOCOL, "http");
732 /* Set handle context */
733 curl_easy_setopt(e, CURLOPT_PRIVATE, handleContext);
734 curl_easy_setopt(e, CURLOPT_WRITEFUNCTION, CHPEasyHandleWriteCb);
735 curl_easy_setopt(e, CURLOPT_WRITEDATA, handleContext);
736 curl_easy_setopt(e, CURLOPT_READFUNCTION, CHPEasyHandleReadCb);
737 curl_easy_setopt(e, CURLOPT_READDATA, handleContext);
738 curl_easy_setopt(e, CURLOPT_HEADERFUNCTION, CHPEasyHandleHeaderCb);
739 curl_easy_setopt(e, CURLOPT_HEADERDATA, handleContext);
741 /* Allow access to only http server's */
742 curl_easy_setopt(e, CURLOPT_PROTOCOLS,
743 CURLPROTO_HTTP | CURLPROTO_HTTPS);
744 /* complete connection within 15 seconds */
745 curl_easy_setopt(e, CURLOPT_CONNECTTIMEOUT, 15L);
746 /* Abort transaction if getting less than 1kbps for 60 seconds */
747 curl_easy_setopt(e, CURLOPT_LOW_SPEED_LIMIT, 1024L);
748 curl_easy_setopt(e, CURLOPT_LOW_SPEED_TIME, 60L);
749 curl_easy_setopt(e, CURLOPT_USERAGENT, DEFAULT_USER_AGENT);
750 /* Close connection once done with transaction */
751 curl_easy_setopt(e, CURLOPT_FORBID_REUSE, 1L);
753 curl_easy_setopt(e, CURLOPT_FOLLOWLOCATION, 1L);
754 /* Only redirect to http servers */
755 curl_easy_setopt(e, CURLOPT_REDIR_PROTOCOLS,
756 CURLPROTO_HTTP | CURLPROTO_HTTPS);
757 /* Limit maximum redirects */
758 curl_easy_setopt(e, CURLOPT_MAXREDIRS, 10L);
760 handleContext->writeOffset = 0;
761 handleContext->readOffset = 0;
765 OIC_LOG(DEBUG, TAG, "Sending GET request");
766 curl_easy_setopt(e, CURLOPT_HTTPGET, 1);
769 OIC_LOG(DEBUG, TAG, "Sending POST request");
770 curl_easy_setopt(e, CURLOPT_POST, 1);
771 curl_easy_setopt(e, CURLOPT_POSTFIELDS, NULL);
772 curl_easy_setopt(e, CURLOPT_POSTFIELDSIZE, req->payloadLength);
773 handleContext->payloadLength = req->payloadLength;
774 handleContext->payload = req->payload;
775 req->payloadCached = true;
778 OIC_LOG(DEBUG, TAG, "Sending PUT request");
779 curl_easy_setopt(e, CURLOPT_UPLOAD, 1);
780 curl_easy_setopt(e, CURLOPT_INFILESIZE, req->payloadLength);
781 handleContext->payloadLength = req->payloadLength;
782 handleContext->payload = req->payload;
783 req->payloadCached = true;
786 OIC_LOG(DEBUG, TAG, "Sending DELETE request");
787 /* libcurl don't have direct option for sending DELETE */
788 curl_easy_setopt(e, CURLOPT_CUSTOMREQUEST, "DELETE");
791 return OC_STACK_INVALID_METHOD;
794 // Add header options from request
795 struct curl_slist *list = NULL;
796 char buffer[CHP_MAX_HF_NAME_LENGTH + CHP_MAX_HF_DATA_LENGTH + 2]; // extra 2 bytes for ": "
798 if (req->headerOptions)
800 HttpHeaderOption_t *option = NULL;
801 int headerCount = u_arraylist_length(req->headerOptions);
802 for(int i = 0; i < headerCount; i++)
804 option = u_arraylist_get(req->headerOptions, i);
807 OIC_LOG_V(DEBUG, TAG, "Adding header option: %s", buffer);
808 snprintf(buffer, sizeof(buffer), "%s: %s", option->optionName, option->optionData);
809 list = curl_slist_append(list, buffer);
814 /* Add content-type and accept header */
815 snprintf(buffer, sizeof(buffer), "Accept: %s", req->acceptFormat);
816 list = curl_slist_append(list, buffer);
817 snprintf(buffer, sizeof(buffer), "Content-Type: %s", req->payloadFormat);
818 curl_easy_setopt(e, CURLOPT_HTTPHEADER, list);
821 OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
825 OCStackResult CHPPostHttpRequest(HttpRequest_t *req, CHPResponseCallback httpcb,
828 OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
829 VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
830 VERIFY_NON_NULL_RET(httpcb, TAG, "httpcb", OC_STACK_INVALID_PARAM);
832 CHPContext_t *ctxt = OICCalloc(1, sizeof(CHPContext_t));
835 OIC_LOG(ERROR, TAG, "Memory failed!");
836 return OC_STACK_NO_MEMORY;
840 ctxt->context = context;
841 OCStackResult ret = CHPInitializeEasyHandle(&ctxt->easyHandle, req, ctxt);
842 if(ret != OC_STACK_OK)
844 OIC_LOG_V(ERROR, TAG, "Failed to initialize easy handle [%d]", ret);
849 // Add easy_handle to multi_handle
850 CHPParserLockMutex();
851 curl_multi_add_handle(g_multiHandle, ctxt->easyHandle);
852 g_activeConnections++;
853 CHPParserUnlockMutex();
858 len = write(g_refreshFds[1], "w", 1);
859 } while ((len == -1) && (errno == EINTR));
861 if ((len == -1) && (errno != EINTR) && (errno != EPIPE))
863 OIC_LOG_V(DEBUG, TAG, "refresh failed: %s", strerror(errno));
866 OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);