Development of CoAP-HTTP Proxy
authorAbhishek Sharma <ce.abhishek@samsung.com>
Thu, 8 Sep 2016 11:25:24 +0000 (16:55 +0530)
committerUze Choi <uzchoi@samsung.com>
Mon, 12 Sep 2016 06:22:19 +0000 (06:22 +0000)
Wiki page: https://wiki.iotivity.org/coap-http_proxy
JIRA Issue: https://jira.iotivity.org/browse/IOT-1128

Change-Id: I12a6c53589a428bdc1f94ea6d48a692ac07aabfe
Signed-off-by: Abhishek Sharma <ce.abhishek@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/11601
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: jihwan seo <jihwan.seo@samsung.com>
Reviewed-by: Uze Choi <uzchoi@samsung.com>
Tested-by: Uze Choi <uzchoi@samsung.com>
34 files changed:
build_common/SConscript
resource/c_common/oic_string/include/oic_string.h
resource/c_common/oic_string/src/oic_string.c
resource/csdk/SConscript
resource/csdk/connectivity/api/cacommon.h
resource/csdk/connectivity/api/cainterface.h
resource/csdk/connectivity/src/SConscript
resource/csdk/connectivity/src/caprotocolmessage.c
resource/csdk/logger/include/logger.h
resource/csdk/stack/include/ocstack.h
resource/csdk/stack/include/ocstackconfig.h
resource/csdk/stack/include/octypes.h
resource/csdk/stack/include/payload_logging.h
resource/csdk/stack/samples/linux/SimpleClientServer/occlient.cpp
resource/csdk/stack/samples/linux/SimpleClientServer/occlient.h
resource/csdk/stack/samples/tizen/SimpleClientServer/SConscript
resource/csdk/stack/samples/tizen/SimpleClientServer/packaging/com.oic.ri.sample.spec
resource/csdk/stack/samples/tizen/SimpleClientServer/scons/SConscript
resource/csdk/stack/samples/tizen/build/SConscript
resource/csdk/stack/samples/tizen/build/gbsbuild.sh
resource/csdk/stack/samples/tizen/build/packaging/com.oic.ri.spec
resource/csdk/stack/src/occlientcb.c
resource/csdk/stack/src/ocstack.c
service/SConscript
service/coap-http-proxy/SConscript [new file with mode: 0644]
service/coap-http-proxy/include/CoapHttpHandler.h [new file with mode: 0644]
service/coap-http-proxy/include/CoapHttpMap.h [new file with mode: 0644]
service/coap-http-proxy/include/CoapHttpParser.h [new file with mode: 0644]
service/coap-http-proxy/samples/SConscript [new file with mode: 0644]
service/coap-http-proxy/samples/proxy_main.c [new file with mode: 0644]
service/coap-http-proxy/src/CoapHttpHandler.c [new file with mode: 0644]
service/coap-http-proxy/src/CoapHttpMap.c [new file with mode: 0644]
service/coap-http-proxy/src/CoapHttpParser.c [new file with mode: 0644]
tools/tizen/iotivity.spec

index 0264e6a..b700453 100644 (file)
@@ -88,6 +88,7 @@ help_vars.Add(EnumVariable('TARGET_OS', 'Target platform', host, host_target_map
 
 help_vars.Add(BoolVariable('WITH_RA', 'Build with Remote Access module', False))
 help_vars.Add(BoolVariable('WITH_TCP', 'Build with TCP adapter', False))
+help_vars.Add(BoolVariable('WITH_PROXY', 'Build with CoAP-HTTP Proxy', False))
 help_vars.Add(ListVariable('WITH_MQ', 'Build with MQ publisher/broker', 'OFF', ['OFF', 'SUB', 'PUB', 'BROKER']))
 help_vars.Add(BoolVariable('WITH_CLOUD', 'Build including AccountManager class and Cloud Client sample', False))
 help_vars.Add(ListVariable('RD_MODE', 'Resource Directory build mode', 'CLIENT', ['CLIENT', 'SERVER']))
index 4aea907..f736bfa 100644 (file)
@@ -38,6 +38,14 @@ extern "C"
 char *OICStrdup(const char *str);
 
 /**
+ * Convert source string to lower case characters.
+ *
+ * @param str Original valid string which needs to be converted.
+ *
+ */
+void OICStringToLower(char* str);
+
+/**
  * Copies a C string into destination buffer.  Ensures that the destination
  * is null terminated.
  *
index 644b4d2..8a8a960 100644 (file)
@@ -42,6 +42,17 @@ char *OICStrdup(const char *str)
     return dup;
 }
 
+void OICStringToLower(char* str)
+{
+    for (int ch = 0; str[ch] != '\0'; ch++)
+    {
+        if (str[ch] >= 'A' && str[ch] <= 'Z')
+        {
+            str[ch] += 32;
+        }
+    }
+}
+
 char* OICStrcpy(char* dest, size_t destSize, const char* source)
 {
     return OICStrcpyPartial(dest, destSize, source, destSize == 0 ? 0 : destSize - 1);
index 6893732..1d17c0e 100644 (file)
@@ -77,6 +77,9 @@ if liboctbstack_env.get('ROUTING') == 'GW':
 elif liboctbstack_env.get('ROUTING') == 'EP':
        liboctbstack_env.AppendUnique(CPPDEFINES = ['ROUTING_EP'])
 
+if liboctbstack_env.get('WITH_PROXY'):
+       liboctbstack_env.AppendUnique(CPPDEFINES = ['WITH_CHPROXY'])
+
 if target_os not in ['windows']:
        liboctbstack_env.AppendUnique(CFLAGS = ['-Wall'])
 
index cedc763..db06c44 100644 (file)
@@ -70,7 +70,7 @@ extern "C"
 /**
  * Max header options data length.
  */
-#define CA_MAX_HEADER_OPTION_DATA_LENGTH 20
+#define CA_MAX_HEADER_OPTION_DATA_LENGTH 1024
 
 /**
 * Max token length.
@@ -341,7 +341,9 @@ typedef enum
     CA_REQUEST_ENTITY_INCOMPLETE = 408,     /**< Request Entity Incomplete */
     CA_REQUEST_ENTITY_TOO_LARGE = 413,      /**< Request Entity Too Large */
     CA_INTERNAL_SERVER_ERROR = 500,         /**< Internal Server Error */
-    CA_RETRANSMIT_TIMEOUT = 504             /**< Retransmit timeout */
+    CA_BAD_GATEWAY = 502,
+    CA_RETRANSMIT_TIMEOUT = 504,            /**< Retransmit timeout */
+    CA_PROXY_NOT_SUPPORTED = 505            /**< Proxy not enabled to service a request */
     /* Response status code - END HERE */
 } CAResponseResult_t;
 
index 5d50d8d..1c2bbfa 100644 (file)
@@ -231,7 +231,16 @@ CAResult_t CAHandleRequestResponse();
 CAResult_t CASetRAInfo(const CARAInfo_t *caraInfo);
 #endif
 
-
+#ifdef WITH_CHPROXY
+/**
+ * This function sets uri being used for proxy.
+ *
+ * @param uri            NULL terminated resource uri for CoAP-HTTP Proxy.
+ *
+ * @return  ::CA_STATUS_OK or ::CA_STATUS_INVALID_PARAM
+ */
+CAResult_t CASetProxyUri(const char *uri);
+#endif
 
 #ifdef __cplusplus
 } /* extern "C" */
index 16d654c..44921de 100644 (file)
@@ -93,6 +93,9 @@ if env.get('ROUTING') == 'GW':
 elif env.get('ROUTING') == 'EP':
        env.AppendUnique(CPPDEFINES = ['ROUTING_EP'])
 
+if env.get('WITH_PROXY'):
+       env.AppendUnique(CPPDEFINES = ['WITH_CHPROXY'])
+
 if ca_os == 'arduino':
        env.AppendUnique(CPPDEFINES = ['SINGLE_THREAD'])
        env.AppendUnique(CPPDEFINES = ['WITH_ARDUINO'])
index 94810ca..d8f1f06 100644 (file)
 
 static const char COAP_URI_HEADER[] = "coap://[::]/";
 
+#ifdef WITH_CHPROXY
+static char g_chproxyUri[CA_MAX_URI_LENGTH];
+
+CAResult_t CASetProxyUri(const char *uri)
+{
+    VERIFY_NON_NULL(uri, TAG, "uri");
+    OICStrcpy(g_chproxyUri, sizeof (g_chproxyUri), uri);
+    return CA_STATUS_OK;
+}
+#endif
+
 CAResult_t CAGetRequestInfoFromPDU(const coap_pdu_t *pdu, const CAEndpoint_t *endpoint,
                                    CARequestInfo_t *outReqInfo)
 {
@@ -673,7 +684,7 @@ uint32_t CAGetOptionCount(coap_opt_iterator_t opt_iter)
             && COAP_OPTION_ACCEPT != opt_iter.type
             && COAP_OPTION_URI_HOST != opt_iter.type && COAP_OPTION_URI_PORT != opt_iter.type
             && COAP_OPTION_ETAG != opt_iter.type && COAP_OPTION_MAXAGE != opt_iter.type
-            && COAP_OPTION_PROXY_URI != opt_iter.type && COAP_OPTION_PROXY_SCHEME != opt_iter.type)
+            && COAP_OPTION_PROXY_SCHEME != opt_iter.type)
         {
             count++;
         }
