From eb4025b48bfd003944a701e7daef86626cdaa505 Mon Sep 17 00:00:00 2001 From: Abhishek Sharma Date: Thu, 8 Sep 2016 16:55:24 +0530 Subject: [PATCH] Development of CoAP-HTTP Proxy 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 Reviewed-on: https://gerrit.iotivity.org/gerrit/11601 Tested-by: jenkins-iotivity Reviewed-by: jihwan seo Reviewed-by: Uze Choi Tested-by: Uze Choi --- build_common/SConscript | 1 + resource/c_common/oic_string/include/oic_string.h | 8 + resource/c_common/oic_string/src/oic_string.c | 11 + resource/csdk/SConscript | 3 + resource/csdk/connectivity/api/cacommon.h | 6 +- resource/csdk/connectivity/api/cainterface.h | 11 +- resource/csdk/connectivity/src/SConscript | 3 + resource/csdk/connectivity/src/caprotocolmessage.c | 42 +- resource/csdk/logger/include/logger.h | 5 + resource/csdk/stack/include/ocstack.h | 9 + resource/csdk/stack/include/ocstackconfig.h | 10 +- resource/csdk/stack/include/octypes.h | 43 +- resource/csdk/stack/include/payload_logging.h | 47 +- .../samples/linux/SimpleClientServer/occlient.cpp | 53 +- .../samples/linux/SimpleClientServer/occlient.h | 10 +- .../samples/tizen/SimpleClientServer/SConscript | 3 + .../packaging/com.oic.ri.sample.spec | 2 +- .../tizen/SimpleClientServer/scons/SConscript | 3 + resource/csdk/stack/samples/tizen/build/SConscript | 6 +- .../csdk/stack/samples/tizen/build/gbsbuild.sh | 7 +- .../samples/tizen/build/packaging/com.oic.ri.spec | 2 +- resource/csdk/stack/src/occlientcb.c | 9 +- resource/csdk/stack/src/ocstack.c | 81 +- service/SConscript | 4 + service/coap-http-proxy/SConscript | 86 ++ service/coap-http-proxy/include/CoapHttpHandler.h | 58 ++ service/coap-http-proxy/include/CoapHttpMap.h | 94 +++ service/coap-http-proxy/include/CoapHttpParser.h | 195 +++++ service/coap-http-proxy/samples/SConscript | 54 ++ service/coap-http-proxy/samples/proxy_main.c | 95 +++ service/coap-http-proxy/src/CoapHttpHandler.c | 502 ++++++++++++ service/coap-http-proxy/src/CoapHttpMap.c | 520 ++++++++++++ service/coap-http-proxy/src/CoapHttpParser.c | 869 +++++++++++++++++++++ tools/tizen/iotivity.spec | 5 +- 34 files changed, 2775 insertions(+), 82 deletions(-) create mode 100644 service/coap-http-proxy/SConscript create mode 100644 service/coap-http-proxy/include/CoapHttpHandler.h create mode 100644 service/coap-http-proxy/include/CoapHttpMap.h create mode 100644 service/coap-http-proxy/include/CoapHttpParser.h create mode 100644 service/coap-http-proxy/samples/SConscript create mode 100644 service/coap-http-proxy/samples/proxy_main.c create mode 100644 service/coap-http-proxy/src/CoapHttpHandler.c create mode 100644 service/coap-http-proxy/src/CoapHttpMap.c create mode 100644 service/coap-http-proxy/src/CoapHttpParser.c diff --git a/build_common/SConscript b/build_common/SConscript index 0264e6a..b700453 100644 --- a/build_common/SConscript +++ b/build_common/SConscript @@ -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'])) diff --git a/resource/c_common/oic_string/include/oic_string.h b/resource/c_common/oic_string/include/oic_string.h index 4aea907..f736bfa 100644 --- a/resource/c_common/oic_string/include/oic_string.h +++ b/resource/c_common/oic_string/include/oic_string.h @@ -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. * diff --git a/resource/c_common/oic_string/src/oic_string.c b/resource/c_common/oic_string/src/oic_string.c index 644b4d2..8a8a960 100644 --- a/resource/c_common/oic_string/src/oic_string.c +++ b/resource/c_common/oic_string/src/oic_string.c @@ -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); diff --git a/resource/csdk/SConscript b/resource/csdk/SConscript index 6893732..1d17c0e 100644 --- a/resource/csdk/SConscript +++ b/resource/csdk/SConscript @@ -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']) diff --git a/resource/csdk/connectivity/api/cacommon.h b/resource/csdk/connectivity/api/cacommon.h index cedc763..db06c44 100644 --- a/resource/csdk/connectivity/api/cacommon.h +++ b/resource/csdk/connectivity/api/cacommon.h @@ -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; diff --git a/resource/csdk/connectivity/api/cainterface.h b/resource/csdk/connectivity/api/cainterface.h index 5d50d8d..1c2bbfa 100644 --- a/resource/csdk/connectivity/api/cainterface.h +++ b/resource/csdk/connectivity/api/cainterface.h @@ -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" */ diff --git a/resource/csdk/connectivity/src/SConscript b/resource/csdk/connectivity/src/SConscript index 16d654c..44921de 100644 --- a/resource/csdk/connectivity/src/SConscript +++ b/resource/csdk/connectivity/src/SConscript @@ -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']) diff --git a/resource/csdk/connectivity/src/caprotocolmessage.c b/resource/csdk/connectivity/src/caprotocolmessage.c index 94810ca..d8f1f06 100644 --- a/resource/csdk/connectivity/src/caprotocolmessage.c +++ b/resource/csdk/connectivity/src/caprotocolmessage.c @@ -51,6 +51,17 @@ 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: diff --git a/resource/csdk/logger/include/logger.h b/resource/csdk/logger/include/logger.h index 7ccb51c..4304d23 100644 --- a/resource/csdk/logger/include/logger.h +++ b/resource/csdk/logger/include/logger.h @@ -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 diff --git a/resource/csdk/stack/include/ocstack.h b/resource/csdk/stack/include/ocstack.h index f168df2..ad06006 100644 --- a/resource/csdk/stack/include/ocstack.h +++ b/resource/csdk/stack/include/ocstack.h @@ -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. diff --git a/resource/csdk/stack/include/ocstackconfig.h b/resource/csdk/stack/include/ocstackconfig.h index 96afa7a..515d601 100644 --- a/resource/csdk/stack/include/ocstackconfig.h +++ b/resource/csdk/stack/include/ocstackconfig.h @@ -63,12 +63,20 @@ * 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). diff --git a/resource/csdk/stack/include/octypes.h b/resource/csdk/stack/include/octypes.h index 5fafce2..fc1d1f0 100644 --- a/resource/csdk/stack/include/octypes.h +++ b/resource/csdk/stack/include/octypes.h @@ -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; /** diff --git a/resource/csdk/stack/include/payload_logging.h b/resource/csdk/stack/include/payload_logging.h index 5f722f4..121d219 100644 --- a/resource/csdk/stack/include/payload_logging.h +++ b/resource/csdk/stack/include/payload_logging.h @@ -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); diff --git a/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.cpp b/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.cpp index 061c94f..4a90b45 100644 --- a/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.cpp +++ b/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.cpp @@ -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; diff --git a/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.h b/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.h index 2ed2e3d..caa0ec9 100644 --- a/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.h +++ b/resource/csdk/stack/samples/linux/SimpleClientServer/occlient.h @@ -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); diff --git a/resource/csdk/stack/samples/tizen/SimpleClientServer/SConscript b/resource/csdk/stack/samples/tizen/SimpleClientServer/SConscript index f48fcb0..532eb20 100644 --- a/resource/csdk/stack/samples/tizen/SimpleClientServer/SConscript +++ b/resource/csdk/stack/samples/tizen/SimpleClientServer/SConscript @@ -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') diff --git a/resource/csdk/stack/samples/tizen/SimpleClientServer/packaging/com.oic.ri.sample.spec b/resource/csdk/stack/samples/tizen/SimpleClientServer/packaging/com.oic.ri.sample.spec index 3054430..832acf1 100644 --- a/resource/csdk/stack/samples/tizen/SimpleClientServer/packaging/com.oic.ri.sample.spec +++ b/resource/csdk/stack/samples/tizen/SimpleClientServer/packaging/com.oic.ri.sample.spec @@ -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 diff --git a/resource/csdk/stack/samples/tizen/SimpleClientServer/scons/SConscript b/resource/csdk/stack/samples/tizen/SimpleClientServer/scons/SConscript index 5237143..9ec20c5 100644 --- a/resource/csdk/stack/samples/tizen/SimpleClientServer/scons/SConscript +++ b/resource/csdk/stack/samples/tizen/SimpleClientServer/scons/SConscript @@ -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' ]) diff --git a/resource/csdk/stack/samples/tizen/build/SConscript b/resource/csdk/stack/samples/tizen/build/SConscript index e3c922b..9a2c022 100644 --- a/resource/csdk/stack/samples/tizen/build/SConscript +++ b/resource/csdk/stack/samples/tizen/build/SConscript @@ -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 diff --git a/resource/csdk/stack/samples/tizen/build/gbsbuild.sh b/resource/csdk/stack/samples/tizen/build/gbsbuild.sh index af03a20..d0b488f 100644 --- a/resource/csdk/stack/samples/tizen/build/gbsbuild.sh +++ b/resource/csdk/stack/samples/tizen/build/gbsbuild.sh @@ -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" diff --git a/resource/csdk/stack/samples/tizen/build/packaging/com.oic.ri.spec b/resource/csdk/stack/samples/tizen/build/packaging/com.oic.ri.spec index 09a9ca4..ea8f03f 100644 --- a/resource/csdk/stack/samples/tizen/build/packaging/com.oic.ri.spec +++ b/resource/csdk/stack/samples/tizen/build/packaging/com.oic.ri.spec @@ -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} diff --git a/resource/csdk/stack/src/occlientcb.c b/resource/csdk/stack/src/occlientcb.c index 42100d6..872e8c4 100644 --- a/resource/csdk/stack/src/occlientcb.c +++ b/resource/csdk/stack/src/occlientcb.c @@ -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); diff --git a/resource/csdk/stack/src/ocstack.c b/resource/csdk/stack/src/ocstack.c index f11501c..a492031 100644 --- a/resource/csdk/stack/src/ocstack.c +++ b/resource/csdk/stack/src/ocstack.c @@ -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) { diff --git a/service/SConscript b/service/SConscript index 2f92966..22eb6db 100755 --- a/service/SConscript +++ b/service/SConscript @@ -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 index 0000000..cc3aa2c --- /dev/null +++ b/service/coap-http-proxy/SConscript @@ -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 index 0000000..7843000 --- /dev/null +++ b/service/coap-http-proxy/include/CoapHttpHandler.h @@ -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 index 0000000..c0e9d6e --- /dev/null +++ b/service/coap-http-proxy/include/CoapHttpMap.h @@ -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 index 0000000..5e5067c --- /dev/null +++ b/service/coap-http-proxy/include/CoapHttpParser.h @@ -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 +#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 index 0000000..fe165a2 --- /dev/null +++ b/service/coap-http-proxy/samples/SConscript @@ -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 index 0000000..9f2479d --- /dev/null +++ b/service/coap-http-proxy/samples/proxy_main.c @@ -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 +#ifdef HAVE_UNISTD_H +#include +#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 index 0000000..f0ee86d --- /dev/null +++ b/service/coap-http-proxy/src/CoapHttpHandler.c @@ -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 index 0000000..82252a7 --- /dev/null +++ b/service/coap-http-proxy/src/CoapHttpMap.c @@ -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 +#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 index 0000000..c24edff --- /dev/null +++ b/service/coap-http-proxy/src/CoapHttpParser.c @@ -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 +#include +#include + +#include "CoapHttpParser.h" +#include "oic_malloc.h" +#include "oic_string.h" +#include "uarraylist.h" +#include "logger.h" + +#include +#include +#ifdef HAVE_PTHREAD_H +#include +#endif +#if !defined(_MSC_VER) +#include +#endif //!defined(_MSC_VER) +#include +#include +#if !defined(_WIN32) +#include +#endif //!defined(_WIN32) +#include + +#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; +} + diff --git a/tools/tizen/iotivity.spec b/tools/tizen/iotivity.spec index 1730a1e..02931f6 100644 --- a/tools/tizen/iotivity.spec +++ b/tools/tizen/iotivity.spec @@ -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} -- 2.7.4