Development of CoAP-HTTP Proxy
[platform/upstream/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
25 #include "CoapHttpParser.h"
26 #include "oic_malloc.h"
27 #include "oic_string.h"
28 #include "uarraylist.h"
29 #include "logger.h"
30
31 #include <string.h>
32 #include <curl/curl.h>
33 #ifdef HAVE_PTHREAD_H
34 #include <pthread.h>
35 #endif
36 #if !defined(_MSC_VER)
37 #include <unistd.h>
38 #endif //!defined(_MSC_VER)
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #if !defined(_WIN32)
42 #include <sys/select.h>
43 #endif //!defined(_WIN32)
44 #include <errno.h>
45
46 #define TAG "CHP_PARSER"
47
48 #define DEFAULT_USER_AGENT "IoTivity"
49 #define MAX_PAYLOAD_SIZE (1048576U) // 1 MB
50
51 typedef struct
52 {
53     void* context;
54     CHPResponseCallback cb;
55     HttpResponse_t resp;
56     /* libcurl will not cache request payload when creating a easy handle hence we need to cache */
57     void* payload;
58     size_t payloadLength;
59     /* To track multiple read_callbacks from curl */
60     size_t readOffset;
61     /* To track multiple write_callbacks from curl */
62     size_t writeOffset;
63     /* libcurl related */
64     CURL* easyHandle;
65     /* libcurl does not copy header options passed to a request */
66     struct curl_slist *list;
67 } CHPContext_t;
68
69 /* A curl mutihandle is not threadsafe so we require mutexes to add new easy
70  * handles to multihandle.
71  */
72 static CURLM *g_multiHandle;
73 static int g_activeConnections;
74
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.
78  */
79 static pthread_mutex_t g_multiHandleMutex;
80
81 /* Fds used to signal threads to stop */
82 static int g_shutdownFds[2];
83
84 static bool g_terminateParser;
85
86 /*
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.
90  */
91 static int g_refreshFds[2];
92
93 /*
94  * Thread handle for curl multi_handle processing.
95  */
96 static pthread_t g_multiHandleThread;
97
98 static void CHPParserLockMutex();
99 static void CHPParserUnlockMutex();
100
101 static void CHPParserResetHeaderOptions(u_arraylist_t** headerOptions)
102 {
103     VERIFY_NON_NULL_VOID(headerOptions, TAG, "headerOptions is NULL");
104
105     HttpHeaderOption_t *option = NULL;
106     while (NULL != (option = u_arraylist_remove(*headerOptions, 0)))
107     {
108         OICFree(option);
109     }
110     u_arraylist_free(headerOptions);
111 }
112
113 static void CHPFreeContext(CHPContext_t *ctxt)
114 {
115     VERIFY_NON_NULL_VOID(ctxt, TAG, "ctxt is NULL");
116     if(ctxt->list)
117     {
118         curl_slist_free_all(ctxt->list);
119     }
120
121     if(ctxt->easyHandle)
122     {
123         curl_easy_cleanup(ctxt->easyHandle);
124     }
125
126     CHPParserResetHeaderOptions(&(ctxt->resp.headerOptions));
127     OICFree(ctxt->resp.payload);
128     OICFree(ctxt->payload);
129     OICFree(ctxt);
130 }
131
132 static void *CHPParserExecuteMultiHandle(void* data)
133 {
134     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
135     UNUSED(data);
136     /*
137      * These fd sets will be fetched from curl multi handle and monitored to execute
138      * curl_multi_perform()
139      */
140     fd_set fdread;
141     fd_set fdwrite;
142     fd_set fdexcep;
143     int maxfd;
144     struct timeval timeout;
145     struct timeval *tv;
146     int activeCon;
147     int activeEasyHandle;
148     bool goForSelect;
149     int retValue;
150
151     /* When active connections exist, curl_multi_perform() shall be called within
152      * curlMultiTimeout seconds regardless of whether select returned successfull or not.
153      */
154     long curlMultiTimeout;
155     while (!g_terminateParser)
156     {
157         // Required everytime before calling curl_multi_fdset()
158         FD_ZERO(&fdread);
159         FD_ZERO(&fdwrite);
160         FD_ZERO(&fdexcep);
161         maxfd = -1;
162         goForSelect = true;
163
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();
170
171         // A 0 represent curl_multi_perform() shall be called.
172         if(curlMultiTimeout < 0)
173         {
174             curlMultiTimeout = 1000;
175         }
176
177         if(maxfd == -1)
178         {
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.)
183              */
184             if(!activeCon)
185             {
186                 // wait until something received on shutdown or refresh fd
187                 // with no timeout.
188                 curlMultiTimeout = -1;
189             }
190             else
191             {
192                 // libcurl recommend doing this.
193                 usleep(100000);
194                 // dont select() and directly call curl_multi_perform()
195                 goForSelect = false;
196             }
197         }
198
199         if(goForSelect)
200         {
201             FD_SET(g_shutdownFds[0], &fdread);
202             if (maxfd < g_shutdownFds[0])
203             {
204                 maxfd = g_shutdownFds[0];
205             }
206
207             FD_SET(g_refreshFds[0], &fdread);
208             if (maxfd < g_refreshFds[0])
209             {
210                 maxfd = g_refreshFds[0];
211             }
212
213             if(curlMultiTimeout == -1)
214             {
215                 tv = NULL;
216             }
217             else
218             {
219                 timeout.tv_sec = curlMultiTimeout / 1000;
220                 timeout.tv_usec = 0;
221                 tv = &timeout;
222             }
223
224             // Select here
225             retValue = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, tv);
226             if(retValue == -1)
227             {
228                 OIC_LOG_V(ERROR, TAG, "Error in select. %s", strerror(errno));
229                 continue;
230             }
231
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()
234             if(retValue)
235             {
236                 if (FD_ISSET(g_shutdownFds[0], &fdread))
237                 {
238                     OIC_LOG(ERROR, TAG, "Shutdown requested. multi_handle returning");
239                     break;
240                 }
241                 else if(FD_ISSET(g_refreshFds[0], &fdread))
242                 {
243                     char buf[20] = {0};
244                     ssize_t len = read(g_refreshFds[0], buf, sizeof(buf));
245                     UNUSED(len);
246                     // new easy handles added, call multi_perform and refresh fds.
247                     OIC_LOG(ERROR, TAG, "New easy handle added");
248                 }
249             }
250         }
251
252         CURLMcode ret;
253         CHPParserLockMutex();
254         do
255         {
256             ret = curl_multi_perform(g_multiHandle, &activeEasyHandle);
257             struct CURLMsg *cmsg;
258             int cmsgq;
259             do
260             {
261                 cmsgq = 0;
262                 cmsg = curl_multi_info_read(g_multiHandle, &cmsgq);
263                 if(cmsg && (cmsg->msg == CURLMSG_DONE))
264                 {
265                     CURL *easyHandle = cmsg->easy_handle;
266                     g_activeConnections--;
267                     curl_multi_remove_handle(g_multiHandle, easyHandle);
268
269                     CHPContext_t *ptr;
270                     char *uri = NULL;
271                     char *contentType = NULL;
272                     long responseCode;
273
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);
278
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,
282                                                                            uri, contentType);
283                     ptr->cb(&(ptr->resp), ptr->context);
284                     CHPFreeContext(ptr);
285                 }
286             } while(cmsg && !g_terminateParser);
287         }while (ret == CURLM_CALL_MULTI_PERFORM && !g_terminateParser);
288         CHPParserUnlockMutex();
289     }
290
291     if (g_terminateParser)
292     {
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;
302     }
303
304     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
305     return NULL;
306 }
307
308 OCStackResult CHPParserInitializePipe(int fds[2])
309 {
310     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
311     int ret = -1;
312     fds[0] = -1;
313     fds[1] = -1;
314 #if defined(HAVE_PIPE2)
315     ret = pipe2(fds, O_CLOEXEC);
316 #else
317     ret = pipe(fds);
318     if (-1 != ret)
319     {
320         ret = fcntl(fds[0], F_GETFD);
321         if (-1 != ret)
322         {
323             ret = fcntl(fds[0], F_SETFD, ret|FD_CLOEXEC);
324         }
325         if (-1 != ret)
326         {
327             ret = fcntl(fds[1], F_GETFD);
328         }
329         if (-1 != ret)
330         {
331             ret = fcntl(fds[1], F_SETFD, ret|FD_CLOEXEC);
332         }
333         if (-1 == ret)
334         {
335             close(fds[1]);
336             close(fds[0]);
337             fds[0] = -1;
338             fds[1] = -1;
339         }
340     }
341 #endif
342     if (-1 == ret)
343     {
344         OIC_LOG_V(ERROR, TAG, "FD initialization failed: %s", strerror(errno));
345         return OC_STACK_ERROR;
346     }
347     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
348     return OC_STACK_OK;
349 }
350
351 static OCStackResult CHPParserInitializeMutex()
352 {
353     // create the mutex with the attributes set
354     int ret = pthread_mutex_init(&g_multiHandleMutex, PTHREAD_MUTEX_DEFAULT);
355     if (0 != ret)
356     {
357         OIC_LOG_V(ERROR, TAG, "%s Failed to initialize mutex !", __func__);
358         return OC_STACK_ERROR;
359     }
360     return OC_STACK_OK;
361 }
362
363 static OCStackResult CHPParserTerminateMutex()
364 {
365     int ret = pthread_mutex_destroy(&g_multiHandleMutex);
366     if (0 != ret)
367     {
368         OIC_LOG_V(ERROR, TAG, "%s Failed to free mutex !", __func__);
369         return OC_STACK_ERROR;
370     }
371     return OC_STACK_OK;
372 }
373
374 static void CHPParserLockMutex()
375 {
376     int ret = pthread_mutex_lock(&g_multiHandleMutex);
377     if(ret != 0)
378     {
379         OIC_LOG_V(ERROR, TAG, "Pthread Mutex lock failed: %d", ret);
380     }
381 }
382
383 static void CHPParserUnlockMutex()
384 {
385     int ret = pthread_mutex_unlock(&g_multiHandleMutex);
386     if(ret != 0)
387     {
388         OIC_LOG_V(ERROR, TAG, "Pthread Mutex unlock failed: %d", ret);
389     }
390 }
391
392 static OCStackResult CHPParserInitializeMultiHandle()
393 {
394     CHPParserLockMutex();
395     if(g_multiHandle)
396     {
397         OIC_LOG(ERROR, TAG, "Multi handle already initialized.");
398         CHPParserUnlockMutex();
399         return OC_STACK_OK;
400     }
401
402     g_multiHandle = curl_multi_init();
403     if(!g_multiHandle)
404     {
405         OIC_LOG(ERROR, TAG, "Failed to create multi handle.");
406         CHPParserUnlockMutex();
407         return OC_STACK_ERROR;
408     }
409
410     CHPParserUnlockMutex();
411     return OC_STACK_OK;
412 }
413
414 static OCStackResult CHPParserTerminateMultiHandle()
415 {
416     CHPParserLockMutex();
417     if(!g_multiHandle)
418     {
419         OIC_LOG(ERROR, TAG, "Multi handle not initialized.");
420         CHPParserUnlockMutex();
421         return OC_STACK_OK;
422     }
423
424     curl_multi_cleanup(g_multiHandle);
425     g_multiHandle = NULL;
426     CHPParserUnlockMutex();
427     return OC_STACK_OK;
428 }
429
430 OCStackResult CHPParserInitialize()
431 {
432     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
433
434     OCStackResult ret = CHPParserInitializeMutex();
435     if(ret != OC_STACK_OK)
436     {
437         return ret;
438     }
439
440     ret = CHPParserInitializeMultiHandle();
441     if(ret != OC_STACK_OK)
442     {
443         OIC_LOG_V(ERROR, TAG, "Failed to intialize multi handle: %d", ret);
444         CHPParserTerminate();
445         return ret;
446     }
447
448     ret = CHPParserInitializePipe(g_shutdownFds);
449     if(ret != OC_STACK_OK)
450     {
451         OIC_LOG_V(ERROR, TAG, "Failed to intialize shutdown fds: %d", ret);
452         CHPParserTerminate();
453         return ret;
454     }
455
456     ret = CHPParserInitializePipe(g_refreshFds);
457     if(ret != OC_STACK_OK)
458     {
459         OIC_LOG_V(ERROR, TAG, "Failed to intialize refresh fds: %d", ret);
460         CHPParserTerminate();
461         return ret;
462     }
463
464     // Launch multi_handle processor thread
465     int result = pthread_create(&g_multiHandleThread, NULL, CHPParserExecuteMultiHandle, NULL);
466     if(result != 0)
467     {
468         OIC_LOG_V(ERROR, TAG, "Thread start failed with error %d", result);
469         CHPParserTerminate();
470         return OC_STACK_ERROR;
471     }
472
473     g_terminateParser = false;
474     CHPParserLockMutex();
475     g_activeConnections = 0;
476     CHPParserUnlockMutex();
477     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
478     return OC_STACK_OK;
479 }
480
481 OCStackResult CHPParserTerminate()
482 {
483     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
484     g_terminateParser = true;
485     if (g_shutdownFds[1] != -1)
486     {
487         // Signal multi_handle thread to come out
488         close(g_shutdownFds[1]);
489     }
490     pthread_join(g_multiHandleThread, NULL);
491
492     OCStackResult ret = CHPParserTerminateMultiHandle();
493     if(ret != OC_STACK_OK)
494     {
495         OIC_LOG_V(ERROR, TAG, "Multi handle termination failed: %d", ret);
496     }
497
498     CHPParserLockMutex();
499     g_activeConnections = 0;
500     CHPParserUnlockMutex();
501
502     ret = CHPParserTerminateMutex();
503     if(ret != OC_STACK_OK)
504     {
505         OIC_LOG_V(ERROR, TAG, "mutex termination failed: %d", ret);
506     }
507     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
508     return OC_STACK_OK;
509 }
510
511 static size_t CHPEasyHandleWriteCb(char *buffer, size_t size, size_t num, void *context)
512 {
513     size_t dataToWrite = size * num;
514     if(!dataToWrite)
515     {
516         // Empty payload received. Ignore.
517         return 0;
518     }
519
520     if(!context || !buffer || g_terminateParser)
521     {
522         OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
523         return 0;
524     }
525
526     CHPContext_t* ctx = context;
527     HttpResponse_t *resp = &(ctx->resp);
528
529     if(ctx->writeOffset + dataToWrite > MAX_PAYLOAD_SIZE)
530     {
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;
536         return 0;
537     }
538
539     if (!resp->payload)
540     {
541         resp->payload = OICMalloc(dataToWrite);
542         if (!resp->payload)
543         {
544             OIC_LOG_V(ERROR, TAG, "%s Out of memory!", __func__);
545             return 0;
546         }
547     }
548     else
549     {
550         // Realloc buffer
551         void *newPayload = OICRealloc(resp->payload, ctx->writeOffset + dataToWrite);
552         if (!newPayload)
553         {
554             OIC_LOG_V(ERROR, TAG, "Realloc failed! Current: %u Extra: %u", ctx->writeOffset,
555                                                                            dataToWrite);
556             resp->payloadLength = 0;
557             ctx->writeOffset = 0;
558             OICFree(resp->payload);
559             resp->payload = NULL;
560             return 0;
561         }
562         resp->payload = newPayload;
563     }
564
565     memcpy(resp->payload + ctx->writeOffset, buffer, dataToWrite);
566     ctx->writeOffset += dataToWrite;
567     resp->payloadLength = ctx->writeOffset;
568
569     OIC_LOG_V(DEBUG, TAG, "%s OUT %u : %u", __func__, resp->payloadLength, dataToWrite);
570     return dataToWrite;
571 }
572
573 static size_t CHPEasyHandleReadCb(char *buffer, size_t size, size_t num, void *context)
574 {
575     if(!context || !buffer || g_terminateParser)
576     {
577         OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
578         return CURL_READFUNC_ABORT;
579     }
580
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;
586     return toTransfer;
587 }
588
589 static size_t CHPEasyHandleHeaderCb(char *buffer, size_t size, size_t num, void *context)
590 {
591     size_t dataToWrite = size * num;
592     if(!buffer || !dataToWrite || !context || g_terminateParser)
593     {
594         OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
595         return 0;
596     }
597
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
603      * response.
604      */
605
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
608      * a new response.
609      */
610
611     CHPContext_t *ctx = context;
612     HttpResponse_t *resp = &(ctx->resp);
613     if (dataToWrite > 5)
614     {
615         if (strncmp("HTTP/", buffer, 5) == 0)
616         {
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.
624             return dataToWrite;
625         }
626     }
627
628
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'))
634     {
635         endOfHeader--;
636     }
637
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)
643     {
644         size_t headerFieldLen = ptr - buffer;
645         size_t headerValueLen;
646         char* headerValuePtr;
647
648         /* Skip any white spaces */
649         ptr++;
650         while(ptr <= endOfHeader && *ptr == ' ')
651         {
652             ptr++;
653         }
654
655         if(ptr > endOfHeader)
656         {
657             headerValueLen = 0;
658             headerValuePtr = NULL;
659         }
660         else
661         {
662             // endOfHeader is pointing to last header value character hence +1
663             headerValueLen = endOfHeader - ptr + 1;
664             headerValuePtr = ptr;
665         }
666
667         if (!(resp->headerOptions))
668         {
669             // First header callback, assign storage for header options
670             resp->headerOptions = u_arraylist_create();
671             if (!(resp->headerOptions))
672             {
673                 OIC_LOG(ERROR, TAG, "Memory failed!");
674                 return 0;
675             }
676         }
677
678         HttpHeaderOption_t *option = OICCalloc(1, sizeof(HttpHeaderOption_t));
679         if (!option)
680         {
681             OIC_LOG(ERROR, TAG, "Memory failed!");
682             return 0;
683         }
684
685         headerFieldLen = headerFieldLen > (sizeof(option->optionName) - 1) ?
686                              (sizeof(option->optionName) - 1): headerFieldLen;
687         memcpy(option->optionName, buffer, headerFieldLen);
688         option->optionName[headerFieldLen] = '\0';
689
690         if(headerValueLen)
691         {
692             headerValueLen = headerValueLen > (sizeof(option->optionData) - 1) ?
693                               (sizeof(option->optionData) - 1): headerValueLen;
694             memcpy(option->optionData, headerValuePtr, headerValueLen);
695             option->optionData[headerValueLen] = '\0';
696         }
697
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))
701         {
702             OIC_LOG(ERROR, TAG, "u_arraylist_add failed!");
703             OICFree(option);
704             return 0;
705         }
706     }
707
708     // ignore else as this might be CRLF header lines.
709     return dataToWrite;
710 }
711
712 static OCStackResult CHPInitializeEasyHandle(CURL** easyHandle, HttpRequest_t *req,
713                                              CHPContext_t* handleContext)
714 {
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);
719
720     CURL *e = curl_easy_init();
721     if(!e)
722     {
723         OIC_LOG(ERROR, TAG, "easy init failed!");
724         return OC_STACK_ERROR;
725     }
726
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);
740
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);
752     /* Allow redirect */
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);
759
760     handleContext->writeOffset = 0;
761     handleContext->readOffset = 0;
762     switch(req->method)
763     {
764         case CHP_GET:
765             OIC_LOG(DEBUG, TAG, "Sending GET request");
766             curl_easy_setopt(e, CURLOPT_HTTPGET, 1);
767             break;
768         case CHP_POST:
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;
776             break;
777         case CHP_PUT:
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;
784             break;;
785         case CHP_DELETE:
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");
789             break;
790         default:
791             return OC_STACK_INVALID_METHOD;
792     }
793
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 ": "
797
798     if (req->headerOptions)
799     {
800         HttpHeaderOption_t *option = NULL;
801         int headerCount = u_arraylist_length(req->headerOptions);
802         for(int i = 0; i < headerCount; i++)
803         {
804             option = u_arraylist_get(req->headerOptions, i);
805             if(option)
806             {
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);
810             }
811         }
812     }
813
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);
819
820     *easyHandle = e;
821     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
822     return OC_STACK_OK;
823 }
824
825 OCStackResult CHPPostHttpRequest(HttpRequest_t *req, CHPResponseCallback httpcb,
826                                  void *context)
827 {
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);
831
832     CHPContext_t *ctxt = OICCalloc(1, sizeof(CHPContext_t));
833     if (!ctxt)
834     {
835         OIC_LOG(ERROR, TAG, "Memory failed!");
836         return OC_STACK_NO_MEMORY;
837     }
838
839     ctxt->cb = httpcb;
840     ctxt->context = context;
841     OCStackResult ret = CHPInitializeEasyHandle(&ctxt->easyHandle, req, ctxt);
842     if(ret != OC_STACK_OK)
843     {
844         OIC_LOG_V(ERROR, TAG, "Failed to initialize easy handle [%d]", ret);
845         OICFree(ctxt);
846         return ret;
847     }
848
849     // Add easy_handle to multi_handle
850     CHPParserLockMutex();
851     curl_multi_add_handle(g_multiHandle, ctxt->easyHandle);
852     g_activeConnections++;
853     CHPParserUnlockMutex();
854     // Notify refreshfd
855     ssize_t len = 0;
856     do
857     {
858         len = write(g_refreshFds[1], "w", 1);
859     } while ((len == -1) && (errno == EINTR));
860
861     if ((len == -1) && (errno != EINTR) && (errno != EPIPE))
862     {
863         OIC_LOG_V(DEBUG, TAG, "refresh failed: %s", strerror(errno));
864     }
865
866     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
867     return OC_STACK_OK;
868 }
869