@@ -753,6 +764,9 @@ CAResult_t CAGetInfoFromPDU(const coap_pdu_t *pdu, const CAEndpoint_t *endpoint,
     uint32_t optionLength = 0;
     bool isfirstsetflag = false;
     bool isQueryBeingProcessed = false;
+#ifdef WITH_CHPROXY
+    bool isProxyRequest = false;
+#endif
 
     while ((option = coap_option_next(&opt_iter)))
     {
@@ -873,7 +887,6 @@ CAResult_t CAGetInfoFromPDU(const coap_pdu_t *pdu, const CAEndpoint_t *endpoint,
                     COAP_OPTION_URI_HOST == opt_iter.type ||
                     COAP_OPTION_ETAG == opt_iter.type ||
                     COAP_OPTION_MAXAGE == opt_iter.type ||
-                    COAP_OPTION_PROXY_URI == opt_iter.type ||
                     COAP_OPTION_PROXY_SCHEME== opt_iter.type)
             {
                 OIC_LOG_V(INFO, TAG, "option[%d] has an unsupported format [%d]",
@@ -881,6 +894,12 @@ CAResult_t CAGetInfoFromPDU(const coap_pdu_t *pdu, const CAEndpoint_t *endpoint,
             }
             else
             {
+#ifdef WITH_CHPROXY
+                if (COAP_OPTION_PROXY_URI == opt_iter.type)
+                {
+                    isProxyRequest = true;
+                }
+#endif
                 if (idx < count)
                 {
                     if (bufLength <= sizeof(outInfo->options[0].optionData))
@@ -946,7 +965,24 @@ CAResult_t CAGetInfoFromPDU(const coap_pdu_t *pdu, const CAEndpoint_t *endpoint,
             return CA_MEMORY_ALLOC_FAILED;
         }
     }
-
+#ifdef WITH_CHPROXY
+    else if(isProxyRequest && g_chproxyUri[0] != '\0')
+    {
+       /*
+        *   A request for CoAP-HTTP Proxy will not have any uri element as per CoAP specs
+        *   and only COAP_OPTION_PROXY_URI will be present. Use preset proxy uri
+        *   for such requests.
+        */
+        outInfo->resourceUri = OICStrdup(g_chproxyUri);
+        if (!outInfo->resourceUri)
+        {
+            OIC_LOG(ERROR, TAG, "Out of memory");
+            OICFree(outInfo->options);
+            OICFree(outInfo->token);
+            return CA_MEMORY_ALLOC_FAILED;
+        }
+    }
+#endif
     return CA_STATUS_OK;
 
 exit:
index 7ccb51c..4304d23 100644 (file)
@@ -42,6 +42,11 @@ extern "C"
 {
 #endif
 
+/**
+* Helper for unused warning.
+*/
+#define UNUSED(x) (void)(x)
+
 // Use the PCF macro to wrap strings stored in FLASH on the Arduino
 // Example:  OIC_LOG(INFO, TAG, PCF("Entering function"));
 #ifdef ARDUINO
index f168df2..ad06006 100644 (file)
@@ -558,6 +558,15 @@ const OCDPDev_t* OCGetDirectPairedDevices();
 OCStackResult OCDoDirectPairing(void *ctx, OCDPDev_t* peer, OCPrm_t pmSel, char *pinNumber,
                                 OCDirectPairingCB resultCallback);
 
+#ifdef WITH_CHPROXY
+/**
+ * This function sets uri being used for proxy.
+ *
+ * @param uri            NULL terminated resource uri for CoAP-HTTP Proxy.
+ */
+OCStackResult OCSetProxyURI(const char *uri);
+#endif
+
 #if defined(RD_CLIENT) || defined(RD_SERVER)
 /**
  * This function binds an resource unique id to the resource.
index 96afa7a..515d601 100644 (file)
  *  Maximum number of vendor specific header options an application can set or receive
  *  in PDU
  */
-#define MAX_HEADER_OPTIONS (100)
+#ifdef ARDUINO
+#define MAX_HEADER_OPTIONS (2)
+#else
+#define MAX_HEADER_OPTIONS (50)
+#endif
 
 /**
  *  Maximum Length of the vendor specific header option
  */
+#ifdef ARDUINO
 #define MAX_HEADER_OPTION_DATA_LENGTH (20)
+#else
+#define MAX_HEADER_OPTION_DATA_LENGTH (1024)
+#endif
 
 /**
  * Sets the time to live (TTL) for response callback(s).
index 5fafce2..fc1d1f0 100644 (file)
@@ -329,6 +329,12 @@ extern "C" {
 /** RD Discovery bias factor type. */
 #define OC_RSRVD_RD_DISCOVERY_SEL        "sel"
 
+/** Resource URI used to discover Proxy */
+#define OC_RSRVD_PROXY_URI "/oic/chp"
+
+/** Resource URI used to discover Proxy */
+#define OC_RSRVD_PROXY_OPTION_ID 35
+
 /** Base URI. */
 #define OC_RSRVD_BASE_URI                "baseURI"
 
@@ -700,6 +706,7 @@ typedef enum
 typedef enum
 {
     OC_FORMAT_CBOR,
+    OC_FORMAT_JSON,
     OC_FORMAT_UNDEFINED,
     OC_FORMAT_UNSUPPORTED,
 } OCPayloadFormat;
@@ -960,21 +967,25 @@ typedef enum
 {
     OC_EH_OK = 0,
     OC_EH_ERROR,
-    OC_EH_RESOURCE_CREATED, // 2.01
-    OC_EH_RESOURCE_DELETED, // 2.02
-    OC_EH_SLOW, // 2.05
-    OC_EH_FORBIDDEN, // 4.03
-    OC_EH_RESOURCE_NOT_FOUND, // 4.04
-    OC_EH_VALID,   // 2.03
-    OC_EH_CHANGED, // 2.04
-    OC_EH_CONTENT, // 2.05
-    OC_EH_BAD_REQ, // 4.00
-    OC_EH_UNAUTHORIZED_REQ, // 4.01
-    OC_EH_BAD_OPT, // 4.02
-    OC_EH_METHOD_NOT_ALLOWED, // 4.05
-    OC_EH_NOT_ACCEPTABLE, // 4.06
-    OC_EH_INTERNAL_SERVER_ERROR, // 5.00
-    OC_EH_RETRANSMIT_TIMEOUT // 5.04
+    OC_EH_SLOW,
+    OC_EH_RESOURCE_CREATED = 201,
+    OC_EH_RESOURCE_DELETED = 202,
+    OC_EH_VALID = 203,
+    OC_EH_CHANGED = 204,
+    OC_EH_CONTENT = 205,
+    OC_EH_BAD_REQ = 400,
+    OC_EH_UNAUTHORIZED_REQ = 401,
+    OC_EH_BAD_OPT = 402,
+    OC_EH_FORBIDDEN = 403,
+    OC_EH_RESOURCE_NOT_FOUND = 404,
+    OC_EH_METHOD_NOT_ALLOWED = 405,
+    OC_EH_NOT_ACCEPTABLE = 406,
+    OC_EH_TOO_LARGE = 413,
+    OC_EH_UNSUPPORTED_MEDIA_TYPE = 415,
+    OC_EH_INTERNAL_SERVER_ERROR = 500,
+    OC_EH_BAD_GATEWAY = 502,
+    OC_EH_SERVICE_UNAVAILABLE = 503,
+    OC_EH_RETRANSMIT_TIMEOUT = 504
 } OCEntityHandlerResult;
 
 /**
@@ -1116,7 +1127,7 @@ typedef enum
     /** The payload is an OCPresencePayload */
     PAYLOAD_TYPE_PRESENCE,
     /** The payload is an OCRDPayload */
-    PAYLOAD_TYPE_RD
+    PAYLOAD_TYPE_RD,
 } OCPayloadType;
 
 /**
index 5f722f4..121d219 100644 (file)
@@ -145,27 +145,36 @@ INLINE_API void OCPayloadLogRep(LogLevel level, OCRepPayload* payload)
     while(rep)
     {
         OIC_LOG_V(level, PL_TAG, "\tResource #%d", i);
-        OIC_LOG_V(level, PL_TAG, "\tURI:%s", rep->uri);
-        OIC_LOG(level, PL_TAG, "\tResource Types:");
+        if(rep->uri)
+        {
+            OIC_LOG_V(level, PL_TAG, "\tURI:%s", rep->uri);
+        }
+
         OCStringLL* strll =  rep->types;
-        while(strll)
+        if(strll)
         {
-            OIC_LOG_V(level, PL_TAG, "\t\t%s", strll->value);
-            strll = strll->next;
+            OIC_LOG(level, PL_TAG, "\tResource Types:");
+            while(strll)
+            {
+                OIC_LOG_V(level, PL_TAG, "\t\t%s", strll->value);
+                strll = strll->next;
+            }
         }
-        OIC_LOG(level, PL_TAG, "\tInterfaces:");
+
         strll =  rep->interfaces;
-        while(strll)
+        if(strll)
         {
-            OIC_LOG_V(level, PL_TAG, "\t\t%s", strll->value);
-            strll = strll->next;
+            OIC_LOG(level, PL_TAG, "\tInterfaces:");
+            while(strll)
+            {
+                OIC_LOG_V(level, PL_TAG, "\t\t%s", strll->value);
+                strll = strll->next;
+            }
         }
 
         // TODO Finish Logging: Values
         OCRepPayloadValue* val = rep->values;
-
         OIC_LOG(level, PL_TAG, "\tValues:");
-
         while(val)
         {
             switch(val->type)
@@ -190,9 +199,8 @@ INLINE_API void OCPayloadLogRep(LogLevel level, OCRepPayload* payload)
                     OIC_LOG_BUFFER(level, PL_TAG, val->ocByteStr.bytes, val->ocByteStr.len);
                     break;
                 case OCREP_PROP_OBJECT:
-                    // Note: Only prints the URI (if available), to print further, you'll
-                    // need to dig into the object better!
                     OIC_LOG_V(level, PL_TAG, "\t\t%s(OCRep):%s", val->name, val->obj->uri);
+                    OCPayloadLogRep(level, val->obj);
                     break;
                 case OCREP_PROP_ARRAY:
                     switch(val->arr.type)
@@ -228,11 +236,22 @@ INLINE_API void OCPayloadLogRep(LogLevel level, OCRepPayload* payload)
                                     val->arr.dimensions[2]);
                             break;
                         case OCREP_PROP_OBJECT:
+                        {
                             OIC_LOG_V(level, PL_TAG, "\t\t%s(OCRep array):%zu x %zu x %zu",
                                     val->name,
                                     val->arr.dimensions[0], val->arr.dimensions[1],
                                     val->arr.dimensions[2]);
+
+                            size_t i = 0;
+                            if (val->arr.dimensions[0] > 0)
+                            {
+                                for (i = 0 ; i < val->arr.dimensions[0] ; i++)
+                                {
+                                    OCPayloadLogRep(level, val->arr.objArray[i]);
+                                }
+                            }
                             break;
+                        }
                         default:
                             OIC_LOG_V(ERROR, PL_TAG, "\t\t%s <-- Unknown/unsupported array type!",
                                     val->name);
@@ -401,7 +420,7 @@ INLINE_API void OCPayloadLogSecurity(LogLevel level, OCSecurityPayload* payload)
     {
         // Add a zero-character string terminator.
         char *securityData = (char *)OICMalloc(payloadSize + 1);
-        
+
         if (securityData)
         {
             memcpy(securityData, payload->securityData, payloadSize);
index 061c94f..4a90b45 100644 (file)
@@ -66,6 +66,10 @@ static OCDevAddr serverAddr;
 static char discoveryAddr[100];
 static std::string coapServerResource = "/a/light";
 
+// Following resource is used to verify coap-http proxy
+static std::string coapProxyResource = OC_RSRVD_PROXY_URI;
+static std::string httpResource;    // Will be taken as user input
+
 #ifdef WITH_PRESENCE
 // The handle for observe registration
 OCDoHandle gPresenceHandle;
@@ -105,7 +109,7 @@ OCPayload* putPayload()
 
 static void PrintUsage()
 {
-    OIC_LOG(INFO, TAG, "Usage : occlient -u <0|1> -t <1..17> -c <0|1>");
+    OIC_LOG(INFO, TAG, "Usage : occlient -u <0|1> -t <1..21> -c <0|1>");
     OIC_LOG(INFO, TAG, "-u <0|1> : Perform multicast/unicast discovery of resources");
     OIC_LOG(INFO, TAG, "-c 0 : Use Default connectivity(IP)");
     OIC_LOG(INFO, TAG, "-c 1 : IP Connectivity Type");
@@ -140,6 +144,7 @@ static void PrintUsage()
             "add  vendor specific header options");
     OIC_LOG(INFO, TAG, "-t 19 :  Discover Platform");
     OIC_LOG(INFO, TAG, "-t 20 :  Discover Devices");
+    OIC_LOG(INFO, TAG, "-t 21 -p \"http_uri\":  Discover Proxy and Initiate Nonconfirmable Get Request");
 }
 
 OCStackResult InvokeOCDoResource(std::ostringstream &query,
@@ -158,7 +163,8 @@ OCStackResult InvokeOCDoResource(std::ostringstream &query,
     cbData.context = (void*)DEFAULT_CONTEXT_VALUE;
     cbData.cd = NULL;
 
-    ret = OCDoResource(&handle, method, query.str().c_str(), remoteAddr,
+    const char *uri = query.str().length() ? query.str().c_str() : NULL;
+    ret = OCDoResource(&handle, method, uri, remoteAddr,
                        (method == OC_REST_PUT) ? putPayload() : NULL,
                        (ConnType), qos, &cbData, options, numOptions);
 
@@ -403,9 +409,12 @@ OCStackApplicationResult discoveryReqCB(void* ctx, OCDoHandle /*handle*/,
 
         OCResourcePayload *resource = (OCResourcePayload*) payload->resources;
         int found = 0;
+
+        std::string resourceToFind = (TestCase == TEST_PROXY_GET_REQ_NON) ?
+                                            coapProxyResource : coapServerResource;
         while (resource)
         {
-            if(resource->uri && strcmp(resource->uri, coapServerResource.c_str()) == 0)
+            if(resource->uri && strcmp(resource->uri, resourceToFind.c_str()) == 0)
             {
                 found = 1;
                 break;
@@ -415,7 +424,7 @@ OCStackApplicationResult discoveryReqCB(void* ctx, OCDoHandle /*handle*/,
 
         if(!found)
         {
-            OIC_LOG_V (INFO, TAG, "No /a/light in payload");
+            OIC_LOG_V (INFO, TAG, "No %s in payload", resourceToFind.c_str());
             return OC_STACK_KEEP_TRANSACTION;
         }
 
@@ -455,6 +464,9 @@ OCStackApplicationResult discoveryReqCB(void* ctx, OCDoHandle /*handle*/,
             case TEST_OBS_REQ_CON:
                 InitObserveRequest(OC_HIGH_QOS);
                 break;
+            case TEST_PROXY_GET_REQ_NON:
+                InitProxyGetRequest(OC_LOW_QOS);
+                break;
 #ifdef WITH_PRESENCE
             case TEST_OBS_PRESENCE:
             case TEST_OBS_PRESENCE_WITH_FILTER:
@@ -691,7 +703,27 @@ int InitDeleteRequest(OCQualityOfService qos)
     return result;
 }
 
-int InitGetRequest(OCQualityOfService qos, uint8_t withVendorSpecificHeaderOptions, bool getWithQuery)
+int InitProxyGetRequest(OCQualityOfService qos)
+{
+    OIC_LOG(INFO, TAG, "InitProxyGetRequest");
+    OCHeaderOption option;
+    memset(&option, 0, sizeof(option));
+
+    option.protocolID = OC_COAP_ID;
+    option.optionID = OC_RSRVD_PROXY_OPTION_ID;
+    memcpy(option.optionData, (uint8_t *)httpResource.c_str(), httpResource.length());
+    option.optionLength = httpResource.length();
+
+    std::ostringstream query;
+    // A request with proxy uri shall not have resource uri
+    // query << coapProxyResource;
+
+    return (InvokeOCDoResource(query, &serverAddr, OC_REST_GET,
+                (qos == OC_HIGH_QOS) ? OC_HIGH_QOS : OC_LOW_QOS, getReqCB, &option, 1));
+}
+
+int InitGetRequest(OCQualityOfService qos, uint8_t withVendorSpecificHeaderOptions,
+                   bool getWithQuery)
 {
 
     OCHeaderOption options[MAX_HEADER_OPTIONS];
@@ -809,7 +841,7 @@ int main(int argc, char* argv[])
 {
     int opt;
 
-    while ((opt = getopt(argc, argv, "u:t:c:")) != -1)
+    while ((opt = getopt(argc, argv, "u:t:c:p:")) != -1)
     {
         switch(opt)
         {
@@ -822,6 +854,12 @@ int main(int argc, char* argv[])
             case 'c':
                 Connectivity = atoi(optarg);
                 break;
+            case 'p':
+                if(optarg)
+                {
+                    httpResource = optarg;
+                }
+                break;
             default:
                 PrintUsage();
                 return -1;
@@ -830,7 +868,8 @@ int main(int argc, char* argv[])
 
     if ((UnicastDiscovery != 0 && UnicastDiscovery != 1) ||
             (TestCase < TEST_DISCOVER_REQ || TestCase >= MAX_TESTS) ||
-            (Connectivity < CT_ADAPTER_DEFAULT || Connectivity >= MAX_CT))
+            (Connectivity < CT_ADAPTER_DEFAULT || Connectivity >= MAX_CT) ||
+            (TestCase == TEST_PROXY_GET_REQ_NON && httpResource.length() == 0) )
     {
         PrintUsage();
         return -1;
index 2ed2e3d..caa0ec9 100644 (file)
@@ -63,6 +63,7 @@ typedef enum {
     TEST_GET_REQ_NON_WITH_VENDOR_HEADER_OPTIONS,
     TEST_DISCOVER_PLATFORM_REQ,
     TEST_DISCOVER_DEV_REQ,
+    TEST_PROXY_GET_REQ_NON,
     MAX_TESTS
 } CLIENT_TEST;
 
@@ -94,9 +95,6 @@ std::string getIPAddrTBServer(OCClientResponse * clientResponse);
 /* Get the port number the server is listening on */
 std::string getPortTBServer(OCClientResponse * clientResponse);
 
-/* Returns the query string for GET and PUT operations */
-std::string getQueryStrForGetPut(OCClientResponse * clientResponse);
-
 /* Following are initialization functions for GET, Observe, PUT
  * POST, Delete & Discovery operations
  */
@@ -110,11 +108,7 @@ int InitGetRequest(OCQualityOfService qos);
 int InitDeviceDiscovery(OCQualityOfService qos);
 int InitPlatformDiscovery(OCQualityOfService qos);
 int InitDiscovery(OCQualityOfService qos);
-
-/* Function to retrieve ip address, port no. of the server
- *  and query for the operations to be performed.
- */
-void parseClientResponse(OCClientResponse * clientResponse);
+int InitProxyGetRequest(OCQualityOfService qos);
 
 /* Call delete operation on already deleted resource */
 void* RequestDeleteDeathResourceTask(void* myqos);
index f48fcb0..532eb20 100644 (file)
@@ -56,6 +56,7 @@ help_vars.Add(ListVariable('TARGET_TRANSPORT', 'Target transport', 'ALL', ['ALL'
 help_vars.Add(EnumVariable('TARGET_ARCH', 'Target architecture', default_arch, os_arch_map[target_os]))
 help_vars.Add(EnumVariable('SECURED', 'Build with DTLS', '0', allowed_values=('0', '1')))
 help_vars.Add(EnumVariable('ROUTING', 'Enable routing', 'EP', allowed_values=('GW', 'EP')))
+help_vars.Add(BoolVariable('WITH_PROXY', 'CoAP-HTTP Proxy', False)) # set to 'no', 'false' or 0 for debug
 
 ######################################################################
 # Platform(build target) specific options: SDK/NDK & toolchain
@@ -177,6 +178,8 @@ if env.get('ROUTING') == 'GW':
 elif env.get('ROUTING') == 'EP':
        env.AppendUnique(CPPDEFINES = ['ROUTING_EP'])
 env.AppendUnique(CPPDEFINES = ['__TIZEN__'])
+if env.get('WITH_PROXY'):
+       env.AppendUnique(CPPDEFINES = ['WITH_CHPROXY'])
 
 Export('env')
 
index 3054430..832acf1 100644 (file)
@@ -31,7 +31,7 @@ OIC RIsample application
 %build
 
 scons TARGET_OS=tizen -c
-scons TARGET_OS=tizen TARGET_TRANSPORT=%{TARGET_TRANSPORT} SECURED=%{SECURED} RELEASE=%{RELEASE} ROUTING=%{ROUTING} WITH_TCP=%{WITH_TCP}
+scons TARGET_OS=tizen TARGET_TRANSPORT=%{TARGET_TRANSPORT} SECURED=%{SECURED} RELEASE=%{RELEASE} ROUTING=%{ROUTING} WITH_TCP=%{WITH_TCP} WITH_PROXY=%{WITH_PROXY} 
 
 %install
 
index 5237143..9ec20c5 100644 (file)
@@ -37,6 +37,9 @@ if routing == 'GW':
 elif routing == 'EP':
        env.AppendUnique(CPPDEFINES = ['ROUTING_EP'])
 
+if env.get('WITH_PROXY'):
+       env.AppendUnique(CPPDEFINES = ['WITH_CHPROXY'])
+       
 env.Append(LIBS=[
   'm', 'pthread', 'rt', 'dl', 'stdc++', 'gobject-2.0', 'gio-2.0', 'glib-2.0', 'capi-network-wifi', 'dlog', 'capi-network-bluetooth', 'connectivity_abstraction', 'coap', 'octbstack', 'ocsrm', 'c_common'
 ])
index e3c922b..9a2c022 100644 (file)
@@ -13,6 +13,7 @@ release_mode = env.get('RELEASE')
 secured = env.get('SECURED')
 logging = env.get('LOGGING')
 routing = env.get('ROUTING')
+with_proxy = env.get('WITH_PROXY')
 with_tcp = env.get('WITH_TCP')
 
 env.PrependUnique(CPPPATH = [
@@ -30,11 +31,14 @@ elif routing == 'EP':
        env.AppendUnique(CPPDEFINES = ['ROUTING_EP'])
 env.AppendUnique(CPPDEFINES = ['__TIZEN__'])
 
+if env.get('WITH_PROXY'):
+       env.AppendUnique(CPPDEFINES = ['WITH_CHPROXY'])
+
 print "Given Transport is %s" % transport
 print "Given OS is %s" % target_os
 
 if target_os == 'tizen':
-       command = "sh resource/csdk/stack/samples/tizen/build/gbsbuild.sh %s %s %s %s %s %s %s" % (transport, secured, buildsample, release_mode, logging, routing, with_tcp)
+       command = "sh resource/csdk/stack/samples/tizen/build/gbsbuild.sh %s %s %s %s %s %s %s %s" % (transport, secured, buildsample, release_mode, logging, routing, with_tcp, with_proxy)
        print "Created Command is %s" % command
        gbs_script = env.Command('gbs_build', None, command)
        AlwaysBuild ('gbs_script')
\ No newline at end of file
index af03a20..d0b488f 100644 (file)
@@ -29,6 +29,9 @@ export ROUTING=$6
 echo $7
 export WITH_TCP=$7
 
+echo $8
+export WITH_PROXY=$8
+
 echo $TARGET_TRANSPORT
 echo $BUILD_SAMPLE
 
@@ -92,7 +95,7 @@ if [ ! -d .git ]; then
 fi
 
 echo "Calling core gbs build command"
-gbscommand="gbs build -A armv7l -B ~/GBS-ROOT-RI-OIC --include-all --repository ./ --define 'TARGET_TRANSPORT $1' --define 'SECURED $2' --define 'RELEASE $4' --define 'LOGGING $5' --define 'ROUTING $6' --define 'WITH_TCP $7'"
+gbscommand="gbs build -A armv7l -B ~/GBS-ROOT-RI-OIC --include-all --repository ./ --define 'TARGET_TRANSPORT $1' --define 'SECURED $2' --define 'RELEASE $4' --define 'LOGGING $5' --define 'ROUTING $6' --define 'WITH_TCP $7' --define 'WITH_PROXY $8'"
 echo $gbscommand
 if eval $gbscommand; then
    echo "Core build is successful"
@@ -115,7 +118,7 @@ if echo $BUILD_SAMPLE|grep -qi '^ON$'; then
       git commit -m "Initial commit"
    fi
    echo "Calling sample gbs build command"
-   gbscommand="gbs build -A armv7l -B ~/GBS-ROOT-RI-OIC --include-all --repository ./ --define 'TARGET_TRANSPORT $1' --define 'SECURED $2' --define 'RELEASE $4' --define 'LOGGING $5' --define 'ROUTING $6' --define 'WITH_TCP $7'"
+   gbscommand="gbs build -A armv7l -B ~/GBS-ROOT-RI-OIC --include-all --repository ./ --define 'TARGET_TRANSPORT $1' --define 'SECURED $2' --define 'RELEASE $4' --define 'LOGGING $5' --define 'ROUTING $6' --define 'WITH_TCP $7' --define 'WITH_PROXY $8'"
    echo $gbscommand
    if eval $gbscommand; then
       echo "Sample build is successful"
index 09a9ca4..ea8f03f 100644 (file)
@@ -37,7 +37,7 @@ SLP oicri application
 echo %{ROOTDIR}
 
 scons TARGET_OS=tizen -c
-scons TARGET_OS=tizen TARGET_TRANSPORT=%{TARGET_TRANSPORT} SECURED=%{SECURED} RELEASE=%{RELEASE} LOGGING=%{LOGGING} ROUTING=%{ROUTING} WITH_TCP=%{WITH_TCP}
+scons TARGET_OS=tizen TARGET_TRANSPORT=%{TARGET_TRANSPORT} SECURED=%{SECURED} RELEASE=%{RELEASE} LOGGING=%{LOGGING} ROUTING=%{ROUTING} WITH_TCP=%{WITH_TCP} WITH_PROXY=%{WITH_PROXY}
 
 %install
 mkdir -p %{DEST_INC_DIR}
index 42100d6..872e8c4 100644 (file)
@@ -49,7 +49,7 @@ AddClientCB (ClientCB** clientCB, OCCallbackData* cbData,
              OCDevAddr *devAddr, char * requestUri,
              char * resourceTypeName, uint32_t ttl)
 {
-    if (!clientCB || !cbData || !handle || !requestUri || tokenLength > CA_MAX_TOKEN_LEN)
+    if (!clientCB || !cbData || !handle || tokenLength > CA_MAX_TOKEN_LEN)
     {
         return OC_STACK_INVALID_PARAM;
     }
@@ -156,8 +156,11 @@ void DeleteClientCB(ClientCB * cbNode)
         CADestroyToken (cbNode->token);
         OICFree(cbNode->devAddr);
         OICFree(cbNode->handle);
-        OIC_LOG_V (INFO, TAG, "Deleting callback with uri %s", cbNode->requestUri);
-        OICFree(cbNode->requestUri);
+        if (cbNode->requestUri)
+        {
+            OIC_LOG_V (INFO, TAG, "Deleting callback with uri %s", cbNode->requestUri);
+            OICFree(cbNode->requestUri);
+        }
         if (cbNode->deleteCallback)
         {
             cbNode->deleteCallback(cbNode->context);
index f11501c..a492031 100644 (file)
@@ -419,6 +419,25 @@ static OCStackResult OCSendRequest(const CAEndpoint_t *object, CARequestInfo_t *
 // Internal functions
 //-----------------------------------------------------------------------------
 
+bool checkProxyUri(OCHeaderOption *options, uint8_t numOptions)
+{
+    if (!options || 0 == numOptions)
+    {
+        OIC_LOG (INFO, TAG, "No options present");
+        return false;
+    }
+
+    for (uint8_t i = 0; i < numOptions; i++)
+    {
+        if (options[i].protocolID == OC_COAP_ID && options[i].optionID == OC_RSRVD_PROXY_OPTION_ID)
+        {
+            OIC_LOG(DEBUG, TAG, "Proxy URI is present");
+            return true;
+        }
+    }
+    return false;
+}
+
 uint32_t GetTicks(uint32_t afterMilliSeconds)
 {
     coap_tick_t now;
@@ -1341,25 +1360,33 @@ void OCHandleResponse(const CAEndpoint_t* endPoint, const CAResponseInfo_t* resp
                          cbNode->method == OC_REST_OBSERVE_ALL ||
                          cbNode->method == OC_REST_DELETE)
                 {
-                    char targetUri[MAX_URI_LENGTH];
-                    snprintf(targetUri, MAX_URI_LENGTH, "%s?rt=%s", OC_RSRVD_RD_URI,
-                            OC_RSRVD_RESOURCE_TYPE_RDPUBLISH);
-                    if (strcmp(targetUri, cbNode->requestUri) == 0)
-                    {
-                        type = PAYLOAD_TYPE_RD;
-                    }
-                    else if (strcmp(OC_RSRVD_PLATFORM_URI, cbNode->requestUri) == 0)
-                    {
-                        type = PAYLOAD_TYPE_PLATFORM;
-                    }
-                    else if (strcmp(OC_RSRVD_DEVICE_URI, cbNode->requestUri) == 0)
+                    if (cbNode->requestUri)
                     {
-                        type = PAYLOAD_TYPE_DEVICE;
+                        char targetUri[MAX_URI_LENGTH];
+                        snprintf(targetUri, MAX_URI_LENGTH, "%s?rt=%s", OC_RSRVD_RD_URI,
+                                OC_RSRVD_RESOURCE_TYPE_RDPUBLISH);
+                        if (strcmp(targetUri, cbNode->requestUri) == 0)
+                        {
+                            type = PAYLOAD_TYPE_RD;
+                        }
+                        else if (strcmp(OC_RSRVD_PLATFORM_URI, cbNode->requestUri) == 0)
+                        {
+                            type = PAYLOAD_TYPE_PLATFORM;
+                        }
+                        else if (strcmp(OC_RSRVD_DEVICE_URI, cbNode->requestUri) == 0)
+                        {
+                            type = PAYLOAD_TYPE_DEVICE;
+                        }
+                        if (type == PAYLOAD_TYPE_INVALID)
+                        {
+                            OIC_LOG_V(INFO, TAG, "Assuming PAYLOAD_TYPE_REPRESENTATION: %d %s",
+                                    cbNode->method, cbNode->requestUri);
+                            type = PAYLOAD_TYPE_REPRESENTATION;
+                        }
                     }
-                    if (type == PAYLOAD_TYPE_INVALID)
+                    else
                     {
-                        OIC_LOG_V(INFO, TAG, "Assuming PAYLOAD_TYPE_REPRESENTATION: %d %s",
-                                cbNode->method, cbNode->requestUri);
+                        OIC_LOG(INFO, TAG, "No Request URI, PROXY URI");
                         type = PAYLOAD_TYPE_REPRESENTATION;
                     }
                 }
@@ -2619,7 +2646,6 @@ OCStackResult OCDoResource(OCDoHandle *handle,
     // Validate input parameters
     VERIFY_NON_NULL(cbData, FATAL, OC_STACK_INVALID_CALLBACK);
     VERIFY_NON_NULL(cbData->cb, FATAL, OC_STACK_INVALID_CALLBACK);
-    VERIFY_NON_NULL(requestUri , FATAL, OC_STACK_INVALID_URI);
 
     OCStackResult result = OC_STACK_ERROR;
     CAResult_t caResult;
@@ -2645,10 +2671,18 @@ OCStackResult OCDoResource(OCDoHandle *handle,
     adapter = (OCTransportAdapter)(connectivityType >> CT_ADAPTER_SHIFT);
     flags = (OCTransportFlags)(connectivityType & CT_MASK_FLAGS);
 
-    result = ParseRequestUri(requestUri, adapter, flags, &devAddr, &resourceUri, &resourceType);
-    if (result != OC_STACK_OK)
+    if (requestUri)
+    {
+        result = ParseRequestUri(requestUri, adapter, flags, &devAddr, &resourceUri, &resourceType);
+        if (result != OC_STACK_OK)
+        {
+            OIC_LOG_V(DEBUG, TAG, "Unable to parse uri: %s", requestUri);
+            goto exit;
+        }
+    }
+    else if (!checkProxyUri(options, numOptions))
     {
-        OIC_LOG_V(DEBUG, TAG, "Unable to parse uri: %s", requestUri);
+        OIC_LOG(ERROR, TAG, "Request doesn't contain RequestURI/Proxy URI");
         goto exit;
     }
 
@@ -4782,6 +4816,13 @@ bool OCResultToSuccess(OCStackResult ocResult)
     }
 }
 
+#ifdef WITH_CHPROXY
+OCStackResult OCSetProxyURI(const char *uri)
+{
+    return CAResultToOCResult(CASetProxyUri(uri));
+}
+#endif
+
 #if defined(RD_CLIENT) || defined(RD_SERVER)
 OCStackResult OCBindResourceInsToResource(OCResourceHandle handle, uint8_t ins)
 {
index 2f92966..22eb6db 100755 (executable)
@@ -55,6 +55,10 @@ if target_os not in ['arduino','darwin', 'ios', 'windows']:
     #if env.get('WITH_RD') == '1':
         #SConscript('resource-directory/SConscript')
 
+    # Build coap-http-proxy project
+    if target_os in ['linux'] and env.get('WITH_PROXY', False):
+        SConscript('coap-http-proxy/SConscript')
+
 # Build EasySetup module
 if target_os in ['arduino', 'android', 'linux','tizen']:
     SConscript('easy-setup/SConscript')
diff --git a/service/coap-http-proxy/SConscript b/service/coap-http-proxy/SConscript
new file mode 100644 (file)
index 0000000..cc3aa2c
--- /dev/null
@@ -0,0 +1,86 @@
+#******************************************************************
+#
+# Copyright 2016 Samsung Electronics All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+##
+# CoAP-HTTP Proxy build script
+##
+Import('env')
+import os
+local_env = env.Clone()
+
+if local_env.get('LOGGING'):
+    local_env.AppendUnique(CPPDEFINES = ['-DTB_LOG'])
+
+if env.get('RELEASE'):
+    env.AppendUnique(CCFLAGS = ['-Os'])
+    env.AppendUnique(CPPDEFINES = ['NDEBUG'])
+else:
+    env.AppendUnique(CCFLAGS = ['-g'])
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+
+######################################################################
+# Build flags
+######################################################################
+local_env.AppendUnique(CPPPATH = ['include',
+                        os.path.join(src_dir, 'resource/csdk/stack/include'),
+                        os.path.join(src_dir, 'resource/csdk/connectivity/common/inc/'),
+                        os.path.join(src_dir, 'resource/csdk/connectivity/lib/libcoap-4.1.1'),
+                        os.path.join(src_dir, 'extlibs/cjson'),
+               ])
+local_env.PrependUnique(LIBS = ['oc', 'octbstack', 'oc_logger', 'connectivity_abstraction', 'coap'])
+if target_os not in ['windows']:
+    local_env.AppendUnique(CXXFLAGS = ['-O2', '-g', '-Wall', '-Wextra'])
+
+if target_os in ['linux']:
+    local_env.AppendUnique(LIBS = ['pthread', 'curl'])
+
+if target_os == 'android':
+    local_env.AppendUnique(CXXFLAGS = ['-frtti', '-fexceptions'])
+    local_env.AppendUnique(LIBS = ['gnustl_static'])
+
+    if not env.get('RELEASE'):
+        rd_env.AppendUnique(LIBS = ['log'])
+######################################################################
+# Source files and Targets
+######################################################################
+
+proxy_src = [
+       './src/CoapHttpHandler.c',
+       './src/CoapHttpMap.c',
+       './src/CoapHttpParser.c',
+]
+
+if target_os in ['tizen'] :
+    proxysdk = local_env.SharedLibrary('coap_http_proxy', proxy_src)
+else :
+    proxysdk = local_env.StaticLibrary('coap_http_proxy', proxy_src)
+
+local_env.InstallTarget(proxysdk, 'coap_http_proxy')
+local_env.UserInstallTargetLib(proxysdk, 'coap_http_proxy')
+local_env.UserInstallTargetHeader('include/CoapHttpHandler.h', 'service/coap-http-proxy', 'CoapHttpHandler.h')
+
+
+######################################################################
+# Samples for the proxy
+######################################################################
+if target_os in ['linux']:
+    SConscript('samples/SConscript')
diff --git a/service/coap-http-proxy/include/CoapHttpHandler.h b/service/coap-http-proxy/include/CoapHttpHandler.h
new file mode 100644 (file)
index 0000000..7843000
--- /dev/null
@@ -0,0 +1,58 @@
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+/**
+ * @file
+ * This file contains the functions to initiate request or response handling by CHP
+ */
+
+#ifndef COAP_HTTP_HANDLER_H_
+#define COAP_HTTP_HANDLER_H_
+
+#include "ocstack.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Initialize the CoAP HTTP Proxy.
+ * @return  ::OC_STACK_OK or Appropriate error code.
+ */
+OCStackResult CHPInitialize();
+
+/**
+ * Terminate the CoAP HTTP Proxy.
+ * @return  ::OC_STACK_OK or Appropriate error code.
+ */
+OCStackResult CHPTerminate();
+
+/**
+ * API to check if CoAP-HTTP Proxy is initialized.
+ * @return  true if initialized else false.
+ */
+bool CHPIsInitialized();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/service/coap-http-proxy/include/CoapHttpMap.h b/service/coap-http-proxy/include/CoapHttpMap.h
new file mode 100644 (file)
index 0000000..c0e9d6e
--- /dev/null
@@ -0,0 +1,94 @@
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+/**
+ * @file
+ * This file contains CoAP to HTTP mapping
+ */
+
+#ifndef COAP_HTTP_MAP_H_
+#define COAP_HTTP_MAP_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "CoapHttpParser.h"
+#include "cJSON.h"
+
+/**
+ * Function to get CoAP code for an HTTP code.
+ * @param[in]   httpCode            HTTP Code.
+ * @param[in]   method              Request Method type
+ * @param[out]  ocfCode             Corresponding OCF code.
+ * @return ::OC_STACK_OK or appropriate error code.
+ */
+OCStackResult CHPGetOCCode(const HttpResponseResult_t httpCode, const OCMethod method,
+                           OCEntityHandlerResult *ocfCode);
+
+/**
+ * Function to get CoAP option for an HTTP option.
+ * @param[in]   httpOption        HTTP Option.
+ * @param[out]  ret               Corresponding CoAP option.
+ * @return ::OC_STACK_OK or appropriate error code.
+ */
+OCStackResult CHPGetOCOption(const HttpHeaderOption_t *httpOption, OCHeaderOption *ret);
+
+/**
+ * Function to get CoAP payload format for HTTP payload format.
+ * @param[in]   httpContentType   HTTP payload format.
+ * @return CoAP payload format.
+ */
+OCPayloadFormat CHPGetOCContentType(const char *httpContentType);
+
+/**
+ * Function to get HTTP method for OCF method.
+ * @param[in]   method            OCF method.
+ * @param[out]  httpMethod        Corresponding HTTP method.
+ * @return ::OC_STACK_OK or appropriate error code.
+ */
+OCStackResult CHPGetHttpMethod(const OCMethod method, HttpMethod_t *httpMethod);
+
+/**
+ * Function to get HTTP option for OCF option.
+ * @param[in]   option            OCF option.
+ * @param[out]  ret               Corresponding HTTP option.
+ * @return ::OC_STACK_OK or appropriate error code.
+ */
+OCStackResult CHPGetHttpOption(const OCHeaderOption* option, HttpHeaderOption_t **ret);
+
+/**
+ * Function to convert Json payload to CBOR representational payload.
+ * @param[in]   rootJSon          Json payload.
+ * @param[out]  payload           CBor representational payload.
+ */
+void CHPJsonToRepPayload(cJSON* rootJSon, OCRepPayload* payload);
+
+/**
+ * Function to convert CBOR representational payload to Json payload.
+ * @param[in]   repData           Cbor representational payload.
+ * @return CJson payload.
+ */
+cJSON* CHPRepPayloadToJson(OCRepPayload* repData);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/service/coap-http-proxy/include/CoapHttpParser.h b/service/coap-http-proxy/include/CoapHttpParser.h
new file mode 100644 (file)
index 0000000..5e5067c
--- /dev/null
@@ -0,0 +1,195 @@
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+/**
+ * @file
+ * This file contains HTTP parsing functions
+ */
+
+#ifndef COAP_HTTP_PARSER_H_
+#define COAP_HTTP_PARSER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <unistd.h>
+#include "uarraylist.h"
+#include "octypes.h"
+
+#define CHP_MAX_HF_DATA_LENGTH 1024
+#define CHP_MAX_HF_NAME_LENGTH 255
+#define JSON_CONTENT_TYPE "application/json"
+#define CBOR_CONTENT_TYPE "application/cbor"
+#define ACCEPT_MEDIA_TYPE (CBOR_CONTENT_TYPE "; q=1.0, " JSON_CONTENT_TYPE "; q=0.5")
+
+// HTTP Option types
+#define HTTP_OPTION_CACHE_CONTROL   "cache-control"
+#define HTTP_OPTION_ACCEPT          "accept"
+#define HTTP_OPTION_IF_MATCH        "if-match"
+#define HTTP_OPTION_IF_NONE_MATCH   "if-none-match"
+#define HTTP_OPTION_ETAG            "etag"
+#define HTTP_OPTION_CONTENT_TYPE    "content-type"
+#define HTTP_OPTION_CONTENT_LENGTH  "content-length"
+#define HTTP_OPTION_EXPIRES         "expires"
+
+/**
+ * @enum HttpResponseResult_t
+ * Enums for HTTP Response values
+ */
+typedef enum
+{
+    /* Response status code - START HERE */
+    CHP_EMPTY = 0,                       /**< Empty */
+    CHP_SUCCESS = 200,                   /**< Success */
+    CHP_CREATED = 201,                   /**< Created */
+    CHP_ACCEPTED = 202,                  /**< Accepted */
+    CHP_NO_CONTENT = 204,                /**< No Content */
+    CHP_RESET_CONTENT = 205,             /**< Reset content */
+    CHP_NOT_MODIFIED = 304,              /**< Not Modified */
+    CHP_BAD_REQ = 400,                   /**< Bad Request */
+    CHP_UNAUTHORIZED_REQ = 401,          /**< Unauthorized Request */
+    CHP_FORBIDDEN_REQ = 403,             /**< Forbidden Request */
+    CHP_NOT_FOUND = 404,                 /**< Not found */
+    CHP_NOT_ACCEPTABLE = 406,            /**< Not Acceptable */
+    CHP_REQUEST_ENTITY_TOO_LARGE = 413,  /**< Request Entity Too Large */
+    CHP_REQUEST_URI_TOO_LARGE = 414,     /**< Request URI Too Large */
+    CHP_UNSUPPORTED_MEDIA_TYPE = 415,    /**< Unsupported Media type */
+    CHP_INTERNAL_SERVER_ERROR = 500,     /**< Internal server Error */
+    CHP_NOT_IMPLEMENTED = 501,           /**< Not Implemented */
+    CHP_BAD_GATEWAY = 502,               /**< Bad Gateway */
+    CHP_SERVICE_UNAVAILABLE = 503,       /**< Service Unavailable */
+    CHP_GATEWAY_TIMEOUT = 504,           /**< Gateway Timeout */
+    CHP_VERSION_NOT_SUPPORTED = 505      /**< Version not supported */
+    /* Response status code - END HERE */
+} HttpResponseResult_t;
+
+/**
+ * Header fields structure to be filled
+ *
+ * This structure is used to hold header information.
+ */
+typedef struct
+{
+    uint16_t optionLength;                                  /**< Option length. **/
+    char optionName[CHP_MAX_HF_NAME_LENGTH];                /**< Option name. **/
+    char optionData[CHP_MAX_HF_DATA_LENGTH];                /**< Option data values. **/
+} HttpHeaderOption_t;
+
+typedef enum
+{
+    CHP_GET = 1,   /**< GET */
+    CHP_POST,      /**< POST */
+    CHP_PUT,       /**< PUT */
+    CHP_DELETE,    /**< DELETE */
+    CHP_INVALID
+}HttpMethod_t;
+
+typedef struct HttpRequest_t
+{
+    unsigned short httpMajor;
+    unsigned short httpMinor;
+    HttpMethod_t method;
+    u_arraylist_t *headerOptions;
+    char resourceUri[CHP_MAX_HF_DATA_LENGTH];
+    void *payload;
+    size_t payloadLength;
+    bool payloadCached;
+    char payloadFormat[CHP_MAX_HF_DATA_LENGTH];
+    char acceptFormat[CHP_MAX_HF_DATA_LENGTH];
+}HttpRequest_t;
+
+typedef struct HttpResponse_t
+{
+    unsigned short httpMajor;
+    unsigned short httpMinor;
+    HttpResponseResult_t status;
+    u_arraylist_t *headerOptions;
+    char dataFormat[CHP_MAX_HF_DATA_LENGTH];
+    void *payload;
+    size_t payloadLength;
+}HttpResponse_t;
+
+typedef void (*CHPResponseCallback)(const HttpResponse_t *response, void *context);
+
+/**
+ * Function to initialize Parser and HTTP stack.
+ */
+OCStackResult CHPParserInitialize();
+
+/**
+ * Function to terminate parser and HTTP stack.
+ */
+OCStackResult CHPParserTerminate();
+
+/**
+ * Function to initiate TCP session and post HTTP request. If the method returns
+ * success, payload might be cached by the parser (req->payloadCached) and caller shall not free the
+ * payload if the flag is set.
+ * @param[in]   req         Object containing HTTP request information.
+ * @param[in]   httpcb      Callback for http response.
+ * @param[in]   context     Any app specific context for request
+ */
+OCStackResult CHPPostHttpRequest(HttpRequest_t *req, CHPResponseCallback httpcb,
+                                 void *context);
+
+/**
+ * Macro to verify the validity of input argument.
+ *
+ * @param  arg  log level
+ * @param  log_tag  log tag
+ * @param  log_message  log message
+ * @param  ret  return value
+ */
+#define VERIFY_NON_NULL_RET(arg, log_tag, log_message, ret) \
+    if (NULL == (arg)) \
+    { \
+        OIC_LOG_V(ERROR, (log_tag), "Invalid input:%s", (log_message)); \
+        return (ret); \
+    } \
+
+/**
+ * Macro to verify the validity of input argument.
+ *
+ * @param  arg  log level
+ * @param  log_tag  log tag
+ * @param  log_message  log message
+ */
+#define VERIFY_NON_NULL(arg, log_tag, log_message) \
+    VERIFY_NON_NULL_RET((arg), (log_tag), (log_message), CA_STATUS_INVALID_PARAM)
+
+/**
+ * Macro to verify the validity of input argument.
+ *
+ * @param  arg  log level
+ * @param  log_tag  log tag
+ * @param  log_message  log message
+ */
+#define VERIFY_NON_NULL_VOID(arg, log_tag, log_message) \
+    if (NULL == (arg)) { \
+        OIC_LOG_V(ERROR, (log_tag), "Invalid input:%s", (log_message)); \
+        return; \
+    } \
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/service/coap-http-proxy/samples/SConscript b/service/coap-http-proxy/samples/SConscript
new file mode 100644 (file)
index 0000000..fe165a2
--- /dev/null
@@ -0,0 +1,54 @@
+#******************************************************************
+#
+# Copyright 2016 Samsung Electronics All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+##
+# CoAP-HTTP-Proxy Sample Apps build script
+##
+
+Import('env')
+
+lib_env = env.Clone()
+SConscript('#service/third_party_libs.scons', 'lib_env')
+
+proxy_sample_app_env = lib_env.Clone()
+
+target_os = env.get('TARGET_OS')
+######################################################################
+# Build flags
+######################################################################
+proxy_sample_app_env.AppendUnique(CPPPATH = ['../include'])
+
+if target_os not in ['windows']:
+    proxy_sample_app_env.AppendUnique(CXXFLAGS = ['-O2', '-g', '-Wall', '-Wextra', '-std=c++0x'])
+proxy_sample_app_env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
+proxy_sample_app_env.AppendUnique(RPATH = [env.get('BUILD_DIR')])
+proxy_sample_app_env.PrependUnique(LIBS = ['coap_http_proxy', 'oc', 'octbstack', 'curl', 'connectivity_abstraction'])
+
+if env.get('SECURED') == '1':
+    proxy_sample_app_env.AppendUnique(LIBS = ['tinydtls'])
+
+####################################################################
+# Source files and Targets
+######################################################################
+proxy_server = proxy_sample_app_env.Program('proxy_main', 'proxy_main.c')
+
+Alias("coap_http_proxy", [proxy_server])
+
+env.AppendTarget('coap_http_proxy')
diff --git a/service/coap-http-proxy/samples/proxy_main.c b/service/coap-http-proxy/samples/proxy_main.c
new file mode 100644 (file)
index 0000000..9f2479d
--- /dev/null
@@ -0,0 +1,95 @@
+//******************************************************************
+//
+// Copyright 2016 Samsung Electronics All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#include "CoapHttpHandler.h"
+
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+int g_quitFlag = 0;
+
+void handleSigInt(int signum);
+
+/*
+* This method is an entry point of CoAP-HTTP Proxy.
+*/
+
+int main()
+{
+    printf("CoAP-HTTP proxy is starting..\n");
+    OCStackResult result = OCInit(NULL, 0, OC_SERVER);
+    if (result != OC_STACK_OK)
+    {
+        printf("Failed starting proxy\n");
+        return 0;
+    }
+
+    if (CHPInitialize() != OC_STACK_OK)
+    {
+        printf("Failed to start proxy.\n");
+        OCStop();
+        return 0;
+    }
+
+    printf("Proxy started successfully.\n");
+
+    signal(SIGINT, handleSigInt);
+    while (!g_quitFlag)
+    {
+        if (OCProcess() != OC_STACK_OK)
+        {
+            CHPTerminate();
+            OCStop();
+            printf("OCStack process error\n");
+            return 0;
+        }
+    }
+
+    if (CHPTerminate() != OC_STACK_OK)
+    {
+        printf("CHPTerminate failed.\n");
+    }
+    else
+    {
+        printf("CHPTerminate success.\n");
+    }
+
+    OCStop();
+    printf("Exiting proxy main loop...\n");
+    return 0;
+
+}
+
+/*
+* This is a signal handling function for SIGINT(CTRL+C).
+* A Resource Directory handle the SIGINT signal for safe exit.
+*
+* @param[in] signal
+*                 signal number of caught signal.
+*/
+void handleSigInt(int signum)
+{
+    if (signum == SIGINT)
+    {
+        g_quitFlag = 1;
+    }
+}
+
diff --git a/service/coap-http-proxy/src/CoapHttpHandler.c b/service/coap-http-proxy/src/CoapHttpHandler.c
new file mode 100644 (file)
index 0000000..f0ee86d
--- /dev/null
@@ -0,0 +1,502 @@
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include "CoapHttpHandler.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "logger.h"
+#include "pdu.h"
+#include "ocpayload.h"
+#include "uarraylist.h"
+#include "CoapHttpParser.h"
+#include "CoapHttpMap.h"
+#include "cJSON.h"
+
+#define TAG "CHPHandler"
+
+#define CHP_RESOURCE_TYPE_NAME "core.chp"
+#define CHP_RESOURCE_INTF_NAME "oc.mi.def"
+
+/**
+ * CoAP request tracking.
+ * This structure is used to hold CoAP request information
+ */
+typedef struct
+{
+    OCMethod method;
+    OCRequestHandle requestHandle;
+} CHPRequest_t;
+
+/**
+ * Pointer to handle of the newly created proxy resource.
+ */
+static OCResourceHandle g_proxyHandle = NULL;
+static int g_isCHProxyInitialized = false;
+
+/**
+ * Function to hand over CoAP request handling to Proxy.
+ */
+OCStackResult CHPHandleOCFRequest(const OCEntityHandlerRequest* requestInfo,
+                                   const char* proxyUri);
+
+/**
+ * Entity handler to receive requests from csdk.
+ */
+OCEntityHandlerResult CHPEntityHandler(OCEntityHandlerFlag flag,
+                                       OCEntityHandlerRequest* entityHandlerRequest,
+                                       void* callbackParam);
+bool CHPIsInitialized()
+{
+    return g_isCHProxyInitialized;
+}
+
+OCStackResult CHPInitialize()
+{
+    OIC_LOG(DEBUG, TAG, "CHPInitialize IN");
+    if (g_isCHProxyInitialized)
+    {
+        OIC_LOG(DEBUG, TAG, "CH Proxy already initialized");
+        return OC_STACK_OK;
+    }
+
+    OCStackResult result = CHPParserInitialize();
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG_V(ERROR, TAG, "Parser initialization failed[%d]", result);
+        return result;
+    }
+
+    result = OCSetProxyURI(OC_RSRVD_PROXY_URI);
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG_V(ERROR, TAG, "Setting proxy uri failed[%d]", result);
+        CHPParserTerminate();
+        return result;
+    }
+
+    result = OCCreateResource(&g_proxyHandle,
+                               CHP_RESOURCE_TYPE_NAME,
+                               CHP_RESOURCE_INTF_NAME,
+                               OC_RSRVD_PROXY_URI,
+                               CHPEntityHandler,
+                               NULL,
+                               OC_ACTIVE | OC_DISCOVERABLE | OC_SLOW);
+
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG_V(ERROR, TAG, "Create resource for proxy failed[%d]", result);
+        CHPParserTerminate();
+        return result;
+    }
+
+    g_isCHProxyInitialized = true;
+    OIC_LOG(DEBUG, TAG, "CHPInitialize OUT");
+    return OC_STACK_OK;
+}
+
+OCStackResult CHPTerminate()
+{
+    OIC_LOG(DEBUG, TAG, "CHPTerminate IN");
+    OCStackResult result = CHPParserTerminate();
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG_V(ERROR, TAG, "Parser termination failed[%d]", result);
+    }
+
+    result = OCDeleteResource(g_proxyHandle);
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG_V(ERROR, TAG, "Delete resource for proxy failed[%d]", result);
+    }
+
+    g_proxyHandle = NULL;
+    g_isCHProxyInitialized = false;
+    return result;
+    OIC_LOG(DEBUG, TAG, "CHPTerminate OUT");
+}
+
+static void CHPGetProxyURI(OCHeaderOption* options, uint8_t *numOptions, char* uri,
+                           size_t uriLength)
+{
+    OIC_LOG(DEBUG, TAG, "CHPGetProxyURI IN");
+    if(!uri || uriLength <= 0)
+    {
+        OIC_LOG (INFO, TAG, "Invalid uri buffer");
+        return;
+    }
+
+    if (!options || !numOptions || 0 == *numOptions)
+    {
+        OIC_LOG (INFO, TAG, "No options present");
+        return;
+    }
+
+    for (int count = 0; count < *numOptions; count++)
+    {
+        if (options[count].protocolID == OC_COAP_ID &&
+                options[count].optionID == COAP_OPTION_PROXY_URI)
+        {
+            OIC_LOG(DEBUG, TAG, "Proxy URI is present");
+            // Extract proxy-uri and delete it from headeroptions.
+            OICStrcpy(uri, uriLength, (char *)options[count].optionData);
+            for (int fwd = count; fwd < *numOptions-1; fwd++)
+            {
+                options[count] = options[count+1];
+            }
+            *numOptions -= 1;
+            return;
+        }
+    }
+
+    OIC_LOG(DEBUG, TAG, "CHPGetProxyURI OUT");
+    return;
+}
+
+// TODO: Will be moved to OCProxyPayload
+static OCRepPayload* CHPGetDiscoveryPayload()
+{
+    OCRepPayload* payload = OCRepPayloadCreate();
+    if(!payload)
+    {
+        OIC_LOG(ERROR, TAG, PCF("Failed to create Payload"));
+        return NULL;
+    }
+
+    OCRepPayloadSetUri(payload, OC_RSRVD_PROXY_URI);
+    OCRepPayloadSetPropString(payload, OC_RSRVD_DEVICE_ID, OCGetServerInstanceIDString());
+    return payload;
+}
+
+OCEntityHandlerResult CHPEntityHandler(OCEntityHandlerFlag flag,
+                                       OCEntityHandlerRequest* entityHandlerRequest,
+                                       void* callbackParam)
+{
+    OIC_LOG_V(INFO, TAG, "Proxy request received");
+    UNUSED(callbackParam);
+
+    if(!g_isCHProxyInitialized)
+    {
+        OIC_LOG (ERROR, TAG, "Proxy not initialized");
+        return OC_EH_INTERNAL_SERVER_ERROR;
+    }
+
+    if (!entityHandlerRequest)
+    {
+        OIC_LOG (ERROR, TAG, "Invalid request pointer");
+        return OC_EH_ERROR;
+    }
+
+    if(flag & OC_OBSERVE_FLAG)
+    {
+        OIC_LOG_V (ERROR, TAG, "Proxy is not observable");
+        return OC_EH_BAD_REQ;
+    }
+    else if (flag & OC_REQUEST_FLAG)
+    {
+        /*
+         *  A proxy can handle two type of requests:
+         *  1. Discovery request in which case proxy-uri option will not be present.
+         *  2. Request for HTTP resource with proxy-uri option.
+         */
+        char proxyUri[MAX_HEADER_OPTION_DATA_LENGTH] = {'\0'};
+        CHPGetProxyURI(entityHandlerRequest->rcvdVendorSpecificHeaderOptions,
+                       &(entityHandlerRequest->numRcvdVendorSpecificHeaderOptions),
+                       proxyUri, sizeof(proxyUri));
+
+        if(proxyUri[0] != '\0')
+        {
+            // A request for HTTP resource. Response will be sent asynchronously
+            if(OC_STACK_OK == CHPHandleOCFRequest(entityHandlerRequest,
+                                                  proxyUri) )
+            {
+                return OC_EH_SLOW;
+            }
+        }
+        else
+        {
+            OCEntityHandlerResult ehResult = OC_EH_ERROR;
+            switch (entityHandlerRequest->method)
+            {
+                case OC_REST_GET:
+                case OC_REST_DISCOVER:
+                {
+                    // Generate discovery payload
+                    OIC_LOG (INFO, TAG, "Discovery request from client");
+                    ehResult = OC_EH_OK;
+                    OCEntityHandlerResponse response =
+                                { .requestHandle = entityHandlerRequest->requestHandle,
+                                  .resourceHandle = entityHandlerRequest->resource,
+                                  .ehResult = ehResult};
+
+                    response.payload = (OCPayload *)CHPGetDiscoveryPayload();
+                    // Indicate that response is NOT in a persistent buffer
+                    response.persistentBufferFlag = 0;
+
+                    // Send the response
+                    if (OCDoResponse(&response) != OC_STACK_OK)
+                    {
+                        OIC_LOG(ERROR, TAG, "Error sending response");
+                        ehResult = OC_EH_ERROR;
+                    }
+
+                    OCPayloadDestroy(response.payload);
+                    break;
+                }
+                default:
+                    // Other methods are not supported
+                    OIC_LOG (INFO, TAG, "Invalid method from client");
+                    ehResult = OC_EH_METHOD_NOT_ALLOWED;
+                    break;
+            }
+            return ehResult;
+        }
+    }
+
+    return OC_EH_ERROR;
+}
+
+void CHPHandleHttpResponse(const HttpResponse_t *httpResponse, void *context)
+{
+    OIC_LOG(DEBUG, TAG, "CHPHandleHttpResponse IN");
+    if (!httpResponse || !context)
+    {
+        OIC_LOG(ERROR, TAG, "Invalid arguements");
+        return;
+    }
+
+    CHPRequest_t *ctxt = (CHPRequest_t *)context;
+    OCEntityHandlerResponse response = { .requestHandle = ctxt->requestHandle,
+                                         .resourceHandle = g_proxyHandle};
+    response.persistentBufferFlag = 0;
+
+    OCStackResult result = CHPGetOCCode(httpResponse->status, ctxt->method,
+                                        &response.ehResult);
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s failed[%d]", __func__, result);
+        response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
+        if (OCDoResponse(&response) != OC_STACK_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Error sending response");
+        }
+        OICFree(ctxt);
+        return;
+    }
+
+    // ctxt not required now.
+    OICFree(ctxt);
+
+    OCPayloadFormat format = CHPGetOCContentType(httpResponse->dataFormat);
+    switch (format)
+    {
+        case OC_FORMAT_CBOR:
+            OIC_LOG(DEBUG, TAG, "Payload format is CBOR");
+            result = OCParsePayload(&response.payload, PAYLOAD_TYPE_REPRESENTATION,
+                                    httpResponse->payload, httpResponse->payloadLength);
+            if(result  != OC_STACK_OK)
+            {
+                OIC_LOG(ERROR, TAG, "Error parsing payload");
+                response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
+                if (OCDoResponse(&response) != OC_STACK_OK)
+                {
+                    OIC_LOG(ERROR, TAG, "Error sending response");
+                }
+                return;
+            }
+            break;
+        case OC_FORMAT_JSON:
+            OIC_LOG(DEBUG, TAG, "Payload format is JSON");
+            cJSON *payloadJson = cJSON_Parse((char *)httpResponse->payload);
+            OCRepPayload* payloadCbor = OCRepPayloadCreate();
+            if(!payloadCbor)
+            {
+                response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
+                if (OCDoResponse(&response) != OC_STACK_OK)
+                {
+                    OIC_LOG(ERROR, TAG, "Error sending response");
+                }
+                cJSON_Delete(payloadJson);
+                return;
+            }
+
+            CHPJsonToRepPayload(payloadJson, payloadCbor);
+            response.payload = (OCPayload *)payloadCbor;
+            cJSON_Delete(payloadJson);
+            break;
+        default:
+            OIC_LOG(ERROR, TAG, "Payload format is not supported");
+            response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
+            if (OCDoResponse(&response) != OC_STACK_OK)
+            {
+                OIC_LOG(ERROR, TAG, "Error sending response");
+            }
+            return;
+    }
+
+    // Header Options parsing
+    response.numSendVendorSpecificHeaderOptions = 0;
+    OCHeaderOption *optionsPointer = response.sendVendorSpecificHeaderOptions;
+
+    uint8_t tempOptionNumber = u_arraylist_length(httpResponse->headerOptions);
+    for (int numOptions = 0; numOptions < tempOptionNumber &&
+                             response.numSendVendorSpecificHeaderOptions < MAX_HEADER_OPTIONS;
+                             numOptions++)
+    {
+        HttpHeaderOption_t *httpOption = u_arraylist_get(httpResponse->headerOptions, numOptions);
+        result = CHPGetOCOption(httpOption, optionsPointer);
+        if (OC_STACK_OK != result)
+        {
+            OIC_LOG_V(ERROR, TAG, "CHPGetCoAPOption failed[%d][%d]", result,
+                                                response.numSendVendorSpecificHeaderOptions);
+            continue;
+        }
+
+        response.numSendVendorSpecificHeaderOptions++;
+        optionsPointer += 1;
+    }
+
+    if (OCDoResponse(&response) != OC_STACK_OK)
+    {
+        OIC_LOG(ERROR, TAG, "Error sending response");
+    }
+
+    //OICFree(coapResponseInfo.info.payload);
+    OIC_LOG(DEBUG, TAG, "CHPHandleHttpResponse OUT");
+}
+
+OCStackResult CHPHandleOCFRequest(const OCEntityHandlerRequest* requestInfo,
+                                   const char* proxyUri)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+
+    HttpRequest_t httpRequest = { .httpMajor = 1,
+                                  .httpMinor = 1};
+
+    OCEntityHandlerResponse response = { .requestHandle = requestInfo->requestHandle,
+                                         .resourceHandle = requestInfo->resource};
+    OCStackResult result = CHPGetHttpMethod(requestInfo->method, &httpRequest.method);
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG(ERROR, TAG, "Method not found in HTTP");
+        response.ehResult = OC_EH_BAD_REQ;
+        if (OCDoResponse(&response) != OC_STACK_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Error sending response");
+        }
+
+        return OC_STACK_ERROR;
+    }
+
+    uint8_t vendorOptions = requestInfo->numRcvdVendorSpecificHeaderOptions;
+    if (vendorOptions)
+    {
+        httpRequest.headerOptions = u_arraylist_create();
+        for (int option = 0; option < vendorOptions; option++)
+        {
+            HttpHeaderOption_t *httpOption = NULL;
+            result = CHPGetHttpOption(requestInfo->rcvdVendorSpecificHeaderOptions + option,
+                                      &httpOption);
+            if (OC_STACK_OK != result || NULL == httpOption )
+            {
+                OIC_LOG_V(ERROR, TAG, "CHPGetHttpOption failed [%d]", result);
+                continue;
+            }
+            u_arraylist_add(httpRequest.headerOptions, (void *)httpOption);
+        }
+    }
+
+    OICStrcpy(httpRequest.resourceUri, sizeof(httpRequest.resourceUri), proxyUri);
+
+    if (requestInfo->payload && requestInfo->payload->type == PAYLOAD_TYPE_REPRESENTATION)
+    {
+        // Conversion from cbor to json.
+        cJSON *payloadJson = CHPRepPayloadToJson((OCRepPayload *)requestInfo->payload);
+        if(!payloadJson)
+        {
+            response.ehResult = OC_EH_BAD_REQ;
+            if (OCDoResponse(&response) != OC_STACK_OK)
+            {
+                OIC_LOG(ERROR, TAG, "Error sending response");
+            }
+
+            return OC_STACK_ERROR;
+
+        }
+        httpRequest.payload = (void *)cJSON_Print(payloadJson);
+        httpRequest.payloadLength = strlen(httpRequest.payload);
+        OICStrcpy(httpRequest.payloadFormat, sizeof(httpRequest.payloadFormat),
+                  CBOR_CONTENT_TYPE);
+        cJSON_Delete(payloadJson);
+    }
+
+    OICStrcpy(httpRequest.acceptFormat, sizeof(httpRequest.acceptFormat),
+              ACCEPT_MEDIA_TYPE);
+    CHPRequest_t *chpRequest = (CHPRequest_t *)OICCalloc(1, sizeof(CHPRequest_t));
+    if (!chpRequest)
+    {
+        OIC_LOG(ERROR, TAG, "Calloc failed");
+        response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
+        if (OCDoResponse(&response) != OC_STACK_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Error sending response");
+        }
+
+        OICFree(httpRequest.payload);
+        u_arraylist_destroy(httpRequest.headerOptions);
+        return OC_STACK_NO_MEMORY;
+    }
+
+    chpRequest->requestHandle = requestInfo->requestHandle;
+    chpRequest->method = requestInfo->method;
+
+    result = CHPPostHttpRequest(&httpRequest, CHPHandleHttpResponse,
+                                (void *)chpRequest);
+    if (OC_STACK_OK != result)
+    {
+        OIC_LOG_V(ERROR, TAG, "CHPPostHttpRequest failed[%d]", result);
+        switch (result)
+        {
+            case OC_STACK_INVALID_URI:
+                response.ehResult = OC_EH_BAD_REQ;
+                break;
+            default:
+                response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
+        }
+
+        if (OCDoResponse(&response) != OC_STACK_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Error sending response");
+        }
+
+        OICFree(httpRequest.payload);
+        OICFree(chpRequest);
+        u_arraylist_destroy(httpRequest.headerOptions);
+        return OC_STACK_ERROR;
+    }
+
+    if(!httpRequest.payloadCached)
+    {
+        // Free only if parser has not cached it.
+        OICFree(httpRequest.payload);
+    }
+    u_arraylist_destroy(httpRequest.headerOptions);
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
diff --git a/service/coap-http-proxy/src/CoapHttpMap.c b/service/coap-http-proxy/src/CoapHttpMap.c
new file mode 100644 (file)
index 0000000..82252a7
--- /dev/null
@@ -0,0 +1,520 @@
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include "CoapHttpMap.h"
+#include <string.h>
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "logger.h"
+#include "ocstack.h"
+#include "pdu.h"
+#include "ocpayload.h"
+
+#define TAG "CHPMap"
+
+int CHPGetOptionID(const char *httpOptionName)
+{
+    if (!httpOptionName)
+    {
+        OIC_LOG(ERROR, TAG, "HTTP option name is NULL");
+        return 0;
+    }
+
+    OICStringToLower((char *)httpOptionName);
+    if (0 == strcmp(httpOptionName, HTTP_OPTION_CACHE_CONTROL) ||
+        0 == strcmp(httpOptionName, HTTP_OPTION_EXPIRES))
+    {
+        return COAP_OPTION_MAXAGE;
+    }
+    else if (0 == strcmp(httpOptionName, HTTP_OPTION_IF_MATCH))
+    {
+        return COAP_OPTION_IF_MATCH;
+    }
+    else if (0 == strcmp(httpOptionName, HTTP_OPTION_IF_NONE_MATCH))
+    {
+        return COAP_OPTION_IF_NONE_MATCH;
+    }
+    else if (0 == strcmp(httpOptionName, HTTP_OPTION_ETAG))
+    {
+        return COAP_OPTION_ETAG;
+    }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "No Mapping found for %s", httpOptionName);
+    }
+
+    return 0;
+}
+
+OCStackResult CHPGetOCCode(const HttpResponseResult_t httpCode, const OCMethod method,
+                            OCEntityHandlerResult *ocfCode)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    OIC_LOG_V(DEBUG, TAG, "Http Code is %d", httpCode);
+
+    switch (httpCode)
+    {
+        case CHP_SUCCESS:
+            if (OC_REST_GET == method)
+            {
+                *ocfCode = OC_EH_CONTENT;
+            }
+            else if (OC_REST_DELETE == method)
+            {
+                *ocfCode = OC_EH_RESOURCE_DELETED;
+            }
+            else
+            {
+                *ocfCode = OC_EH_CHANGED;
+            }
+            break;
+        case CHP_NO_CONTENT:
+            if (OC_REST_DELETE == method)
+            {
+                *ocfCode = OC_EH_RESOURCE_DELETED;
+            }
+            else
+            {
+                *ocfCode = OC_EH_CHANGED;
+            }
+            break;
+        case CHP_CREATED:
+            *ocfCode = OC_EH_RESOURCE_CREATED;
+            break;
+        case CHP_NOT_MODIFIED:
+            *ocfCode = OC_EH_VALID;
+            break;
+        case CHP_BAD_REQ:
+        case CHP_REQUEST_URI_TOO_LARGE:
+            *ocfCode = OC_EH_BAD_REQ;
+            break;
+        case CHP_BAD_GATEWAY:
+        case CHP_VERSION_NOT_SUPPORTED:
+            *ocfCode = OC_EH_BAD_GATEWAY;
+            break;
+        case CHP_UNAUTHORIZED_REQ:
+        case CHP_FORBIDDEN_REQ:
+        case CHP_NOT_FOUND:
+        case CHP_NOT_ACCEPTABLE:
+        case CHP_REQUEST_ENTITY_TOO_LARGE:
+        case CHP_UNSUPPORTED_MEDIA_TYPE:
+        case CHP_INTERNAL_SERVER_ERROR:
+        case CHP_NOT_IMPLEMENTED:
+        case CHP_SERVICE_UNAVAILABLE:
+        case CHP_GATEWAY_TIMEOUT:
+            *ocfCode = httpCode;
+            break;
+        default:
+            OIC_LOG_V(ERROR, TAG, "HTTP Response code[%d] is not matching the OCF Response code",
+                      httpCode);
+            return OC_STACK_ERROR;
+    }
+
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
+OCStackResult CHPGetOCOption(const HttpHeaderOption_t *httpOption, OCHeaderOption *ocfOption)
+{
+    OIC_LOG(DEBUG, TAG, "CHPGetCoAPOption IN");
+    if (!httpOption)
+    {
+        OIC_LOG(ERROR, TAG, "HTTP option is Null");
+        return OC_STACK_INVALID_PARAM;
+    }
+
+    ocfOption->optionID = CHPGetOptionID(httpOption->optionName);
+    if (!ocfOption->optionID)
+    {
+        OIC_LOG(INFO, TAG, "No match for HTTP option found");
+        return OC_STACK_INVALID_OPTION;
+    }
+
+    ocfOption->protocolID = OC_COAP_ID;
+    ocfOption->optionLength = httpOption->optionLength < sizeof(ocfOption->optionData) ?
+                                httpOption->optionLength : sizeof(ocfOption->optionData);
+    memcpy(ocfOption->optionData,  httpOption->optionData, ocfOption->optionLength);
+
+    OIC_LOG(DEBUG, TAG, "CHPGetCoAPOption OUT");
+    return OC_STACK_OK;
+}
+
+OCPayloadFormat CHPGetOCContentType(const char *httpContentType)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+
+    OICStringToLower((char *)httpContentType);
+    if (strstr(httpContentType, CBOR_CONTENT_TYPE))
+    {
+        return OC_FORMAT_CBOR;
+    }
+    else if (strstr(httpContentType, JSON_CONTENT_TYPE))
+    {
+        return OC_FORMAT_JSON;
+    }
+
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_FORMAT_UNSUPPORTED;
+}
+
+OCStackResult CHPGetHttpMethod(const OCMethod method, HttpMethod_t *httpMethod)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+
+    switch (method)
+    {
+        case OC_REST_GET:
+            *httpMethod = CHP_GET;
+            break;
+        case OC_REST_PUT:
+            *httpMethod = CHP_PUT;
+            break;
+        case OC_REST_POST:
+            *httpMethod = CHP_POST;
+            break;
+        case OC_REST_DELETE:
+            *httpMethod = CHP_DELETE;
+            break;
+        default:
+            *httpMethod = CHP_INVALID;
+            OIC_LOG_V(ERROR, TAG, "Unknown method type %d", method);
+            return OC_STACK_INVALID_METHOD;
+    }
+
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
+OCStackResult CHPGetHttpOption(const OCHeaderOption* option, HttpHeaderOption_t** httpOption)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    if (!option)
+    {
+        OIC_LOG(ERROR, TAG, "option is NULL");
+        return OC_STACK_INVALID_PARAM;
+    }
+
+    *httpOption = (HttpHeaderOption_t *)OICCalloc(1, sizeof(HttpHeaderOption_t));
+    if (NULL == *httpOption)
+    {
+        OIC_LOG(ERROR, TAG, "Memory allocation failed");
+        return OC_STACK_NO_MEMORY;
+    }
+
+    switch (option->optionID)
+    {
+        case COAP_OPTION_ACCEPT:
+            OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+                      HTTP_OPTION_ACCEPT);
+            break;
+        case COAP_OPTION_IF_MATCH:
+            OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+                      HTTP_OPTION_IF_MATCH);
+            break;
+        case COAP_OPTION_IF_NONE_MATCH:
+            OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+                      HTTP_OPTION_IF_NONE_MATCH);
+            break;
+        case COAP_OPTION_ETAG:
+            OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+                      HTTP_OPTION_ETAG);
+            break;
+        case COAP_OPTION_CONTENT_TYPE:
+            OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+                      HTTP_OPTION_CONTENT_TYPE);
+            break;
+        default:
+            OIC_LOG_V(INFO, TAG, "No Matching found for the ID %d", option->optionID);
+    }
+
+    if ('\0' == (*httpOption)->optionName[0])
+    {
+        OIC_LOG(ERROR, TAG, "No matching is found");
+        OICFree(*httpOption);
+        return OC_STACK_INVALID_OPTION;
+    }
+
+    (*httpOption)->optionLength = option->optionLength < sizeof((*httpOption)->optionData) ?
+                                      option->optionLength : sizeof((*httpOption)->optionData);
+    memcpy((*httpOption)->optionData, option->optionData, (*httpOption)->optionLength);
+
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
+void CHPJsonToRepPayload(cJSON* rootJSon, OCRepPayload* payload)
+{
+    cJSON* dataJson = rootJSon->child;
+    while (dataJson)
+    {
+        switch (dataJson->type)
+        {
+            case cJSON_String:
+                OCRepPayloadSetPropString(payload, dataJson->string, dataJson->valuestring);
+                break;
+            case cJSON_Number:
+                if (dataJson->valueint == dataJson->valuedouble)
+                {
+                    OCRepPayloadSetPropInt(payload, dataJson->string, dataJson->valueint);
+                }
+                else
+                {
+                    OCRepPayloadSetPropDouble(payload, dataJson->string, dataJson->valuedouble);
+                }
+                break;
+            case cJSON_False:
+                OCRepPayloadSetPropBool(payload, dataJson->string, false);
+                break;
+            case cJSON_True:
+                OCRepPayloadSetPropBool(payload, dataJson->string, true);
+                break;
+            case cJSON_Object:
+            {
+                OCRepPayload* childPayload = OCRepPayloadCreate();
+                CHPJsonToRepPayload(dataJson,childPayload);
+                OCRepPayloadSetPropObject(payload, dataJson->string,childPayload );
+                break;
+            }
+            case cJSON_Array:
+            {
+                int size = cJSON_GetArraySize(dataJson);
+                size_t dimensions[MAX_REP_ARRAY_DEPTH];
+                dimensions[0] = size;
+                dimensions[1] = dimensions[2] = 0;
+
+                int i = 0;
+                int type = cJSON_IsReference;
+                int numType = 0;    // int:1, double:2
+                const int intType = 1;
+                const int doubleType = 2;
+
+                int64_t intArray[size];
+                double doubleArray[size];
+                char* strArray[size];
+                OCRepPayload* objPayloadArray[size];
+
+                for (; i < size ; ++i)
+                {
+                    cJSON* subitem = cJSON_GetArrayItem(dataJson, i);
+                    if (subitem == NULL)
+                    {
+                        continue;
+                    }
+
+                    if ((type != cJSON_IsReference) && (type != subitem->type))
+                    {
+                        continue;
+                    }
+                    else
+                    {
+                        type = subitem->type;
+                        switch (type)
+                        {
+                            case cJSON_Number:
+                                if (subitem->valueint == subitem->valuedouble)
+                                {
+                                    numType = intType;
+                                    intArray[i] = (int64_t) subitem->valueint;
+                                }
+                                else
+                                {
+                                    numType = doubleType;
+                                    doubleArray[i] = subitem->valuedouble;
+                                }
+                                break;
+                            case cJSON_String:
+                                strArray[i] = subitem->valuestring;
+                                break;
+                            case cJSON_Object:
+                                objPayloadArray[i] = OCRepPayloadCreate();
+                                CHPJsonToRepPayload(subitem,objPayloadArray[i]);
+                                break;
+                            default:
+                                OIC_LOG(ERROR, TAG, "wrong ArrayType in JsonToRepPayload()");
+                                break;
+                        }
+                    }
+                }
+
+                switch (type)
+                {
+                    case cJSON_Number:
+                    if (numType == intType)
+                    {
+                        OCRepPayloadSetIntArray(payload, dataJson->string,(const int64_t*)intArray,
+                                                dimensions);
+                    }
+                    else if (numType == doubleType)
+                    {
+                        OCRepPayloadSetDoubleArray(payload, dataJson->string,
+                                                   (const double*)doubleArray,
+                                                   dimensions);
+                    }
+                    break;
+                    case cJSON_String:
+                        OCRepPayloadSetStringArray(payload, dataJson->string,
+                                                   (const char**)strArray,
+                                                   dimensions);
+                    break;
+                    case cJSON_Object:
+                        OCRepPayloadSetPropObjectArray(payload, dataJson->string,
+                                                       (const OCRepPayload**)objPayloadArray,
+                                                       dimensions);
+                        break;
+                  default:
+                        OIC_LOG(ERROR, TAG, "wrong ArrayType in JsonToRepPayload()");
+                        break;
+                }
+                break;
+            }
+        }
+        dataJson = dataJson->next;
+    }
+}
+
+cJSON* CHPRepPayloadToJson(OCRepPayload* repData)
+{
+    cJSON *outJson = cJSON_CreateObject();
+    if (outJson == NULL)
+    {
+        return NULL;
+    }
+
+    OCRepPayloadValue* val = repData->values;
+    while (val)
+    {
+        switch (val->type)
+        {
+            case OCREP_PROP_NULL:
+                break;
+            case OCREP_PROP_INT:
+                OIC_LOG_V(DEBUG, TAG, "%s(int):%d", val->name, (int)val->i);
+                cJSON_AddNumberToObject(outJson,val->name,(int)val->i);
+                break;
+            case OCREP_PROP_DOUBLE:
+                OIC_LOG_V(DEBUG, TAG, "%s(double):%f", val->name, val->d);
+                cJSON_AddNumberToObject(outJson,val->name,val->d);
+                break;
+            case OCREP_PROP_BOOL:
+                OIC_LOG_V(DEBUG, TAG, "%s(bool):%s", val->name, val->b ? "true" : "false");
+                cJSON_AddBoolToObject(outJson,val->name,val->b);
+                break;
+            case OCREP_PROP_STRING:
+                OIC_LOG_V(DEBUG, TAG, "%s(string):%s", val->name, val->str);
+                cJSON_AddStringToObject(outJson,val->name,val->str);
+                break;
+            case OCREP_PROP_OBJECT:
+            {
+                cJSON *objJson = CHPRepPayloadToJson(val->obj);
+                if (objJson != NULL)
+                {
+                    cJSON_AddItemToObject(outJson,val->name,objJson);
+                }
+                break;
+            }
+            case OCREP_PROP_ARRAY:
+            {
+                unsigned int i = 0;
+                int arraySize = (int)val->arr.dimensions[0];
+                switch (val->arr.type)
+                {
+                    case OCREP_PROP_INT:
+                        OIC_LOG_V(DEBUG, TAG, "%s(int array)", val->name);
+                        if (arraySize > 0)
+                        {
+                            int castVal[val->arr.dimensions[0]];
+                            for (i = 0 ; i < (unsigned int)arraySize ; i++)
+                            {
+                                castVal[i] = (int)val->arr.iArray[i];
+                            }
+                            cJSON *array = cJSON_CreateIntArray(castVal,arraySize);
+                            if (array != NULL)
+                            {
+                                cJSON_AddItemToObject(outJson,val->name,array);
+                            }
+                        }
+                        break;
+                    case OCREP_PROP_DOUBLE:
+                        OIC_LOG_V(DEBUG, TAG, "%s(double array)", val->name);
+                        if (arraySize > 0)
+                        {
+                            cJSON *array = cJSON_CreateDoubleArray(val->arr.dArray,arraySize);
+                            if (array != NULL)
+                            {
+                                cJSON_AddItemToObject(outJson,val->name,array);
+                            }
+                        }
+                        break;
+                    case OCREP_PROP_STRING:
+                        OIC_LOG_V(DEBUG, TAG, "%s(string array)", val->name);
+                        if (arraySize > 0)
+                        {
+                            cJSON *array = cJSON_CreateStringArray((const char**)val->arr.strArray,
+                                                                   arraySize);
+                            if (array != NULL)
+                            {
+                                cJSON_AddItemToObject(outJson,val->name,array);
+                            }
+                        }
+                        break;
+                    case OCREP_PROP_OBJECT:
+                        if (arraySize > 0)
+                        {
+                            cJSON *arrayJson = cJSON_CreateArray();
+                            for (i = 0 ; i < (unsigned int)arraySize ; i++)
+                            {
+                                cJSON *objJson = CHPRepPayloadToJson(val->arr.objArray[i]);
+                                if (objJson != NULL && arrayJson != NULL)
+                                {
+                                    cJSON_AddItemToArray(arrayJson, objJson);
+                                }
+                            }
+                            if (arrayJson != NULL)
+                            {
+                                cJSON_AddItemToObject(outJson,val->name,arrayJson);
+                            }
+                        }
+                        break;
+                    case OCREP_PROP_BOOL:
+                        //TODO : Not support - cJSON_CreateBoolArray
+                        break;
+                    default:
+                        OIC_LOG_V(ERROR, TAG, "Unknown/unsupported array type: %s", val->name);
+                        break;
+                }
+                break;
+            }
+            default:
+                OIC_LOG_V(ERROR, TAG, "Unknown type: %s", val->name);
+                break;
+        }
+        val = val->next;
+    }
+
+    if( repData->values != NULL)
+    {
+        return outJson;
+    }
+    else
+    {
+        cJSON_Delete(outJson);
+        return NULL;
+    }
+}
diff --git a/service/coap-http-proxy/src/CoapHttpParser.c b/service/coap-http-proxy/src/CoapHttpParser.c
new file mode 100644 (file)
index 0000000..c24edff
--- /dev/null
@@ -0,0 +1,869 @@
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "CoapHttpParser.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "uarraylist.h"
+#include "logger.h"
+
+#include <string.h>
+#include <curl/curl.h>
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif //!defined(_MSC_VER)
+#include <sys/types.h>
+#include <fcntl.h>
+#if !defined(_WIN32)
+#include <sys/select.h>
+#endif //!defined(_WIN32)
+#include <errno.h>
+
+#define TAG "CHP_PARSER"
+
+#define DEFAULT_USER_AGENT "IoTivity"
+#define MAX_PAYLOAD_SIZE (1048576U) // 1 MB
+
+typedef struct
+{
+    void* context;
+    CHPResponseCallback cb;
+    HttpResponse_t resp;
+    /* libcurl will not cache request payload when creating a easy handle hence we need to cache */
+    void* payload;
+    size_t payloadLength;
+    /* To track multiple read_callbacks from curl */
+    size_t readOffset;
+    /* To track multiple write_callbacks from curl */
+    size_t writeOffset;
+    /* libcurl related */
+    CURL* easyHandle;
+    /* libcurl does not copy header options passed to a request */
+    struct curl_slist *list;
+} CHPContext_t;
+
+/* A curl mutihandle is not threadsafe so we require mutexes to add new easy
+ * handles to multihandle.
+ */
+static CURLM *g_multiHandle;
+static int g_activeConnections;
+
+/*  Mutex code is taken from CA.
+ *  General utility functions shall be placed in common location
+ *  so that all modules can use them.
+ */
+static pthread_mutex_t g_multiHandleMutex;
+
+/* Fds used to signal threads to stop */
+static int g_shutdownFds[2];
+
+static bool g_terminateParser;
+
+/*
+ * Fds used to signal fdset to be refreshed.
+ * When a new easy_handle is added to multi_handle,
+ * existing fd_set has to be updated.
+ */
+static int g_refreshFds[2];
+
+/*
+ * Thread handle for curl multi_handle processing.
+ */
+static pthread_t g_multiHandleThread;
+
+static void CHPParserLockMutex();
+static void CHPParserUnlockMutex();
+
+static void CHPParserResetHeaderOptions(u_arraylist_t** headerOptions)
+{
+    VERIFY_NON_NULL_VOID(headerOptions, TAG, "headerOptions is NULL");
+
+    HttpHeaderOption_t *option = NULL;
+    while (NULL != (option = u_arraylist_remove(*headerOptions, 0)))
+    {
+        OICFree(option);
+    }
+    u_arraylist_free(headerOptions);
+}
+
+static void CHPFreeContext(CHPContext_t *ctxt)
+{
+    VERIFY_NON_NULL_VOID(ctxt, TAG, "ctxt is NULL");
+    if(ctxt->list)
+    {
+        curl_slist_free_all(ctxt->list);
+    }
+
+    if(ctxt->easyHandle)
+    {
+        curl_easy_cleanup(ctxt->easyHandle);
+    }
+
+    CHPParserResetHeaderOptions(&(ctxt->resp.headerOptions));
+    OICFree(ctxt->resp.payload);
+    OICFree(ctxt->payload);
+    OICFree(ctxt);
+}
+
+static void *CHPParserExecuteMultiHandle(void* data)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    UNUSED(data);
+    /*
+     * These fd sets will be fetched from curl multi handle and monitored to execute
+     * curl_multi_perform()
+     */
+    fd_set fdread;
+    fd_set fdwrite;
+    fd_set fdexcep;
+    int maxfd;
+    struct timeval timeout;
+    struct timeval *tv;
+    int activeCon;
+    int activeEasyHandle;
+    bool goForSelect;
+    int retValue;
+
+    /* When active connections exist, curl_multi_perform() shall be called within
+     * curlMultiTimeout seconds regardless of whether select returned successfull or not.
+     */
+    long curlMultiTimeout;
+    while (!g_terminateParser)
+    {
+        // Required everytime before calling curl_multi_fdset()
+        FD_ZERO(&fdread);
+        FD_ZERO(&fdwrite);
+        FD_ZERO(&fdexcep);
+        maxfd = -1;
+        goForSelect = true;
+
+        // Get currently active transfer fds from curl
+        CHPParserLockMutex();
+        curl_multi_fdset(g_multiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
+        curl_multi_timeout(g_multiHandle, &curlMultiTimeout);
+        activeCon = g_activeConnections;
+        CHPParserUnlockMutex();
+
+        // A 0 represent curl_multi_perform() shall be called.
+        if(curlMultiTimeout < 0)
+        {
+            curlMultiTimeout = 1000;
+        }
+
+        if(maxfd == -1)
+        {
+            /* Nothing to monitor from libcurl.
+             * This mean that no active sockets exist either because
+             * there are no transfer taking place or sockets are not in a
+             * state that could be monitored (connecting, retry etc.)
+             */
+            if(!activeCon)
+            {
+                // wait until something received on shutdown or refresh fd
+                // with no timeout.
+                curlMultiTimeout = -1;
+            }
+            else
+            {
+                // libcurl recommend doing this.
+                usleep(100000);
+                // dont select() and directly call curl_multi_perform()
+                goForSelect = false;
+            }
+        }
+
+        if(goForSelect)
+        {
+            FD_SET(g_shutdownFds[0], &fdread);
+            if (maxfd < g_shutdownFds[0])
+            {
+                maxfd = g_shutdownFds[0];
+            }
+
+            FD_SET(g_refreshFds[0], &fdread);
+            if (maxfd < g_refreshFds[0])
+            {
+                maxfd = g_refreshFds[0];
+            }
+
+            if(curlMultiTimeout == -1)
+            {
+                tv = NULL;
+            }
+            else
+            {
+                timeout.tv_sec = curlMultiTimeout / 1000;
+                timeout.tv_usec = 0;
+                tv = &timeout;
+            }
+
+            // Select here
+            retValue = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, tv);
+            if(retValue == -1)
+            {
+                OIC_LOG_V(ERROR, TAG, "Error in select. %s", strerror(errno));
+                continue;
+            }
+
+            // Some sockets are available for operations, check if shutdown or refresh fds are
+            // among them. In any case, go ahead and call curl_multi_perform()
+            if(retValue)
+            {
+                if (FD_ISSET(g_shutdownFds[0], &fdread))
+                {
+                    OIC_LOG(ERROR, TAG, "Shutdown requested. multi_handle returning");
+                    break;
+                }
+                else if(FD_ISSET(g_refreshFds[0], &fdread))
+                {
+                    char buf[20] = {0};
+                    ssize_t len = read(g_refreshFds[0], buf, sizeof(buf));
+                    UNUSED(len);
+                    // new easy handles added, call multi_perform and refresh fds.
+                    OIC_LOG(ERROR, TAG, "New easy handle added");
+                }
+            }
+        }
+
+        CURLMcode ret;
+        CHPParserLockMutex();
+        do
+        {
+            ret = curl_multi_perform(g_multiHandle, &activeEasyHandle);
+            struct CURLMsg *cmsg;
+            int cmsgq;
+            do
+            {
+                cmsgq = 0;
+                cmsg = curl_multi_info_read(g_multiHandle, &cmsgq);
+                if(cmsg && (cmsg->msg == CURLMSG_DONE))
+                {
+                    CURL *easyHandle = cmsg->easy_handle;
+                    g_activeConnections--;
+                    curl_multi_remove_handle(g_multiHandle, easyHandle);
+
+                    CHPContext_t *ptr;
+                    char *uri = NULL;
+                    char *contentType = NULL;
+                    long responseCode;
+
+                    curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &ptr);
+                    curl_easy_getinfo(easyHandle, CURLINFO_EFFECTIVE_URL, &uri);
+                    curl_easy_getinfo(easyHandle, CURLINFO_RESPONSE_CODE, &responseCode);
+                    curl_easy_getinfo(easyHandle, CURLINFO_CONTENT_TYPE, &contentType);
+
+                    ptr->resp.status = responseCode;
+                    OICStrcpy(ptr->resp.dataFormat, sizeof(ptr->resp.dataFormat), contentType);
+                    OIC_LOG_V(DEBUG, TAG, "Transfer completed %d uri: %s, %s", g_activeConnections,
+                                                                           uri, contentType);
+                    ptr->cb(&(ptr->resp), ptr->context);
+                    CHPFreeContext(ptr);
+                }
+            } while(cmsg && !g_terminateParser);
+        }while (ret == CURLM_CALL_MULTI_PERFORM && !g_terminateParser);
+        CHPParserUnlockMutex();
+    }
+
+    if (g_terminateParser)
+    {
+        OIC_LOG_V(DEBUG, TAG, "Shutdown request received.");
+        // g_shutdownFds[1] will be already closed.
+        close(g_shutdownFds[0]);
+        close(g_refreshFds[0]);
+        close(g_refreshFds[1]);
+        g_shutdownFds[0] = -1;
+        g_shutdownFds[1] = -1;
+        g_refreshFds[0] = -1;
+        g_refreshFds[1] = -1;
+    }
+
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    return NULL;
+}
+
+OCStackResult CHPParserInitializePipe(int fds[2])
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    int ret = -1;
+    fds[0] = -1;
+    fds[1] = -1;
+#if defined(HAVE_PIPE2)
+    ret = pipe2(fds, O_CLOEXEC);
+#else
+    ret = pipe(fds);
+    if (-1 != ret)
+    {
+        ret = fcntl(fds[0], F_GETFD);
+        if (-1 != ret)
+        {
+            ret = fcntl(fds[0], F_SETFD, ret|FD_CLOEXEC);
+        }
+        if (-1 != ret)
+        {
+            ret = fcntl(fds[1], F_GETFD);
+        }
+        if (-1 != ret)
+        {
+            ret = fcntl(fds[1], F_SETFD, ret|FD_CLOEXEC);
+        }
+        if (-1 == ret)
+        {
+            close(fds[1]);
+            close(fds[0]);
+            fds[0] = -1;
+            fds[1] = -1;
+        }
+    }
+#endif
+    if (-1 == ret)
+    {
+        OIC_LOG_V(ERROR, TAG, "FD initialization failed: %s", strerror(errno));
+        return OC_STACK_ERROR;
+    }
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
+static OCStackResult CHPParserInitializeMutex()
+{
+    // create the mutex with the attributes set
+    int ret = pthread_mutex_init(&g_multiHandleMutex, PTHREAD_MUTEX_DEFAULT);
+    if (0 != ret)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s Failed to initialize mutex !", __func__);
+        return OC_STACK_ERROR;
+    }
+    return OC_STACK_OK;
+}
+
+static OCStackResult CHPParserTerminateMutex()
+{
+    int ret = pthread_mutex_destroy(&g_multiHandleMutex);
+    if (0 != ret)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s Failed to free mutex !", __func__);
+        return OC_STACK_ERROR;
+    }
+    return OC_STACK_OK;
+}
+
+static void CHPParserLockMutex()
+{
+    int ret = pthread_mutex_lock(&g_multiHandleMutex);
+    if(ret != 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Pthread Mutex lock failed: %d", ret);
+    }
+}
+
+static void CHPParserUnlockMutex()
+{
+    int ret = pthread_mutex_unlock(&g_multiHandleMutex);
+    if(ret != 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Pthread Mutex unlock failed: %d", ret);
+    }
+}
+
+static OCStackResult CHPParserInitializeMultiHandle()
+{
+    CHPParserLockMutex();
+    if(g_multiHandle)
+    {
+        OIC_LOG(ERROR, TAG, "Multi handle already initialized.");
+        CHPParserUnlockMutex();
+        return OC_STACK_OK;
+    }
+
+    g_multiHandle = curl_multi_init();
+    if(!g_multiHandle)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to create multi handle.");
+        CHPParserUnlockMutex();
+        return OC_STACK_ERROR;
+    }
+
+    CHPParserUnlockMutex();
+    return OC_STACK_OK;
+}
+
+static OCStackResult CHPParserTerminateMultiHandle()
+{
+    CHPParserLockMutex();
+    if(!g_multiHandle)
+    {
+        OIC_LOG(ERROR, TAG, "Multi handle not initialized.");
+        CHPParserUnlockMutex();
+        return OC_STACK_OK;
+    }
+
+    curl_multi_cleanup(g_multiHandle);
+    g_multiHandle = NULL;
+    CHPParserUnlockMutex();
+    return OC_STACK_OK;
+}
+
+OCStackResult CHPParserInitialize()
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+
+    OCStackResult ret = CHPParserInitializeMutex();
+    if(ret != OC_STACK_OK)
+    {
+        return ret;
+    }
+
+    ret = CHPParserInitializeMultiHandle();
+    if(ret != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to intialize multi handle: %d", ret);
+        CHPParserTerminate();
+        return ret;
+    }
+
+    ret = CHPParserInitializePipe(g_shutdownFds);
+    if(ret != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to intialize shutdown fds: %d", ret);
+        CHPParserTerminate();
+        return ret;
+    }
+
+    ret = CHPParserInitializePipe(g_refreshFds);
+    if(ret != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to intialize refresh fds: %d", ret);
+        CHPParserTerminate();
+        return ret;
+    }
+
+    // Launch multi_handle processor thread
+    int result = pthread_create(&g_multiHandleThread, NULL, CHPParserExecuteMultiHandle, NULL);
+    if(result != 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Thread start failed with error %d", result);
+        CHPParserTerminate();
+        return OC_STACK_ERROR;
+    }
+
+    g_terminateParser = false;
+    CHPParserLockMutex();
+    g_activeConnections = 0;
+    CHPParserUnlockMutex();
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
+OCStackResult CHPParserTerminate()
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    g_terminateParser = true;
+    if (g_shutdownFds[1] != -1)
+    {
+        // Signal multi_handle thread to come out
+        close(g_shutdownFds[1]);
+    }
+    pthread_join(g_multiHandleThread, NULL);
+
+    OCStackResult ret = CHPParserTerminateMultiHandle();
+    if(ret != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Multi handle termination failed: %d", ret);
+    }
+
+    CHPParserLockMutex();
+    g_activeConnections = 0;
+    CHPParserUnlockMutex();
+
+    ret = CHPParserTerminateMutex();
+    if(ret != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "mutex termination failed: %d", ret);
+    }
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
+static size_t CHPEasyHandleWriteCb(char *buffer, size_t size, size_t num, void *context)
+{
+    size_t dataToWrite = size * num;
+    if(!dataToWrite)
+    {
+        // Empty payload received. Ignore.
+        return 0;
+    }
+
+    if(!context || !buffer || g_terminateParser)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
+        return 0;
+    }
+
+    CHPContext_t* ctx = context;
+    HttpResponse_t *resp = &(ctx->resp);
+
+    if(ctx->writeOffset + dataToWrite > MAX_PAYLOAD_SIZE)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s Payload limit exceeded", __func__);
+        resp->payloadLength = 0;
+        ctx->writeOffset = 0;
+        OICFree(resp->payload);
+        resp->payload = NULL;
+        return 0;
+    }
+
+    if (!resp->payload)
+    {
+        resp->payload = OICMalloc(dataToWrite);
+        if (!resp->payload)
+        {
+            OIC_LOG_V(ERROR, TAG, "%s Out of memory!", __func__);
+            return 0;
+        }
+    }
+    else
+    {
+        // Realloc buffer
+        void *newPayload = OICRealloc(resp->payload, ctx->writeOffset + dataToWrite);
+        if (!newPayload)
+        {
+            OIC_LOG_V(ERROR, TAG, "Realloc failed! Current: %u Extra: %u", ctx->writeOffset,
+                                                                           dataToWrite);
+            resp->payloadLength = 0;
+            ctx->writeOffset = 0;
+            OICFree(resp->payload);
+            resp->payload = NULL;
+            return 0;
+        }
+        resp->payload = newPayload;
+    }
+
+    memcpy(resp->payload + ctx->writeOffset, buffer, dataToWrite);
+    ctx->writeOffset += dataToWrite;
+    resp->payloadLength = ctx->writeOffset;
+
+    OIC_LOG_V(DEBUG, TAG, "%s OUT %u : %u", __func__, resp->payloadLength, dataToWrite);
+    return dataToWrite;
+}
+
+static size_t CHPEasyHandleReadCb(char *buffer, size_t size, size_t num, void *context)
+{
+    if(!context || !buffer || g_terminateParser)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
+        return CURL_READFUNC_ABORT;
+    }
+
+    CHPContext_t *ctx = context;
+    size_t remainingLen = ctx->payloadLength - ctx->readOffset;
+    size_t toTransfer = size * num > remainingLen ? remainingLen : size * num;
+    memcpy(buffer, ctx->payload + ctx->readOffset, toTransfer);
+    ctx->readOffset += toTransfer;
+    return toTransfer;
+}
+
+static size_t CHPEasyHandleHeaderCb(char *buffer, size_t size, size_t num, void *context)
+{
+    size_t dataToWrite = size * num;
+    if(!buffer || !dataToWrite || !context || g_terminateParser)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
+        return 0;
+    }
+
+    /* curl will call this function for each line in response header including status line
+     * and for each http response that it might have received from http server for ex: redirect,
+     * proxy handshakes etc. All these intermediary responses are not useful for us but there
+     * isn't any mechanism to track which one is going to be final.
+     * Hence here we process each response and assume that the relevant one will be the final
+     * response.
+     */
+
+    /* Start of a response is tracked by presence of status line starting with "HTTP/"
+     * This also acts as a reset for everything else (payload, header options) as we are processing
+     * a new response.
+     */
+
+    CHPContext_t *ctx = context;
+    HttpResponse_t *resp = &(ctx->resp);
+    if (dataToWrite > 5)
+    {
+        if (strncmp("HTTP/", buffer, 5) == 0)
+        {
+            OIC_LOG(ERROR, TAG, "New header received");
+            resp->payloadLength = 0;
+            ctx->writeOffset = 0;
+            OICFree(resp->payload);
+            resp->payload = NULL;
+            CHPParserResetHeaderOptions(&(resp->headerOptions));
+            // This is a status line. We are only interested in header options.
+            return dataToWrite;
+        }
+    }
+
+
+    // A header line can have CR LF NULL and spaces at end. Make endOfHeader point to last
+    // character in header value
+    char* endOfHeader = buffer + dataToWrite;
+    while ((endOfHeader > buffer) && (*endOfHeader == '\r' || *endOfHeader == '\n'
+                || *endOfHeader == ' ' || *endOfHeader == '\0'))
+    {
+        endOfHeader--;
+    }
+
+    /* curl might not send the buffer NULL terminated and copying each header is too much overhead
+     * hence use mem family of function to search */
+    char* ptr = (char*) memchr(buffer, ':', dataToWrite);
+    // There is a colon and its not the first character
+    if(ptr && ptr != buffer && ptr <= endOfHeader)
+    {
+        size_t headerFieldLen = ptr - buffer;
+        size_t headerValueLen;
+        char* headerValuePtr;
+
+        /* Skip any white spaces */
+        ptr++;
+        while(ptr <= endOfHeader && *ptr == ' ')
+        {
+            ptr++;
+        }
+
+        if(ptr > endOfHeader)
+        {
+            headerValueLen = 0;
+            headerValuePtr = NULL;
+        }
+        else
+        {
+            // endOfHeader is pointing to last header value character hence +1
+            headerValueLen = endOfHeader - ptr + 1;
+            headerValuePtr = ptr;
+        }
+
+        if (!(resp->headerOptions))
+        {
+            // First header callback, assign storage for header options
+            resp->headerOptions = u_arraylist_create();
+            if (!(resp->headerOptions))
+            {
+                OIC_LOG(ERROR, TAG, "Memory failed!");
+                return 0;
+            }
+        }
+
+        HttpHeaderOption_t *option = OICCalloc(1, sizeof(HttpHeaderOption_t));
+        if (!option)
+        {
+            OIC_LOG(ERROR, TAG, "Memory failed!");
+            return 0;
+        }
+
+        headerFieldLen = headerFieldLen > (sizeof(option->optionName) - 1) ?
+                             (sizeof(option->optionName) - 1): headerFieldLen;
+        memcpy(option->optionName, buffer, headerFieldLen);
+        option->optionName[headerFieldLen] = '\0';
+
+        if(headerValueLen)
+        {
+            headerValueLen = headerValueLen > (sizeof(option->optionData) - 1) ?
+                              (sizeof(option->optionData) - 1): headerValueLen;
+            memcpy(option->optionData, headerValuePtr, headerValueLen);
+            option->optionData[headerValueLen] = '\0';
+        }
+
+        OIC_LOG_V(DEBUG, TAG, "%s:: %s: %s", __func__, option->optionName, option->optionData);
+        // Add to header option list
+        if(!u_arraylist_add(resp->headerOptions, option))
+        {
+            OIC_LOG(ERROR, TAG, "u_arraylist_add failed!");
+            OICFree(option);
+            return 0;
+        }
+    }
+
+    // ignore else as this might be CRLF header lines.
+    return dataToWrite;
+}
+
+static OCStackResult CHPInitializeEasyHandle(CURL** easyHandle, HttpRequest_t *req,
+                                             CHPContext_t* handleContext)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
+    VERIFY_NON_NULL_RET(easyHandle, TAG, "easyHandle", OC_STACK_INVALID_PARAM);
+    VERIFY_NON_NULL_RET(handleContext, TAG, "handleContext", OC_STACK_INVALID_PARAM);
+
+    CURL *e = curl_easy_init();
+    if(!e)
+    {
+        OIC_LOG(ERROR, TAG, "easy init failed!");
+        return OC_STACK_ERROR;
+    }
+
+    /* Set http resource uri */
+    curl_easy_setopt(e, CURLOPT_URL, req->resourceUri);
+    /* Default protocol when scheme is not available in uri */
+    // curl version 7.22 don't support this option.
+    //curl_easy_setopt(e, CURLOPT_DEFAULT_PROTOCOL, "http");
+    /* Set handle context */
+    curl_easy_setopt(e, CURLOPT_PRIVATE, handleContext);
+    curl_easy_setopt(e, CURLOPT_WRITEFUNCTION, CHPEasyHandleWriteCb);
+    curl_easy_setopt(e, CURLOPT_WRITEDATA, handleContext);
+    curl_easy_setopt(e, CURLOPT_READFUNCTION, CHPEasyHandleReadCb);
+    curl_easy_setopt(e, CURLOPT_READDATA, handleContext);
+    curl_easy_setopt(e, CURLOPT_HEADERFUNCTION, CHPEasyHandleHeaderCb);
+    curl_easy_setopt(e, CURLOPT_HEADERDATA, handleContext);
+
+    /* Allow access to only http server's */
+    curl_easy_setopt(e, CURLOPT_PROTOCOLS,
+                     CURLPROTO_HTTP | CURLPROTO_HTTPS);
+    /* complete connection within 15 seconds */
+    curl_easy_setopt(e, CURLOPT_CONNECTTIMEOUT, 15L);
+    /* Abort transaction if getting less than 1kbps for 60 seconds */
+    curl_easy_setopt(e, CURLOPT_LOW_SPEED_LIMIT, 1024L);
+    curl_easy_setopt(e, CURLOPT_LOW_SPEED_TIME, 60L);
+    curl_easy_setopt(e, CURLOPT_USERAGENT, DEFAULT_USER_AGENT);
+    /* Close connection once done with transaction */
+    curl_easy_setopt(e, CURLOPT_FORBID_REUSE, 1L);
+    /* Allow redirect */
+    curl_easy_setopt(e, CURLOPT_FOLLOWLOCATION, 1L);
+    /* Only redirect to http servers */
+    curl_easy_setopt(e, CURLOPT_REDIR_PROTOCOLS,
+                     CURLPROTO_HTTP | CURLPROTO_HTTPS);
+    /* Limit maximum redirects */
+    curl_easy_setopt(e, CURLOPT_MAXREDIRS, 10L);
+
+    handleContext->writeOffset = 0;
+    handleContext->readOffset = 0;
+    switch(req->method)
+    {
+        case CHP_GET:
+            OIC_LOG(DEBUG, TAG, "Sending GET request");
+            curl_easy_setopt(e, CURLOPT_HTTPGET, 1);
+            break;
+        case CHP_POST:
+            OIC_LOG(DEBUG, TAG, "Sending POST request");
+            curl_easy_setopt(e, CURLOPT_POST, 1);
+            curl_easy_setopt(e, CURLOPT_POSTFIELDS, NULL);
+            curl_easy_setopt(e, CURLOPT_POSTFIELDSIZE, req->payloadLength);
+            handleContext->payloadLength = req->payloadLength;
+            handleContext->payload = req->payload;
+            req->payloadCached = true;
+            break;
+        case CHP_PUT:
+            OIC_LOG(DEBUG, TAG, "Sending PUT request");
+            curl_easy_setopt(e, CURLOPT_UPLOAD, 1);
+            curl_easy_setopt(e, CURLOPT_INFILESIZE, req->payloadLength);
+            handleContext->payloadLength = req->payloadLength;
+            handleContext->payload = req->payload;
+            req->payloadCached = true;
+            break;;
+        case CHP_DELETE:
+            OIC_LOG(DEBUG, TAG, "Sending DELETE request");
+            /* libcurl don't have direct option for sending DELETE */
+            curl_easy_setopt(e, CURLOPT_CUSTOMREQUEST, "DELETE");
+            break;
+        default:
+            return OC_STACK_INVALID_METHOD;
+    }
+
+    // Add header options from request
+    struct curl_slist *list = NULL;
+    char buffer[CHP_MAX_HF_NAME_LENGTH + CHP_MAX_HF_DATA_LENGTH + 2]; // extra 2 bytes for ": "
+
+    if (req->headerOptions)
+    {
+        HttpHeaderOption_t *option = NULL;
+        int headerCount = u_arraylist_length(req->headerOptions);
+        for(int i = 0; i < headerCount; i++)
+        {
+            option = u_arraylist_get(req->headerOptions, i);
+            if(option)
+            {
+                OIC_LOG_V(DEBUG, TAG, "Adding header option: %s", buffer);
+                snprintf(buffer, sizeof(buffer), "%s: %s", option->optionName, option->optionData);
+                list = curl_slist_append(list, buffer);
+            }
+        }
+    }
+
+    /* Add content-type and accept header */
+    snprintf(buffer, sizeof(buffer), "Accept: %s", req->acceptFormat);
+    list = curl_slist_append(list, buffer);
+    snprintf(buffer, sizeof(buffer), "Content-Type: %s", req->payloadFormat);
+    curl_easy_setopt(e, CURLOPT_HTTPHEADER, list);
+
+    *easyHandle = e;
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
+OCStackResult CHPPostHttpRequest(HttpRequest_t *req, CHPResponseCallback httpcb,
+                                 void *context)
+{
+    OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+    VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
+    VERIFY_NON_NULL_RET(httpcb, TAG, "httpcb", OC_STACK_INVALID_PARAM);
+
+    CHPContext_t *ctxt = OICCalloc(1, sizeof(CHPContext_t));
+    if (!ctxt)
+    {
+        OIC_LOG(ERROR, TAG, "Memory failed!");
+        return OC_STACK_NO_MEMORY;
+    }
+
+    ctxt->cb = httpcb;
+    ctxt->context = context;
+    OCStackResult ret = CHPInitializeEasyHandle(&ctxt->easyHandle, req, ctxt);
+    if(ret != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to initialize easy handle [%d]", ret);
+        OICFree(ctxt);
+        return ret;
+    }
+
+    // Add easy_handle to multi_handle
+    CHPParserLockMutex();
+    curl_multi_add_handle(g_multiHandle, ctxt->easyHandle);
+    g_activeConnections++;
+    CHPParserUnlockMutex();
+    // Notify refreshfd
+    ssize_t len = 0;
+    do
+    {
+        len = write(g_refreshFds[1], "w", 1);
+    } while ((len == -1) && (errno == EINTR));
+
+    if ((len == -1) && (errno != EINTR) && (errno != EPIPE))
+    {
+        OIC_LOG_V(DEBUG, TAG, "refresh failed: %s", strerror(errno));
+    }
+
+    OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+    return OC_STACK_OK;
+}
+
index 1730a1e..02931f6 100644 (file)
@@ -39,6 +39,7 @@ Requires(post): /sbin/ldconfig
 %{!?LOGGING: %define LOGGING True}
 %{!?ROUTING: %define ROUTING EP}
 %{!?WITH_TCP: %define WITH_TCP true}
