IOT-2539 Cleanup -Wformat= warnings
[iotivity.git] / service / coap-http-proxy / src / CoapHttpParser.c
1 /* ****************************************************************
2  *
3  * Copyright 2016 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  ******************************************************************/
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <inttypes.h>
25
26 #include "iotivity_config.h"
27 #include "CoapHttpParser.h"
28 #include "oic_malloc.h"
29 #include "oic_string.h"
30 #include "uarraylist.h"
31 #include "logger.h"
32
33 #include <string.h>
34 #include <curl/curl.h>
35 #ifdef HAVE_PTHREAD_H
36 #include <pthread.h>
37 #endif
38 #if !defined(_MSC_VER)
39 #include <unistd.h>
40 #endif //!defined(_MSC_VER)
41 #include <sys/types.h>
42 #include <fcntl.h>
43 #if !defined(_WIN32)
44 #include <sys/select.h>
45 #endif //!defined(_WIN32)
46 #include <errno.h>
47
48 #define TAG "CHP_PARSER"
49
50 #define DEFAULT_USER_AGENT "IoTivity"
51 #define MAX_PAYLOAD_SIZE (1048576U) // 1 MB
52
53 typedef struct
54 {
55     void* context;
56     CHPResponseCallback cb;
57     HttpResponse_t resp;
58     /* libcurl will not cache request payload when creating a easy handle hence we need to cache */
59     void* payload;
60     size_t payloadLength;
61     /* To track multiple read_callbacks from curl */
62     size_t readOffset;
63     /* To track multiple write_callbacks from curl */
64     size_t writeOffset;
65     /* libcurl related */
66     CURL* easyHandle;
67     /* libcurl does not copy header options passed to a request */
68     struct curl_slist *list;
69 } CHPContext_t;
70
71 /* A curl mutihandle is not threadsafe so we require mutexes to add new easy
72  * handles to multihandle.
73  */
74 static CURLM *g_multiHandle;
75 static int g_activeConnections;
76
77 /*  Mutex code is taken from CA.
78  *  General utility functions shall be placed in common location
79  *  so that all modules can use them.
80  */
81 static pthread_mutex_t g_multiHandleMutex;
82
83 /* Fds used to signal threads to stop */
84 static int g_shutdownFds[2];
85
86 static bool g_terminateParser;
87
88 /*
89  * Fds used to signal fdset to be refreshed.
90  * When a new easy_handle is added to multi_handle,
91  * existing fd_set has to be updated.
92  */
93 static int g_refreshFds[2];
94
95 /*
96  * Thread handle for curl multi_handle processing.
97  */
98 static pthread_t g_multiHandleThread;
99
100 static void CHPParserLockMutex();
101 static void CHPParserUnlockMutex();
102
103 static void CHPParserResetHeaderOptions(u_arraylist_t** headerOptions)
104 {
105     VERIFY_NON_NULL_VOID(headerOptions, TAG, "headerOptions is NULL");
106
107     HttpHeaderOption_t *option = NULL;
108     while (NULL != (option = u_arraylist_remove(*headerOptions, 0)))
109     {
110         OICFree(option);
111     }
112     u_arraylist_free(headerOptions);
113 }
114
115 static void CHPFreeContext(CHPContext_t *ctxt)
116 {
117     VERIFY_NON_NULL_VOID(ctxt, TAG, "ctxt is NULL");
118     if(ctxt->list)
119     {
120         curl_slist_free_all(ctxt->list);
121     }
122
123     if(ctxt->easyHandle)
124     {
125         curl_easy_cleanup(ctxt->easyHandle);
126     }
127
128     CHPParserResetHeaderOptions(&(ctxt->resp.headerOptions));
129     OICFree(ctxt->resp.payload);
130     OICFree(ctxt->payload);
131     OICFree(ctxt);
132 }
133
134 static void *CHPParserExecuteMultiHandle(void* data)
135 {
136     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
137     OC_UNUSED(data);
138     /*
139      * These fd sets will be fetched from curl multi handle and monitored to execute
140      * curl_multi_perform()
141      */
142     fd_set fdread;
143     fd_set fdwrite;
144     fd_set fdexcep;
145     int maxfd;
146     struct timeval timeout;
147     struct timeval *tv;
148     int activeCon;
149     int activeEasyHandle;
150     bool goForSelect;
151     int retValue;
152
153     /* When active connections exist, curl_multi_perform() shall be called within
154      * curlMultiTimeout seconds regardless of whether select returned successfull or not.
155      */
156     long curlMultiTimeout;
157     while (!g_terminateParser)
158     {
159         // Required everytime before calling curl_multi_fdset()
160         FD_ZERO(&fdread);
161         FD_ZERO(&fdwrite);
162         FD_ZERO(&fdexcep);
163         maxfd = -1;
164         goForSelect = true;
165
166         // Get currently active transfer fds from curl
167         CHPParserLockMutex();
168         curl_multi_fdset(g_multiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
169         curl_multi_timeout(g_multiHandle, &curlMultiTimeout);
170         activeCon = g_activeConnections;
171         CHPParserUnlockMutex();
172
173         // A 0 represent curl_multi_perform() shall be called.
174         if(curlMultiTimeout < 0)
175         {
176             curlMultiTimeout = 1000;
177         }
178
179         if(maxfd == -1)
180         {
181             /* Nothing to monitor from libcurl.
182              * This mean that no active sockets exist either because
183              * there are no transfer taking place or sockets are not in a
184              * state that could be monitored (connecting, retry etc.)
185              */
186             if(!activeCon)
187             {
188                 // wait until something received on shutdown or refresh fd
189                 // with no timeout.
190                 curlMultiTimeout = -1;
191             }
192             else
193             {
194                 // libcurl recommend doing this.
195                 usleep(100000);
196                 // dont select() and directly call curl_multi_perform()
197                 goForSelect = false;
198             }
199         }
200
201         if(goForSelect)
202         {
203             FD_SET(g_shutdownFds[0], &fdread);
204             if (maxfd < g_shutdownFds[0])
205             {
206                 maxfd = g_shutdownFds[0];
207             }
208
209             FD_SET(g_refreshFds[0], &fdread);
210             if (maxfd < g_refreshFds[0])
211             {
212                 maxfd = g_refreshFds[0];
213             }
214
215             if(curlMultiTimeout == -1)
216             {
217                 tv = NULL;
218             }
219             else
220             {
221                 timeout.tv_sec = curlMultiTimeout / 1000;
222                 timeout.tv_usec = 0;
223                 tv = &timeout;
224             }
225
226             // Select here
227             retValue = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, tv);
228             if(retValue == -1)
229             {
230                 OIC_LOG_V(ERROR, TAG, "Error in select. %s", strerror(errno));
231                 continue;
232             }
233
234             // Some sockets are available for operations, check if shutdown or refresh fds are
235             // among them. In any case, go ahead and call curl_multi_perform()
236             if(retValue)
237             {
238                 if (FD_ISSET(g_shutdownFds[0], &fdread))
239                 {
240                     OIC_LOG(ERROR, TAG, "Shutdown requested. multi_handle returning");
241                     break;
242                 }
243                 else if(FD_ISSET(g_refreshFds[0], &fdread))
244                 {
245                     char buf[20] = {0};
246                     ssize_t len = read(g_refreshFds[0], buf, sizeof(buf));
247                     OC_UNUSED(len);
248                     // new easy handles added, call multi_perform and refresh fds.
249                     OIC_LOG(ERROR, TAG, "New easy handle added");
250                 }
251             }
252         }
253
254         CURLMcode ret;
255         CHPParserLockMutex();
256         do
257         {
258             ret = curl_multi_perform(g_multiHandle, &activeEasyHandle);
259             struct CURLMsg *cmsg;
260             int cmsgq;
261             do
262             {
263                 cmsgq = 0;
264                 cmsg = curl_multi_info_read(g_multiHandle, &cmsgq);
265                 if(cmsg && (cmsg->msg == CURLMSG_DONE))
266                 {
267                     CURL *easyHandle = cmsg->easy_handle;
268                     g_activeConnections--;
269                     curl_multi_remove_handle(g_multiHandle, easyHandle);
270
271                     CHPContext_t *ptr;
272                     char *uri = NULL;
273                     char *contentType = NULL;
274                     long responseCode;
275
276                     curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &ptr);
277                     curl_easy_getinfo(easyHandle, CURLINFO_EFFECTIVE_URL, &uri);
278                     curl_easy_getinfo(easyHandle, CURLINFO_RESPONSE_CODE, &responseCode);
279                     curl_easy_getinfo(easyHandle, CURLINFO_CONTENT_TYPE, &contentType);
280
281                     ptr->resp.status = responseCode;
282                     OICStrcpy(ptr->resp.dataFormat, sizeof(ptr->resp.dataFormat), contentType);
283                     OIC_LOG_V(DEBUG, TAG, "Transfer completed %d uri: %s, %s", g_activeConnections,
284                                                                            uri, contentType);
285                     ptr->cb(&(ptr->resp), ptr->context);
286                     CHPFreeContext(ptr);
287                 }
288             } while(cmsg && !g_terminateParser);
289         }while (ret == CURLM_CALL_MULTI_PERFORM && !g_terminateParser);
290         CHPParserUnlockMutex();
291     }
292
293     if (g_terminateParser)
294     {
295         OIC_LOG_V(DEBUG, TAG, "Shutdown request received.");
296         // g_shutdownFds[1] will be already closed.
297         close(g_shutdownFds[0]);
298         close(g_refreshFds[0]);
299         close(g_refreshFds[1]);
300         g_shutdownFds[0] = -1;
301         g_shutdownFds[1] = -1;
302         g_refreshFds[0] = -1;
303         g_refreshFds[1] = -1;
304     }
305
306     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
307     return NULL;
308 }
309
310 OCStackResult CHPParserInitializePipe(int fds[2])
311 {
312     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
313     int ret = -1;
314     fds[0] = -1;
315     fds[1] = -1;
316 #if defined(HAVE_PIPE2)
317     ret = pipe2(fds, O_CLOEXEC);
318 #else
319     ret = pipe(fds);
320     if (-1 != ret)
321     {
322         ret = fcntl(fds[0], F_GETFD);
323         if (-1 != ret)
324         {
325             ret = fcntl(fds[0], F_SETFD, ret|FD_CLOEXEC);
326         }
327         if (-1 != ret)
328         {
329             ret = fcntl(fds[1], F_GETFD);
330         }
331         if (-1 != ret)
332         {
333             ret = fcntl(fds[1], F_SETFD, ret|FD_CLOEXEC);
334         }
335         if (-1 == ret)
336         {
337             close(fds[1]);
338             close(fds[0]);
339             fds[0] = -1;
340             fds[1] = -1;
341         }
342     }
343 #endif
344     if (-1 == ret)
345     {
346         OIC_LOG_V(ERROR, TAG, "FD initialization failed: %s", strerror(errno));
347         return OC_STACK_ERROR;
348     }
349     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
350     return OC_STACK_OK;
351 }
352
353 static OCStackResult CHPParserInitializeMutex()
354 {
355     // create the mutex with the attributes set
356     int ret = pthread_mutex_init(&g_multiHandleMutex, PTHREAD_MUTEX_DEFAULT);
357     if (0 != ret)
358     {
359         OIC_LOG_V(ERROR, TAG, "%s Failed to initialize mutex !", __func__);
360         return OC_STACK_ERROR;
361     }
362     return OC_STACK_OK;
363 }
364
365 static OCStackResult CHPParserTerminateMutex()
366 {
367     int ret = pthread_mutex_destroy(&g_multiHandleMutex);
368     if (0 != ret)
369     {
370         OIC_LOG_V(ERROR, TAG, "%s Failed to free mutex !", __func__);
371         return OC_STACK_ERROR;
372     }
373     return OC_STACK_OK;
374 }
375
376 static void CHPParserLockMutex()
377 {
378     int ret = pthread_mutex_lock(&g_multiHandleMutex);
379     if(ret != 0)
380     {
381         OIC_LOG_V(ERROR, TAG, "Pthread Mutex lock failed: %d", ret);
382     }
383 }
384
385 static void CHPParserUnlockMutex()
386 {
387     int ret = pthread_mutex_unlock(&g_multiHandleMutex);
388     if(ret != 0)
389     {
390         OIC_LOG_V(ERROR, TAG, "Pthread Mutex unlock failed: %d", ret);
391     }
392 }
393
394 static OCStackResult CHPParserInitializeMultiHandle()
395 {
396     CHPParserLockMutex();
397     if(g_multiHandle)
398     {
399         OIC_LOG(ERROR, TAG, "Multi handle already initialized.");
400         CHPParserUnlockMutex();
401         return OC_STACK_OK;
402     }
403
404     g_multiHandle = curl_multi_init();
405     if(!g_multiHandle)
406     {
407         OIC_LOG(ERROR, TAG, "Failed to create multi handle.");
408         CHPParserUnlockMutex();
409         return OC_STACK_ERROR;
410     }
411
412     CHPParserUnlockMutex();
413     return OC_STACK_OK;
414 }
415
416 static OCStackResult CHPParserTerminateMultiHandle()
417 {
418     CHPParserLockMutex();
419     if(!g_multiHandle)
420     {
421         OIC_LOG(ERROR, TAG, "Multi handle not initialized.");
422         CHPParserUnlockMutex();
423         return OC_STACK_OK;
424     }
425
426     curl_multi_cleanup(g_multiHandle);
427     g_multiHandle = NULL;
428     CHPParserUnlockMutex();
429     return OC_STACK_OK;
430 }
431
432 OCStackResult CHPParserInitialize()
433 {
434     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
435
436     OCStackResult ret = CHPParserInitializeMutex();
437     if(ret != OC_STACK_OK)
438     {
439         return ret;
440     }
441
442     ret = CHPParserInitializeMultiHandle();
443     if(ret != OC_STACK_OK)
444     {
445         OIC_LOG_V(ERROR, TAG, "Failed to intialize multi handle: %d", ret);
446         CHPParserTerminate();
447         return ret;
448     }
449
450     ret = CHPParserInitializePipe(g_shutdownFds);
451     if(ret != OC_STACK_OK)
452     {
453         OIC_LOG_V(ERROR, TAG, "Failed to intialize shutdown fds: %d", ret);
454         CHPParserTerminate();
455         return ret;
456     }
457
458     ret = CHPParserInitializePipe(g_refreshFds);
459     if(ret != OC_STACK_OK)
460     {
461         OIC_LOG_V(ERROR, TAG, "Failed to intialize refresh fds: %d", ret);
462         CHPParserTerminate();
463         return ret;
464     }
465
466     // Launch multi_handle processor thread
467     int result = pthread_create(&g_multiHandleThread, NULL, CHPParserExecuteMultiHandle, NULL);
468     if(result != 0)
469     {
470         OIC_LOG_V(ERROR, TAG, "Thread start failed with error %d", result);
471         CHPParserTerminate();
472         return OC_STACK_ERROR;
473     }
474
475     g_terminateParser = false;
476     CHPParserLockMutex();
477     g_activeConnections = 0;
478     CHPParserUnlockMutex();
479     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
480     return OC_STACK_OK;
481 }
482
483 OCStackResult CHPParserTerminate()
484 {
485     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
486     g_terminateParser = true;
487     if (g_shutdownFds[1] != -1)
488     {
489         // Signal multi_handle thread to come out
490         close(g_shutdownFds[1]);
491     }
492     pthread_join(g_multiHandleThread, NULL);
493
494     OCStackResult ret = CHPParserTerminateMultiHandle();
495     if(ret != OC_STACK_OK)
496     {
497         OIC_LOG_V(ERROR, TAG, "Multi handle termination failed: %d", ret);
498     }
499
500     CHPParserLockMutex();
501     g_activeConnections = 0;
502     CHPParserUnlockMutex();
503
504     ret = CHPParserTerminateMutex();
505     if(ret != OC_STACK_OK)
506     {
507         OIC_LOG_V(ERROR, TAG, "mutex termination failed: %d", ret);
508     }
509     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
510     return OC_STACK_OK;
511 }
512
513 static size_t CHPEasyHandleWriteCb(char *buffer, size_t size, size_t num, void *context)
514 {
515     size_t dataToWrite = size * num;
516     if(!dataToWrite)
517     {
518         // Empty payload received. Ignore.
519         return 0;
520     }
521
522     if(!context || !buffer || g_terminateParser)
523     {
524         OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
525         return 0;
526     }
527
528     CHPContext_t* ctx = context;
529     HttpResponse_t *resp = &(ctx->resp);
530
531     if(ctx->writeOffset + dataToWrite > MAX_PAYLOAD_SIZE)
532     {
533         OIC_LOG_V(ERROR, TAG, "%s Payload limit exceeded", __func__);
534         resp->payloadLength = 0;
535         ctx->writeOffset = 0;
536         OICFree(resp->payload);
537         resp->payload = NULL;
538         return 0;
539     }
540
541     if (!resp->payload)
542     {
543         resp->payload = OICMalloc(dataToWrite);
544         if (!resp->payload)
545         {
546             OIC_LOG_V(ERROR, TAG, "%s Out of memory!", __func__);
547             return 0;
548         }
549     }
550     else
551     {
552         // Realloc buffer
553         void *newPayload = OICRealloc(resp->payload, ctx->writeOffset + dataToWrite);
554         if (!newPayload)
555         {
556             OIC_LOG_V(ERROR, TAG, "Realloc failed! Current: %" PRIuPTR " Extra: %" PRIuPTR, ctx->writeOffset,
557                                                                            dataToWrite);
558             resp->payloadLength = 0;
559             ctx->writeOffset = 0;
560             OICFree(resp->payload);
561             resp->payload = NULL;
562             return 0;
563         }
564         resp->payload = newPayload;
565     }
566
567     memcpy(resp->payload + ctx->writeOffset, buffer, dataToWrite);
568     ctx->writeOffset += dataToWrite;
569     resp->payloadLength = ctx->writeOffset;
570
571     OIC_LOG_V(DEBUG, TAG, "%s OUT %" PRIuPTR " : %" PRIuPTR, __func__, resp->payloadLength, dataToWrite);
572     return dataToWrite;
573 }
574
575 static size_t CHPEasyHandleReadCb(char *buffer, size_t size, size_t num, void *context)
576 {
577     if(!context || !buffer || g_terminateParser)
578     {
579         OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
580         return CURL_READFUNC_ABORT;
581     }
582
583     CHPContext_t *ctx = context;
584     size_t remainingLen = ctx->payloadLength - ctx->readOffset;
585     size_t toTransfer = size * num > remainingLen ? remainingLen : size * num;
586     memcpy(buffer, ctx->payload + ctx->readOffset, toTransfer);
587     ctx->readOffset += toTransfer;
588     return toTransfer;
589 }
590
591 static size_t CHPEasyHandleHeaderCb(char *buffer, size_t size, size_t num, void *context)
592 {
593     size_t dataToWrite = size * num;
594     if(!buffer || !dataToWrite || !context || g_terminateParser)
595     {
596         OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
597         return 0;
598     }
599
600     /* curl will call this function for each line in response header including status line
601      * and for each http response that it might have received from http server for ex: redirect,
602      * proxy handshakes etc. All these intermediary responses are not useful for us but there
603      * isn't any mechanism to track which one is going to be final.
604      * Hence here we process each response and assume that the relevant one will be the final
605      * response.
606      */
607
608     /* Start of a response is tracked by presence of status line starting with "HTTP/"
609      * This also acts as a reset for everything else (payload, header options) as we are processing
610      * a new response.
611      */
612
613     CHPContext_t *ctx = context;
614     HttpResponse_t *resp = &(ctx->resp);
615     if (dataToWrite > 5)
616     {
617         if (strncmp("HTTP/", buffer, 5) == 0)
618         {
619             OIC_LOG(ERROR, TAG, "New header received");
620             resp->payloadLength = 0;
621             ctx->writeOffset = 0;
622             OICFree(resp->payload);
623             resp->payload = NULL;
624             CHPParserResetHeaderOptions(&(resp->headerOptions));
625             // This is a status line. We are only interested in header options.
626             return dataToWrite;
627         }
628     }
629
630
631     // A header line can have CR LF NULL and spaces at end. Make endOfHeader point to last
632     // character in header value
633     char* endOfHeader = buffer + dataToWrite;
634     while ((endOfHeader > buffer) && (*endOfHeader == '\r' || *endOfHeader == '\n'
635                 || *endOfHeader == ' ' || *endOfHeader == '\0'))
636     {
637         endOfHeader--;
638     }
639
640     /* curl might not send the buffer NULL terminated and copying each header is too much overhead
641      * hence use mem family of function to search */
642     char* ptr = (char*) memchr(buffer, ':', dataToWrite);
643     // There is a colon and its not the first character
644     if(ptr && ptr != buffer && ptr <= endOfHeader)
645     {
646         size_t headerFieldLen = ptr - buffer;
647         size_t headerValueLen;
648         char* headerValuePtr;
649
650         /* Skip any white spaces */
651         ptr++;
652         while(ptr <= endOfHeader && *ptr == ' ')
653         {
654             ptr++;
655         }
656
657         if(ptr > endOfHeader)
658         {
659             headerValueLen = 0;
660             headerValuePtr = NULL;
661         }
662         else
663         {
664             // endOfHeader is pointing to last header value character hence +1
665             headerValueLen = endOfHeader - ptr + 1;
666             headerValuePtr = ptr;
667         }
668
669         if (!(resp->headerOptions))
670         {
671             // First header callback, assign storage for header options
672             resp->headerOptions = u_arraylist_create();
673             if (!(resp->headerOptions))
674             {
675                 OIC_LOG(ERROR, TAG, "Memory failed!");
676                 return 0;
677             }
678         }
679
680         HttpHeaderOption_t *option = OICCalloc(1, sizeof(HttpHeaderOption_t));
681         if (!option)
682         {
683             OIC_LOG(ERROR, TAG, "Memory failed!");
684             return 0;
685         }
686
687         headerFieldLen = headerFieldLen > (sizeof(option->optionName) - 1) ?
688                              (sizeof(option->optionName) - 1): headerFieldLen;
689         memcpy(option->optionName, buffer, headerFieldLen);
690         option->optionName[headerFieldLen] = '\0';
691
692         if(headerValueLen)
693         {
694             headerValueLen = headerValueLen > (sizeof(option->optionData) - 1) ?
695                               (sizeof(option->optionData) - 1): headerValueLen;
696             memcpy(option->optionData, headerValuePtr, headerValueLen);
697             option->optionData[headerValueLen] = '\0';
698         }
699
700         OIC_LOG_V(DEBUG, TAG, "%s:: %s: %s", __func__, option->optionName, option->optionData);
701         // Add to header option list
702         if(!u_arraylist_add(resp->headerOptions, option))
703         {
704             OIC_LOG(ERROR, TAG, "u_arraylist_add failed!");
705             OICFree(option);
706             return 0;
707         }
708     }
709
710     // ignore else as this might be CRLF header lines.
711     return dataToWrite;
712 }
713
714 static OCStackResult CHPInitializeEasyHandle(CURL** easyHandle, HttpRequest_t *req,
715                                              CHPContext_t* handleContext)
716 {
717     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
718     VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
719     VERIFY_NON_NULL_RET(easyHandle, TAG, "easyHandle", OC_STACK_INVALID_PARAM);
720     VERIFY_NON_NULL_RET(handleContext, TAG, "handleContext", OC_STACK_INVALID_PARAM);
721
722     CURL *e = curl_easy_init();
723     if(!e)
724     {
725         OIC_LOG(ERROR, TAG, "easy init failed!");
726         return OC_STACK_ERROR;
727     }
728
729     /* Set http resource uri */
730     curl_easy_setopt(e, CURLOPT_URL, req->resourceUri);
731     /* Default protocol when scheme is not available in uri */
732     // curl version 7.22 don't support this option.
733     //curl_easy_setopt(e, CURLOPT_DEFAULT_PROTOCOL, "http");
734     /* Set handle context */
735     curl_easy_setopt(e, CURLOPT_PRIVATE, handleContext);
736     curl_easy_setopt(e, CURLOPT_WRITEFUNCTION, CHPEasyHandleWriteCb);
737     curl_easy_setopt(e, CURLOPT_WRITEDATA, handleContext);
738     curl_easy_setopt(e, CURLOPT_READFUNCTION, CHPEasyHandleReadCb);
739     curl_easy_setopt(e, CURLOPT_READDATA, handleContext);
740     curl_easy_setopt(e, CURLOPT_HEADERFUNCTION, CHPEasyHandleHeaderCb);
741     curl_easy_setopt(e, CURLOPT_HEADERDATA, handleContext);
742
743     /* Allow access to only http server's */
744     curl_easy_setopt(e, CURLOPT_PROTOCOLS,
745                      CURLPROTO_HTTP | CURLPROTO_HTTPS);
746     /* complete connection within 15 seconds */
747     curl_easy_setopt(e, CURLOPT_CONNECTTIMEOUT, 15L);
748     /* Abort transaction if getting less than 1kbps for 60 seconds */
749     curl_easy_setopt(e, CURLOPT_LOW_SPEED_LIMIT, 1024L);
750     curl_easy_setopt(e, CURLOPT_LOW_SPEED_TIME, 60L);
751     curl_easy_setopt(e, CURLOPT_USERAGENT, DEFAULT_USER_AGENT);
752     /* Close connection once done with transaction */
753     curl_easy_setopt(e, CURLOPT_FORBID_REUSE, 1L);
754     /* Allow redirect */
755     curl_easy_setopt(e, CURLOPT_FOLLOWLOCATION, 1L);
756     /* Only redirect to http servers */
757     curl_easy_setopt(e, CURLOPT_REDIR_PROTOCOLS,
758                      CURLPROTO_HTTP | CURLPROTO_HTTPS);
759     /* Limit maximum redirects */
760     curl_easy_setopt(e, CURLOPT_MAXREDIRS, 10L);
761
762     handleContext->writeOffset = 0;
763     handleContext->readOffset = 0;
764     switch(req->method)
765     {
766         case CHP_GET:
767             OIC_LOG(DEBUG, TAG, "Sending GET request");
768             curl_easy_setopt(e, CURLOPT_HTTPGET, 1);
769             break;
770         case CHP_POST:
771             OIC_LOG(DEBUG, TAG, "Sending POST request");
772             curl_easy_setopt(e, CURLOPT_POST, 1);
773             curl_easy_setopt(e, CURLOPT_POSTFIELDS, NULL);
774             curl_easy_setopt(e, CURLOPT_POSTFIELDSIZE, req->payloadLength);
775             handleContext->payloadLength = req->payloadLength;
776             handleContext->payload = req->payload;
777             req->payloadCached = true;
778             break;
779         case CHP_PUT:
780             OIC_LOG(DEBUG, TAG, "Sending PUT request");
781             curl_easy_setopt(e, CURLOPT_UPLOAD, 1);
782             curl_easy_setopt(e, CURLOPT_INFILESIZE, req->payloadLength);
783             handleContext->payloadLength = req->payloadLength;
784             handleContext->payload = req->payload;
785             req->payloadCached = true;
786             break;
787         case CHP_DELETE:
788             OIC_LOG(DEBUG, TAG, "Sending DELETE request");
789             /* libcurl don't have direct option for sending DELETE */
790             curl_easy_setopt(e, CURLOPT_CUSTOMREQUEST, "DELETE");
791             break;
792         default:
793             return OC_STACK_INVALID_METHOD;
794     }
795
796     // Add header options from request
797     struct curl_slist *list = NULL;
798     char buffer[CHP_MAX_HF_NAME_LENGTH + CHP_MAX_HF_DATA_LENGTH + 2]; // extra 2 bytes for ": "
799
800     if (req->headerOptions)
801     {
802         HttpHeaderOption_t *option = NULL;
803         size_t headerCount = u_arraylist_length(req->headerOptions);
804         for(size_t i = 0; i < headerCount; i++)
805         {
806             option = u_arraylist_get(req->headerOptions, i);
807             if(option)
808             {
809                 OIC_LOG_V(DEBUG, TAG, "Adding header option: %s", buffer);
810                 snprintf(buffer, sizeof(buffer), "%s: %s", option->optionName, option->optionData);
811                 list = curl_slist_append(list, buffer);
812             }
813         }
814     }
815
816     /* Add content-type and accept header */
817     snprintf(buffer, sizeof(buffer), "Accept: %s", req->acceptFormat);
818     list = curl_slist_append(list, buffer);
819     snprintf(buffer, sizeof(buffer), "Content-Type: %s", req->payloadFormat);
820     curl_easy_setopt(e, CURLOPT_HTTPHEADER, list);
821
822     *easyHandle = e;
823     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
824     return OC_STACK_OK;
825 }
826
827 OCStackResult CHPPostHttpRequest(HttpRequest_t *req, CHPResponseCallback httpcb,
828                                  void *context)
829 {
830     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
831     VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
832     VERIFY_NON_NULL_RET(httpcb, TAG, "httpcb", OC_STACK_INVALID_PARAM);
833
834     CHPContext_t *ctxt = OICCalloc(1, sizeof(CHPContext_t));
835     if (!ctxt)
836     {
837         OIC_LOG(ERROR, TAG, "Memory failed!");
838         return OC_STACK_NO_MEMORY;
839     }
840
841     ctxt->cb = httpcb;
842     ctxt->context = context;
843     OCStackResult ret = CHPInitializeEasyHandle(&ctxt->easyHandle, req, ctxt);
844     if(ret != OC_STACK_OK)
845     {
846         OIC_LOG_V(ERROR, TAG, "Failed to initialize easy handle [%d]", ret);
847         OICFree(ctxt);
848         return ret;
849     }
850
851     // Add easy_handle to multi_handle
852     CHPParserLockMutex();
853     curl_multi_add_handle(g_multiHandle, ctxt->easyHandle);
854     g_activeConnections++;
855     CHPParserUnlockMutex();
856     // Notify refreshfd
857     ssize_t len = 0;
858     do
859     {
860         len = write(g_refreshFds[1], "w", 1);
861     } while ((len == -1) && (errno == EINTR));
862
863     if ((len == -1) && (errno != EINTR) && (errno != EPIPE))
864     {
865         OIC_LOG_V(DEBUG, TAG, "refresh failed: %s", strerror(errno));
866     }
867
868     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
869     return OC_STACK_OK;
870 }