+%{!?WITH_PROXY: %define WITH_PROXY False}
 %{!?ES_TARGET_ENROLLEE: %define ES_TARGET_ENROLLEE tizen}
 %{!?VERBOSE: %define VERBOSE 1}
 
@@ -110,7 +111,7 @@ scons -j2 --prefix=%{_prefix} \
        VERBOSE=%{VERBOSE} \
        TARGET_OS=tizen TARGET_ARCH=%{RPM_ARCH} TARGET_TRANSPORT=%{TARGET_TRANSPORT} \
        RELEASE=%{RELEASE} SECURED=%{SECURED} WITH_TCP=%{WITH_TCP} WITH_CLOUD=%{WITH_CLOUD} LOGGING=%{LOGGING} ROUTING=%{ROUTING} \
-       ES_TARGET_ENROLLEE=%{ES_TARGET_ENROLLEE} LIB_INSTALL_DIR=%{_libdir}
+       ES_TARGET_ENROLLEE=%{ES_TARGET_ENROLLEE} LIB_INSTALL_DIR=%{_libdir} WITH_PROXY=%{WITH_PROXY}
 
 
 
@@ -120,7 +121,7 @@ CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ;
 scons install --install-sandbox=%{buildroot} --prefix=%{_prefix} \
        TARGET_OS=tizen TARGET_ARCH=%{RPM_ARCH} TARGET_TRANSPORT=%{TARGET_TRANSPORT} \
        RELEASE=%{RELEASE} SECURED=%{SECURED} WITH_TCP=%{WITH_TCP} WITH_CLOUD=%{WITH_CLOUD} LOGGING=%{LOGGING} ROUTING=%{ROUTING} \
-       ES_TARGET_ENROLLEE=%{ES_TARGET_ENROLLEE} LIB_INSTALL_DIR=%{_libdir}
+       ES_TARGET_ENROLLEE=%{ES_TARGET_ENROLLEE} LIB_INSTALL_DIR=%{_libdir} WITH_PROXY=%{WITH_PROXY}