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']))
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.
*
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);
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'])
/**
* Max header options data length.
*/
-#define CA_MAX_HEADER_OPTION_DATA_LENGTH 20
+#define CA_MAX_HEADER_OPTION_DATA_LENGTH 1024
/**
* Max token length.
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;
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" */
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'])
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)
{
&& 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++;
}
uint32_t optionLength = 0;
bool isfirstsetflag = false;
bool isQueryBeingProcessed = false;
+#ifdef WITH_CHPROXY
+ bool isProxyRequest = false;
+#endif
while ((option = coap_option_next(&opt_iter)))
{
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]",
}
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))
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:
{
#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
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.
/**
* Maximum Length of the vendor specific header option
*/
-#define MAX_HEADER_OPTION_DATA_LENGTH (20)
+#define MAX_HEADER_OPTION_DATA_LENGTH (1024)
/**
* Sets the time to live (TTL) for response callback(s).
/** 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"
typedef enum
{
OC_FORMAT_CBOR,
+ OC_FORMAT_JSON,
OC_FORMAT_UNDEFINED,
OC_FORMAT_UNSUPPORTED,
} OCPayloadFormat;
{
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;
/**
/** The payload is an OCPresencePayload */
PAYLOAD_TYPE_PRESENCE,
/** The payload is an OCRDPayload */
- PAYLOAD_TYPE_RD
+ PAYLOAD_TYPE_RD,
} OCPayloadType;
/**
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)
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)
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);
{
// Add a zero-character string terminator.
char *securityData = (char *)OICMalloc(payloadSize + 1);
-
+
if (securityData)
{
memcpy(securityData, payload->securityData, payloadSize);
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;
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");
"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,
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);
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;
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;
}
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:
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];
{
int opt;
- while ((opt = getopt(argc, argv, "u:t:c:")) != -1)
+ while ((opt = getopt(argc, argv, "u:t:c:p:")) != -1)
{
switch(opt)
{
case 'c':
Connectivity = atoi(optarg);
break;
+ case 'p':
+ if(optarg)
+ {
+ httpResource = optarg;
+ }
+ break;
default:
PrintUsage();
return -1;
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;
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;
/* 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
*/
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);
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
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')
%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
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'
])
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 = [
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
echo $7
export WITH_TCP=$7
+echo $8
+export WITH_PROXY=$8
+
echo $TARGET_TRANSPORT
echo $BUILD_SAMPLE
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"
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"
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}
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;
}
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);
// 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;
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;
}
}
// 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;
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;
}
}
}
+#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)
{
#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')
--- /dev/null
+#******************************************************************
+#
+# 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')
--- /dev/null
+/* ****************************************************************
+ *
+ * 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
--- /dev/null
+/* ****************************************************************
+ *
+ * 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
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+/**
+ * @file
+ * This file contains HTTP parsing functions
+ */
+
+#ifndef COAP_HTTP_PARSER_H_
+#define COAP_HTTP_PARSER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <unistd.h>
+#include "uarraylist.h"
+#include "octypes.h"
+
+#define CHP_MAX_HF_DATA_LENGTH 1024
+#define CHP_MAX_HF_NAME_LENGTH 255
+#define JSON_CONTENT_TYPE "application/json"
+#define CBOR_CONTENT_TYPE "application/cbor"
+#define ACCEPT_MEDIA_TYPE (CBOR_CONTENT_TYPE "; q=1.0, " JSON_CONTENT_TYPE "; q=0.5")
+
+// HTTP Option types
+#define HTTP_OPTION_CACHE_CONTROL "cache-control"
+#define HTTP_OPTION_ACCEPT "accept"
+#define HTTP_OPTION_IF_MATCH "if-match"
+#define HTTP_OPTION_IF_NONE_MATCH "if-none-match"
+#define HTTP_OPTION_ETAG "etag"
+#define HTTP_OPTION_CONTENT_TYPE "content-type"
+#define HTTP_OPTION_CONTENT_LENGTH "content-length"
+#define HTTP_OPTION_EXPIRES "expires"
+
+/**
+ * @enum HttpResponseResult_t
+ * Enums for HTTP Response values
+ */
+typedef enum
+{
+ /* Response status code - START HERE */
+ CHP_EMPTY = 0, /**< Empty */
+ CHP_SUCCESS = 200, /**< Success */
+ CHP_CREATED = 201, /**< Created */
+ CHP_ACCEPTED = 202, /**< Accepted */
+ CHP_NO_CONTENT = 204, /**< No Content */
+ CHP_RESET_CONTENT = 205, /**< Reset content */
+ CHP_NOT_MODIFIED = 304, /**< Not Modified */
+ CHP_BAD_REQ = 400, /**< Bad Request */
+ CHP_UNAUTHORIZED_REQ = 401, /**< Unauthorized Request */
+ CHP_FORBIDDEN_REQ = 403, /**< Forbidden Request */
+ CHP_NOT_FOUND = 404, /**< Not found */
+ CHP_NOT_ACCEPTABLE = 406, /**< Not Acceptable */
+ CHP_REQUEST_ENTITY_TOO_LARGE = 413, /**< Request Entity Too Large */
+ CHP_REQUEST_URI_TOO_LARGE = 414, /**< Request URI Too Large */
+ CHP_UNSUPPORTED_MEDIA_TYPE = 415, /**< Unsupported Media type */
+ CHP_INTERNAL_SERVER_ERROR = 500, /**< Internal server Error */
+ CHP_NOT_IMPLEMENTED = 501, /**< Not Implemented */
+ CHP_BAD_GATEWAY = 502, /**< Bad Gateway */
+ CHP_SERVICE_UNAVAILABLE = 503, /**< Service Unavailable */
+ CHP_GATEWAY_TIMEOUT = 504, /**< Gateway Timeout */
+ CHP_VERSION_NOT_SUPPORTED = 505 /**< Version not supported */
+ /* Response status code - END HERE */
+} HttpResponseResult_t;
+
+/**
+ * Header fields structure to be filled
+ *
+ * This structure is used to hold header information.
+ */
+typedef struct
+{
+ uint16_t optionLength; /**< Option length. **/
+ char optionName[CHP_MAX_HF_NAME_LENGTH]; /**< Option name. **/
+ char optionData[CHP_MAX_HF_DATA_LENGTH]; /**< Option data values. **/
+} HttpHeaderOption_t;
+
+typedef enum
+{
+ CHP_GET = 1, /**< GET */
+ CHP_POST, /**< POST */
+ CHP_PUT, /**< PUT */
+ CHP_DELETE, /**< DELETE */
+ CHP_INVALID
+}HttpMethod_t;
+
+typedef struct HttpRequest_t
+{
+ unsigned short httpMajor;
+ unsigned short httpMinor;
+ HttpMethod_t method;
+ u_arraylist_t *headerOptions;
+ char resourceUri[CHP_MAX_HF_DATA_LENGTH];
+ void *payload;
+ size_t payloadLength;
+ bool payloadCached;
+ char payloadFormat[CHP_MAX_HF_DATA_LENGTH];
+ char acceptFormat[CHP_MAX_HF_DATA_LENGTH];
+}HttpRequest_t;
+
+typedef struct HttpResponse_t
+{
+ unsigned short httpMajor;
+ unsigned short httpMinor;
+ HttpResponseResult_t status;
+ u_arraylist_t *headerOptions;
+ char dataFormat[CHP_MAX_HF_DATA_LENGTH];
+ void *payload;
+ size_t payloadLength;
+}HttpResponse_t;
+
+typedef void (*CHPResponseCallback)(const HttpResponse_t *response, void *context);
+
+/**
+ * Function to initialize Parser and HTTP stack.
+ */
+OCStackResult CHPParserInitialize();
+
+/**
+ * Function to terminate parser and HTTP stack.
+ */
+OCStackResult CHPParserTerminate();
+
+/**
+ * Function to initiate TCP session and post HTTP request. If the method returns
+ * success, payload might be cached by the parser (req->payloadCached) and caller shall not free the
+ * payload if the flag is set.
+ * @param[in] req Object containing HTTP request information.
+ * @param[in] httpcb Callback for http response.
+ * @param[in] context Any app specific context for request
+ */
+OCStackResult CHPPostHttpRequest(HttpRequest_t *req, CHPResponseCallback httpcb,
+ void *context);
+
+/**
+ * Macro to verify the validity of input argument.
+ *
+ * @param arg log level
+ * @param log_tag log tag
+ * @param log_message log message
+ * @param ret return value
+ */
+#define VERIFY_NON_NULL_RET(arg, log_tag, log_message, ret) \
+ if (NULL == (arg)) \
+ { \
+ OIC_LOG_V(ERROR, (log_tag), "Invalid input:%s", (log_message)); \
+ return (ret); \
+ } \
+
+/**
+ * Macro to verify the validity of input argument.
+ *
+ * @param arg log level
+ * @param log_tag log tag
+ * @param log_message log message
+ */
+#define VERIFY_NON_NULL(arg, log_tag, log_message) \
+ VERIFY_NON_NULL_RET((arg), (log_tag), (log_message), CA_STATUS_INVALID_PARAM)
+
+/**
+ * Macro to verify the validity of input argument.
+ *
+ * @param arg log level
+ * @param log_tag log tag
+ * @param log_message log message
+ */
+#define VERIFY_NON_NULL_VOID(arg, log_tag, log_message) \
+ if (NULL == (arg)) { \
+ OIC_LOG_V(ERROR, (log_tag), "Invalid input:%s", (log_message)); \
+ return; \
+ } \
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null
+#******************************************************************
+#
+# 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')
--- /dev/null
+//******************************************************************
+//
+// Copyright 2016 Samsung Electronics All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#include "CoapHttpHandler.h"
+
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+int g_quitFlag = 0;
+
+void handleSigInt(int signum);
+
+/*
+* This method is an entry point of CoAP-HTTP Proxy.
+*/
+
+int main()
+{
+ printf("CoAP-HTTP proxy is starting..\n");
+ OCStackResult result = OCInit(NULL, 0, OC_SERVER);
+ if (result != OC_STACK_OK)
+ {
+ printf("Failed starting proxy\n");
+ return 0;
+ }
+
+ if (CHPInitialize() != OC_STACK_OK)
+ {
+ printf("Failed to start proxy.\n");
+ OCStop();
+ return 0;
+ }
+
+ printf("Proxy started successfully.\n");
+
+ signal(SIGINT, handleSigInt);
+ while (!g_quitFlag)
+ {
+ if (OCProcess() != OC_STACK_OK)
+ {
+ CHPTerminate();
+ OCStop();
+ printf("OCStack process error\n");
+ return 0;
+ }
+ }
+
+ if (CHPTerminate() != OC_STACK_OK)
+ {
+ printf("CHPTerminate failed.\n");
+ }
+ else
+ {
+ printf("CHPTerminate success.\n");
+ }
+
+ OCStop();
+ printf("Exiting proxy main loop...\n");
+ return 0;
+
+}
+
+/*
+* This is a signal handling function for SIGINT(CTRL+C).
+* A Resource Directory handle the SIGINT signal for safe exit.
+*
+* @param[in] signal
+* signal number of caught signal.
+*/
+void handleSigInt(int signum)
+{
+ if (signum == SIGINT)
+ {
+ g_quitFlag = 1;
+ }
+}
+
--- /dev/null
+/* ****************************************************************
+ *
+ * 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;
+}
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include "CoapHttpMap.h"
+#include <string.h>
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "logger.h"
+#include "ocstack.h"
+#include "pdu.h"
+#include "ocpayload.h"
+
+#define TAG "CHPMap"
+
+int CHPGetOptionID(const char *httpOptionName)
+{
+ if (!httpOptionName)
+ {
+ OIC_LOG(ERROR, TAG, "HTTP option name is NULL");
+ return 0;
+ }
+
+ OICStringToLower((char *)httpOptionName);
+ if (0 == strcmp(httpOptionName, HTTP_OPTION_CACHE_CONTROL) ||
+ 0 == strcmp(httpOptionName, HTTP_OPTION_EXPIRES))
+ {
+ return COAP_OPTION_MAXAGE;
+ }
+ else if (0 == strcmp(httpOptionName, HTTP_OPTION_IF_MATCH))
+ {
+ return COAP_OPTION_IF_MATCH;
+ }
+ else if (0 == strcmp(httpOptionName, HTTP_OPTION_IF_NONE_MATCH))
+ {
+ return COAP_OPTION_IF_NONE_MATCH;
+ }
+ else if (0 == strcmp(httpOptionName, HTTP_OPTION_ETAG))
+ {
+ return COAP_OPTION_ETAG;
+ }
+ else
+ {
+ OIC_LOG_V(ERROR, TAG, "No Mapping found for %s", httpOptionName);
+ }
+
+ return 0;
+}
+
+OCStackResult CHPGetOCCode(const HttpResponseResult_t httpCode, const OCMethod method,
+ OCEntityHandlerResult *ocfCode)
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ OIC_LOG_V(DEBUG, TAG, "Http Code is %d", httpCode);
+
+ switch (httpCode)
+ {
+ case CHP_SUCCESS:
+ if (OC_REST_GET == method)
+ {
+ *ocfCode = OC_EH_CONTENT;
+ }
+ else if (OC_REST_DELETE == method)
+ {
+ *ocfCode = OC_EH_RESOURCE_DELETED;
+ }
+ else
+ {
+ *ocfCode = OC_EH_CHANGED;
+ }
+ break;
+ case CHP_NO_CONTENT:
+ if (OC_REST_DELETE == method)
+ {
+ *ocfCode = OC_EH_RESOURCE_DELETED;
+ }
+ else
+ {
+ *ocfCode = OC_EH_CHANGED;
+ }
+ break;
+ case CHP_CREATED:
+ *ocfCode = OC_EH_RESOURCE_CREATED;
+ break;
+ case CHP_NOT_MODIFIED:
+ *ocfCode = OC_EH_VALID;
+ break;
+ case CHP_BAD_REQ:
+ case CHP_REQUEST_URI_TOO_LARGE:
+ *ocfCode = OC_EH_BAD_REQ;
+ break;
+ case CHP_BAD_GATEWAY:
+ case CHP_VERSION_NOT_SUPPORTED:
+ *ocfCode = OC_EH_BAD_GATEWAY;
+ break;
+ case CHP_UNAUTHORIZED_REQ:
+ case CHP_FORBIDDEN_REQ:
+ case CHP_NOT_FOUND:
+ case CHP_NOT_ACCEPTABLE:
+ case CHP_REQUEST_ENTITY_TOO_LARGE:
+ case CHP_UNSUPPORTED_MEDIA_TYPE:
+ case CHP_INTERNAL_SERVER_ERROR:
+ case CHP_NOT_IMPLEMENTED:
+ case CHP_SERVICE_UNAVAILABLE:
+ case CHP_GATEWAY_TIMEOUT:
+ *ocfCode = httpCode;
+ break;
+ default:
+ OIC_LOG_V(ERROR, TAG, "HTTP Response code[%d] is not matching the OCF Response code",
+ httpCode);
+ return OC_STACK_ERROR;
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
+OCStackResult CHPGetOCOption(const HttpHeaderOption_t *httpOption, OCHeaderOption *ocfOption)
+{
+ OIC_LOG(DEBUG, TAG, "CHPGetCoAPOption IN");
+ if (!httpOption)
+ {
+ OIC_LOG(ERROR, TAG, "HTTP option is Null");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ ocfOption->optionID = CHPGetOptionID(httpOption->optionName);
+ if (!ocfOption->optionID)
+ {
+ OIC_LOG(INFO, TAG, "No match for HTTP option found");
+ return OC_STACK_INVALID_OPTION;
+ }
+
+ ocfOption->protocolID = OC_COAP_ID;
+ ocfOption->optionLength = httpOption->optionLength < sizeof(ocfOption->optionData) ?
+ httpOption->optionLength : sizeof(ocfOption->optionData);
+ memcpy(ocfOption->optionData, httpOption->optionData, ocfOption->optionLength);
+
+ OIC_LOG(DEBUG, TAG, "CHPGetCoAPOption OUT");
+ return OC_STACK_OK;
+}
+
+OCPayloadFormat CHPGetOCContentType(const char *httpContentType)
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+
+ OICStringToLower((char *)httpContentType);
+ if (strstr(httpContentType, CBOR_CONTENT_TYPE))
+ {
+ return OC_FORMAT_CBOR;
+ }
+ else if (strstr(httpContentType, JSON_CONTENT_TYPE))
+ {
+ return OC_FORMAT_JSON;
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_FORMAT_UNSUPPORTED;
+}
+
+OCStackResult CHPGetHttpMethod(const OCMethod method, HttpMethod_t *httpMethod)
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+
+ switch (method)
+ {
+ case OC_REST_GET:
+ *httpMethod = CHP_GET;
+ break;
+ case OC_REST_PUT:
+ *httpMethod = CHP_PUT;
+ break;
+ case OC_REST_POST:
+ *httpMethod = CHP_POST;
+ break;
+ case OC_REST_DELETE:
+ *httpMethod = CHP_DELETE;
+ break;
+ default:
+ *httpMethod = CHP_INVALID;
+ OIC_LOG_V(ERROR, TAG, "Unknown method type %d", method);
+ return OC_STACK_INVALID_METHOD;
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
+OCStackResult CHPGetHttpOption(const OCHeaderOption* option, HttpHeaderOption_t** httpOption)
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ if (!option)
+ {
+ OIC_LOG(ERROR, TAG, "option is NULL");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ *httpOption = (HttpHeaderOption_t *)OICCalloc(1, sizeof(HttpHeaderOption_t));
+ if (NULL == *httpOption)
+ {
+ OIC_LOG(ERROR, TAG, "Memory allocation failed");
+ return OC_STACK_NO_MEMORY;
+ }
+
+ switch (option->optionID)
+ {
+ case COAP_OPTION_ACCEPT:
+ OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+ HTTP_OPTION_ACCEPT);
+ break;
+ case COAP_OPTION_IF_MATCH:
+ OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+ HTTP_OPTION_IF_MATCH);
+ break;
+ case COAP_OPTION_IF_NONE_MATCH:
+ OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+ HTTP_OPTION_IF_NONE_MATCH);
+ break;
+ case COAP_OPTION_ETAG:
+ OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+ HTTP_OPTION_ETAG);
+ break;
+ case COAP_OPTION_CONTENT_TYPE:
+ OICStrcpy((*httpOption)->optionName, sizeof((*httpOption)->optionName),
+ HTTP_OPTION_CONTENT_TYPE);
+ break;
+ default:
+ OIC_LOG_V(INFO, TAG, "No Matching found for the ID %d", option->optionID);
+ }
+
+ if ('\0' == (*httpOption)->optionName[0])
+ {
+ OIC_LOG(ERROR, TAG, "No matching is found");
+ OICFree(*httpOption);
+ return OC_STACK_INVALID_OPTION;
+ }
+
+ (*httpOption)->optionLength = option->optionLength < sizeof((*httpOption)->optionData) ?
+ option->optionLength : sizeof((*httpOption)->optionData);
+ memcpy((*httpOption)->optionData, option->optionData, (*httpOption)->optionLength);
+
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
+void CHPJsonToRepPayload(cJSON* rootJSon, OCRepPayload* payload)
+{
+ cJSON* dataJson = rootJSon->child;
+ while (dataJson)
+ {
+ switch (dataJson->type)
+ {
+ case cJSON_String:
+ OCRepPayloadSetPropString(payload, dataJson->string, dataJson->valuestring);
+ break;
+ case cJSON_Number:
+ if (dataJson->valueint == dataJson->valuedouble)
+ {
+ OCRepPayloadSetPropInt(payload, dataJson->string, dataJson->valueint);
+ }
+ else
+ {
+ OCRepPayloadSetPropDouble(payload, dataJson->string, dataJson->valuedouble);
+ }
+ break;
+ case cJSON_False:
+ OCRepPayloadSetPropBool(payload, dataJson->string, false);
+ break;
+ case cJSON_True:
+ OCRepPayloadSetPropBool(payload, dataJson->string, true);
+ break;
+ case cJSON_Object:
+ {
+ OCRepPayload* childPayload = OCRepPayloadCreate();
+ CHPJsonToRepPayload(dataJson,childPayload);
+ OCRepPayloadSetPropObject(payload, dataJson->string,childPayload );
+ break;
+ }
+ case cJSON_Array:
+ {
+ int size = cJSON_GetArraySize(dataJson);
+ size_t dimensions[MAX_REP_ARRAY_DEPTH];
+ dimensions[0] = size;
+ dimensions[1] = dimensions[2] = 0;
+
+ int i = 0;
+ int type = cJSON_IsReference;
+ int numType = 0; // int:1, double:2
+ const int intType = 1;
+ const int doubleType = 2;
+
+ int64_t intArray[size];
+ double doubleArray[size];
+ char* strArray[size];
+ OCRepPayload* objPayloadArray[size];
+
+ for (; i < size ; ++i)
+ {
+ cJSON* subitem = cJSON_GetArrayItem(dataJson, i);
+ if (subitem == NULL)
+ {
+ continue;
+ }
+
+ if ((type != cJSON_IsReference) && (type != subitem->type))
+ {
+ continue;
+ }
+ else
+ {
+ type = subitem->type;
+ switch (type)
+ {
+ case cJSON_Number:
+ if (subitem->valueint == subitem->valuedouble)
+ {
+ numType = intType;
+ intArray[i] = (int64_t) subitem->valueint;
+ }
+ else
+ {
+ numType = doubleType;
+ doubleArray[i] = subitem->valuedouble;
+ }
+ break;
+ case cJSON_String:
+ strArray[i] = subitem->valuestring;
+ break;
+ case cJSON_Object:
+ objPayloadArray[i] = OCRepPayloadCreate();
+ CHPJsonToRepPayload(subitem,objPayloadArray[i]);
+ break;
+ default:
+ OIC_LOG(ERROR, TAG, "wrong ArrayType in JsonToRepPayload()");
+ break;
+ }
+ }
+ }
+
+ switch (type)
+ {
+ case cJSON_Number:
+ if (numType == intType)
+ {
+ OCRepPayloadSetIntArray(payload, dataJson->string,(const int64_t*)intArray,
+ dimensions);
+ }
+ else if (numType == doubleType)
+ {
+ OCRepPayloadSetDoubleArray(payload, dataJson->string,
+ (const double*)doubleArray,
+ dimensions);
+ }
+ break;
+ case cJSON_String:
+ OCRepPayloadSetStringArray(payload, dataJson->string,
+ (const char**)strArray,
+ dimensions);
+ break;
+ case cJSON_Object:
+ OCRepPayloadSetPropObjectArray(payload, dataJson->string,
+ (const OCRepPayload**)objPayloadArray,
+ dimensions);
+ break;
+ default:
+ OIC_LOG(ERROR, TAG, "wrong ArrayType in JsonToRepPayload()");
+ break;
+ }
+ break;
+ }
+ }
+ dataJson = dataJson->next;
+ }
+}
+
+cJSON* CHPRepPayloadToJson(OCRepPayload* repData)
+{
+ cJSON *outJson = cJSON_CreateObject();
+ if (outJson == NULL)
+ {
+ return NULL;
+ }
+
+ OCRepPayloadValue* val = repData->values;
+ while (val)
+ {
+ switch (val->type)
+ {
+ case OCREP_PROP_NULL:
+ break;
+ case OCREP_PROP_INT:
+ OIC_LOG_V(DEBUG, TAG, "%s(int):%d", val->name, (int)val->i);
+ cJSON_AddNumberToObject(outJson,val->name,(int)val->i);
+ break;
+ case OCREP_PROP_DOUBLE:
+ OIC_LOG_V(DEBUG, TAG, "%s(double):%f", val->name, val->d);
+ cJSON_AddNumberToObject(outJson,val->name,val->d);
+ break;
+ case OCREP_PROP_BOOL:
+ OIC_LOG_V(DEBUG, TAG, "%s(bool):%s", val->name, val->b ? "true" : "false");
+ cJSON_AddBoolToObject(outJson,val->name,val->b);
+ break;
+ case OCREP_PROP_STRING:
+ OIC_LOG_V(DEBUG, TAG, "%s(string):%s", val->name, val->str);
+ cJSON_AddStringToObject(outJson,val->name,val->str);
+ break;
+ case OCREP_PROP_OBJECT:
+ {
+ cJSON *objJson = CHPRepPayloadToJson(val->obj);
+ if (objJson != NULL)
+ {
+ cJSON_AddItemToObject(outJson,val->name,objJson);
+ }
+ break;
+ }
+ case OCREP_PROP_ARRAY:
+ {
+ unsigned int i = 0;
+ int arraySize = (int)val->arr.dimensions[0];
+ switch (val->arr.type)
+ {
+ case OCREP_PROP_INT:
+ OIC_LOG_V(DEBUG, TAG, "%s(int array)", val->name);
+ if (arraySize > 0)
+ {
+ int castVal[val->arr.dimensions[0]];
+ for (i = 0 ; i < (unsigned int)arraySize ; i++)
+ {
+ castVal[i] = (int)val->arr.iArray[i];
+ }
+ cJSON *array = cJSON_CreateIntArray(castVal,arraySize);
+ if (array != NULL)
+ {
+ cJSON_AddItemToObject(outJson,val->name,array);
+ }
+ }
+ break;
+ case OCREP_PROP_DOUBLE:
+ OIC_LOG_V(DEBUG, TAG, "%s(double array)", val->name);
+ if (arraySize > 0)
+ {
+ cJSON *array = cJSON_CreateDoubleArray(val->arr.dArray,arraySize);
+ if (array != NULL)
+ {
+ cJSON_AddItemToObject(outJson,val->name,array);
+ }
+ }
+ break;
+ case OCREP_PROP_STRING:
+ OIC_LOG_V(DEBUG, TAG, "%s(string array)", val->name);
+ if (arraySize > 0)
+ {
+ cJSON *array = cJSON_CreateStringArray((const char**)val->arr.strArray,
+ arraySize);
+ if (array != NULL)
+ {
+ cJSON_AddItemToObject(outJson,val->name,array);
+ }
+ }
+ break;
+ case OCREP_PROP_OBJECT:
+ if (arraySize > 0)
+ {
+ cJSON *arrayJson = cJSON_CreateArray();
+ for (i = 0 ; i < (unsigned int)arraySize ; i++)
+ {
+ cJSON *objJson = CHPRepPayloadToJson(val->arr.objArray[i]);
+ if (objJson != NULL && arrayJson != NULL)
+ {
+ cJSON_AddItemToArray(arrayJson, objJson);
+ }
+ }
+ if (arrayJson != NULL)
+ {
+ cJSON_AddItemToObject(outJson,val->name,arrayJson);
+ }
+ }
+ break;
+ case OCREP_PROP_BOOL:
+ //TODO : Not support - cJSON_CreateBoolArray
+ break;
+ default:
+ OIC_LOG_V(ERROR, TAG, "Unknown/unsupported array type: %s", val->name);
+ break;
+ }
+ break;
+ }
+ default:
+ OIC_LOG_V(ERROR, TAG, "Unknown type: %s", val->name);
+ break;
+ }
+ val = val->next;
+ }
+
+ if( repData->values != NULL)
+ {
+ return outJson;
+ }
+ else
+ {
+ cJSON_Delete(outJson);
+ return NULL;
+ }
+}
--- /dev/null
+/* ****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "CoapHttpParser.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "uarraylist.h"
+#include "logger.h"
+
+#include <string.h>
+#include <curl/curl.h>
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif //!defined(_MSC_VER)
+#include <sys/types.h>
+#include <fcntl.h>
+#if !defined(_WIN32)
+#include <sys/select.h>
+#endif //!defined(_WIN32)
+#include <errno.h>
+
+#define TAG "CHP_PARSER"
+
+#define DEFAULT_USER_AGENT "IoTivity"
+#define MAX_PAYLOAD_SIZE (1048576U) // 1 MB
+
+typedef struct
+{
+ void* context;
+ CHPResponseCallback cb;
+ HttpResponse_t resp;
+ /* libcurl will not cache request payload when creating a easy handle hence we need to cache */
+ void* payload;
+ size_t payloadLength;
+ /* To track multiple read_callbacks from curl */
+ size_t readOffset;
+ /* To track multiple write_callbacks from curl */
+ size_t writeOffset;
+ /* libcurl related */
+ CURL* easyHandle;
+ /* libcurl does not copy header options passed to a request */
+ struct curl_slist *list;
+} CHPContext_t;
+
+/* A curl mutihandle is not threadsafe so we require mutexes to add new easy
+ * handles to multihandle.
+ */
+static CURLM *g_multiHandle;
+static int g_activeConnections;
+
+/* Mutex code is taken from CA.
+ * General utility functions shall be placed in common location
+ * so that all modules can use them.
+ */
+static pthread_mutex_t g_multiHandleMutex;
+
+/* Fds used to signal threads to stop */
+static int g_shutdownFds[2];
+
+static bool g_terminateParser;
+
+/*
+ * Fds used to signal fdset to be refreshed.
+ * When a new easy_handle is added to multi_handle,
+ * existing fd_set has to be updated.
+ */
+static int g_refreshFds[2];
+
+/*
+ * Thread handle for curl multi_handle processing.
+ */
+static pthread_t g_multiHandleThread;
+
+static void CHPParserLockMutex();
+static void CHPParserUnlockMutex();
+
+static void CHPParserResetHeaderOptions(u_arraylist_t** headerOptions)
+{
+ VERIFY_NON_NULL_VOID(headerOptions, TAG, "headerOptions is NULL");
+
+ HttpHeaderOption_t *option = NULL;
+ while (NULL != (option = u_arraylist_remove(*headerOptions, 0)))
+ {
+ OICFree(option);
+ }
+ u_arraylist_free(headerOptions);
+}
+
+static void CHPFreeContext(CHPContext_t *ctxt)
+{
+ VERIFY_NON_NULL_VOID(ctxt, TAG, "ctxt is NULL");
+ if(ctxt->list)
+ {
+ curl_slist_free_all(ctxt->list);
+ }
+
+ if(ctxt->easyHandle)
+ {
+ curl_easy_cleanup(ctxt->easyHandle);
+ }
+
+ CHPParserResetHeaderOptions(&(ctxt->resp.headerOptions));
+ OICFree(ctxt->resp.payload);
+ OICFree(ctxt->payload);
+ OICFree(ctxt);
+}
+
+static void *CHPParserExecuteMultiHandle(void* data)
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ UNUSED(data);
+ /*
+ * These fd sets will be fetched from curl multi handle and monitored to execute
+ * curl_multi_perform()
+ */
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexcep;
+ int maxfd;
+ struct timeval timeout;
+ struct timeval *tv;
+ int activeCon;
+ int activeEasyHandle;
+ bool goForSelect;
+ int retValue;
+
+ /* When active connections exist, curl_multi_perform() shall be called within
+ * curlMultiTimeout seconds regardless of whether select returned successfull or not.
+ */
+ long curlMultiTimeout;
+ while (!g_terminateParser)
+ {
+ // Required everytime before calling curl_multi_fdset()
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+ FD_ZERO(&fdexcep);
+ maxfd = -1;
+ goForSelect = true;
+
+ // Get currently active transfer fds from curl
+ CHPParserLockMutex();
+ curl_multi_fdset(g_multiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
+ curl_multi_timeout(g_multiHandle, &curlMultiTimeout);
+ activeCon = g_activeConnections;
+ CHPParserUnlockMutex();
+
+ // A 0 represent curl_multi_perform() shall be called.
+ if(curlMultiTimeout < 0)
+ {
+ curlMultiTimeout = 1000;
+ }
+
+ if(maxfd == -1)
+ {
+ /* Nothing to monitor from libcurl.
+ * This mean that no active sockets exist either because
+ * there are no transfer taking place or sockets are not in a
+ * state that could be monitored (connecting, retry etc.)
+ */
+ if(!activeCon)
+ {
+ // wait until something received on shutdown or refresh fd
+ // with no timeout.
+ curlMultiTimeout = -1;
+ }
+ else
+ {
+ // libcurl recommend doing this.
+ usleep(100000);
+ // dont select() and directly call curl_multi_perform()
+ goForSelect = false;
+ }
+ }
+
+ if(goForSelect)
+ {
+ FD_SET(g_shutdownFds[0], &fdread);
+ if (maxfd < g_shutdownFds[0])
+ {
+ maxfd = g_shutdownFds[0];
+ }
+
+ FD_SET(g_refreshFds[0], &fdread);
+ if (maxfd < g_refreshFds[0])
+ {
+ maxfd = g_refreshFds[0];
+ }
+
+ if(curlMultiTimeout == -1)
+ {
+ tv = NULL;
+ }
+ else
+ {
+ timeout.tv_sec = curlMultiTimeout / 1000;
+ timeout.tv_usec = 0;
+ tv = &timeout;
+ }
+
+ // Select here
+ retValue = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, tv);
+ if(retValue == -1)
+ {
+ OIC_LOG_V(ERROR, TAG, "Error in select. %s", strerror(errno));
+ continue;
+ }
+
+ // Some sockets are available for operations, check if shutdown or refresh fds are
+ // among them. In any case, go ahead and call curl_multi_perform()
+ if(retValue)
+ {
+ if (FD_ISSET(g_shutdownFds[0], &fdread))
+ {
+ OIC_LOG(ERROR, TAG, "Shutdown requested. multi_handle returning");
+ break;
+ }
+ else if(FD_ISSET(g_refreshFds[0], &fdread))
+ {
+ char buf[20] = {0};
+ ssize_t len = read(g_refreshFds[0], buf, sizeof(buf));
+ UNUSED(len);
+ // new easy handles added, call multi_perform and refresh fds.
+ OIC_LOG(ERROR, TAG, "New easy handle added");
+ }
+ }
+ }
+
+ CURLMcode ret;
+ CHPParserLockMutex();
+ do
+ {
+ ret = curl_multi_perform(g_multiHandle, &activeEasyHandle);
+ struct CURLMsg *cmsg;
+ int cmsgq;
+ do
+ {
+ cmsgq = 0;
+ cmsg = curl_multi_info_read(g_multiHandle, &cmsgq);
+ if(cmsg && (cmsg->msg == CURLMSG_DONE))
+ {
+ CURL *easyHandle = cmsg->easy_handle;
+ g_activeConnections--;
+ curl_multi_remove_handle(g_multiHandle, easyHandle);
+
+ CHPContext_t *ptr;
+ char *uri = NULL;
+ char *contentType = NULL;
+ long responseCode;
+
+ curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &ptr);
+ curl_easy_getinfo(easyHandle, CURLINFO_EFFECTIVE_URL, &uri);
+ curl_easy_getinfo(easyHandle, CURLINFO_RESPONSE_CODE, &responseCode);
+ curl_easy_getinfo(easyHandle, CURLINFO_CONTENT_TYPE, &contentType);
+
+ ptr->resp.status = responseCode;
+ OICStrcpy(ptr->resp.dataFormat, sizeof(ptr->resp.dataFormat), contentType);
+ OIC_LOG_V(DEBUG, TAG, "Transfer completed %d uri: %s, %s", g_activeConnections,
+ uri, contentType);
+ ptr->cb(&(ptr->resp), ptr->context);
+ CHPFreeContext(ptr);
+ }
+ } while(cmsg && !g_terminateParser);
+ }while (ret == CURLM_CALL_MULTI_PERFORM && !g_terminateParser);
+ CHPParserUnlockMutex();
+ }
+
+ if (g_terminateParser)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Shutdown request received.");
+ // g_shutdownFds[1] will be already closed.
+ close(g_shutdownFds[0]);
+ close(g_refreshFds[0]);
+ close(g_refreshFds[1]);
+ g_shutdownFds[0] = -1;
+ g_shutdownFds[1] = -1;
+ g_refreshFds[0] = -1;
+ g_refreshFds[1] = -1;
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ return NULL;
+}
+
+OCStackResult CHPParserInitializePipe(int fds[2])
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ int ret = -1;
+ fds[0] = -1;
+ fds[1] = -1;
+#if defined(HAVE_PIPE2)
+ ret = pipe2(fds, O_CLOEXEC);
+#else
+ ret = pipe(fds);
+ if (-1 != ret)
+ {
+ ret = fcntl(fds[0], F_GETFD);
+ if (-1 != ret)
+ {
+ ret = fcntl(fds[0], F_SETFD, ret|FD_CLOEXEC);
+ }
+ if (-1 != ret)
+ {
+ ret = fcntl(fds[1], F_GETFD);
+ }
+ if (-1 != ret)
+ {
+ ret = fcntl(fds[1], F_SETFD, ret|FD_CLOEXEC);
+ }
+ if (-1 == ret)
+ {
+ close(fds[1]);
+ close(fds[0]);
+ fds[0] = -1;
+ fds[1] = -1;
+ }
+ }
+#endif
+ if (-1 == ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "FD initialization failed: %s", strerror(errno));
+ return OC_STACK_ERROR;
+ }
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
+static OCStackResult CHPParserInitializeMutex()
+{
+ // create the mutex with the attributes set
+ int ret = pthread_mutex_init(&g_multiHandleMutex, PTHREAD_MUTEX_DEFAULT);
+ if (0 != ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s Failed to initialize mutex !", __func__);
+ return OC_STACK_ERROR;
+ }
+ return OC_STACK_OK;
+}
+
+static OCStackResult CHPParserTerminateMutex()
+{
+ int ret = pthread_mutex_destroy(&g_multiHandleMutex);
+ if (0 != ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s Failed to free mutex !", __func__);
+ return OC_STACK_ERROR;
+ }
+ return OC_STACK_OK;
+}
+
+static void CHPParserLockMutex()
+{
+ int ret = pthread_mutex_lock(&g_multiHandleMutex);
+ if(ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Pthread Mutex lock failed: %d", ret);
+ }
+}
+
+static void CHPParserUnlockMutex()
+{
+ int ret = pthread_mutex_unlock(&g_multiHandleMutex);
+ if(ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Pthread Mutex unlock failed: %d", ret);
+ }
+}
+
+static OCStackResult CHPParserInitializeMultiHandle()
+{
+ CHPParserLockMutex();
+ if(g_multiHandle)
+ {
+ OIC_LOG(ERROR, TAG, "Multi handle already initialized.");
+ CHPParserUnlockMutex();
+ return OC_STACK_OK;
+ }
+
+ g_multiHandle = curl_multi_init();
+ if(!g_multiHandle)
+ {
+ OIC_LOG(ERROR, TAG, "Failed to create multi handle.");
+ CHPParserUnlockMutex();
+ return OC_STACK_ERROR;
+ }
+
+ CHPParserUnlockMutex();
+ return OC_STACK_OK;
+}
+
+static OCStackResult CHPParserTerminateMultiHandle()
+{
+ CHPParserLockMutex();
+ if(!g_multiHandle)
+ {
+ OIC_LOG(ERROR, TAG, "Multi handle not initialized.");
+ CHPParserUnlockMutex();
+ return OC_STACK_OK;
+ }
+
+ curl_multi_cleanup(g_multiHandle);
+ g_multiHandle = NULL;
+ CHPParserUnlockMutex();
+ return OC_STACK_OK;
+}
+
+OCStackResult CHPParserInitialize()
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+
+ OCStackResult ret = CHPParserInitializeMutex();
+ if(ret != OC_STACK_OK)
+ {
+ return ret;
+ }
+
+ ret = CHPParserInitializeMultiHandle();
+ if(ret != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to intialize multi handle: %d", ret);
+ CHPParserTerminate();
+ return ret;
+ }
+
+ ret = CHPParserInitializePipe(g_shutdownFds);
+ if(ret != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to intialize shutdown fds: %d", ret);
+ CHPParserTerminate();
+ return ret;
+ }
+
+ ret = CHPParserInitializePipe(g_refreshFds);
+ if(ret != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to intialize refresh fds: %d", ret);
+ CHPParserTerminate();
+ return ret;
+ }
+
+ // Launch multi_handle processor thread
+ int result = pthread_create(&g_multiHandleThread, NULL, CHPParserExecuteMultiHandle, NULL);
+ if(result != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Thread start failed with error %d", result);
+ CHPParserTerminate();
+ return OC_STACK_ERROR;
+ }
+
+ g_terminateParser = false;
+ CHPParserLockMutex();
+ g_activeConnections = 0;
+ CHPParserUnlockMutex();
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
+OCStackResult CHPParserTerminate()
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ g_terminateParser = true;
+ if (g_shutdownFds[1] != -1)
+ {
+ // Signal multi_handle thread to come out
+ close(g_shutdownFds[1]);
+ }
+ pthread_join(g_multiHandleThread, NULL);
+
+ OCStackResult ret = CHPParserTerminateMultiHandle();
+ if(ret != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Multi handle termination failed: %d", ret);
+ }
+
+ CHPParserLockMutex();
+ g_activeConnections = 0;
+ CHPParserUnlockMutex();
+
+ ret = CHPParserTerminateMutex();
+ if(ret != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "mutex termination failed: %d", ret);
+ }
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
+static size_t CHPEasyHandleWriteCb(char *buffer, size_t size, size_t num, void *context)
+{
+ size_t dataToWrite = size * num;
+ if(!dataToWrite)
+ {
+ // Empty payload received. Ignore.
+ return 0;
+ }
+
+ if(!context || !buffer || g_terminateParser)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
+ return 0;
+ }
+
+ CHPContext_t* ctx = context;
+ HttpResponse_t *resp = &(ctx->resp);
+
+ if(ctx->writeOffset + dataToWrite > MAX_PAYLOAD_SIZE)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s Payload limit exceeded", __func__);
+ resp->payloadLength = 0;
+ ctx->writeOffset = 0;
+ OICFree(resp->payload);
+ resp->payload = NULL;
+ return 0;
+ }
+
+ if (!resp->payload)
+ {
+ resp->payload = OICMalloc(dataToWrite);
+ if (!resp->payload)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s Out of memory!", __func__);
+ return 0;
+ }
+ }
+ else
+ {
+ // Realloc buffer
+ void *newPayload = OICRealloc(resp->payload, ctx->writeOffset + dataToWrite);
+ if (!newPayload)
+ {
+ OIC_LOG_V(ERROR, TAG, "Realloc failed! Current: %u Extra: %u", ctx->writeOffset,
+ dataToWrite);
+ resp->payloadLength = 0;
+ ctx->writeOffset = 0;
+ OICFree(resp->payload);
+ resp->payload = NULL;
+ return 0;
+ }
+ resp->payload = newPayload;
+ }
+
+ memcpy(resp->payload + ctx->writeOffset, buffer, dataToWrite);
+ ctx->writeOffset += dataToWrite;
+ resp->payloadLength = ctx->writeOffset;
+
+ OIC_LOG_V(DEBUG, TAG, "%s OUT %u : %u", __func__, resp->payloadLength, dataToWrite);
+ return dataToWrite;
+}
+
+static size_t CHPEasyHandleReadCb(char *buffer, size_t size, size_t num, void *context)
+{
+ if(!context || !buffer || g_terminateParser)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
+ return CURL_READFUNC_ABORT;
+ }
+
+ CHPContext_t *ctx = context;
+ size_t remainingLen = ctx->payloadLength - ctx->readOffset;
+ size_t toTransfer = size * num > remainingLen ? remainingLen : size * num;
+ memcpy(buffer, ctx->payload + ctx->readOffset, toTransfer);
+ ctx->readOffset += toTransfer;
+ return toTransfer;
+}
+
+static size_t CHPEasyHandleHeaderCb(char *buffer, size_t size, size_t num, void *context)
+{
+ size_t dataToWrite = size * num;
+ if(!buffer || !dataToWrite || !context || g_terminateParser)
+ {
+ OIC_LOG_V(ERROR, TAG, "%s invalid arguments or terminating", __func__);
+ return 0;
+ }
+
+ /* curl will call this function for each line in response header including status line
+ * and for each http response that it might have received from http server for ex: redirect,
+ * proxy handshakes etc. All these intermediary responses are not useful for us but there
+ * isn't any mechanism to track which one is going to be final.
+ * Hence here we process each response and assume that the relevant one will be the final
+ * response.
+ */
+
+ /* Start of a response is tracked by presence of status line starting with "HTTP/"
+ * This also acts as a reset for everything else (payload, header options) as we are processing
+ * a new response.
+ */
+
+ CHPContext_t *ctx = context;
+ HttpResponse_t *resp = &(ctx->resp);
+ if (dataToWrite > 5)
+ {
+ if (strncmp("HTTP/", buffer, 5) == 0)
+ {
+ OIC_LOG(ERROR, TAG, "New header received");
+ resp->payloadLength = 0;
+ ctx->writeOffset = 0;
+ OICFree(resp->payload);
+ resp->payload = NULL;
+ CHPParserResetHeaderOptions(&(resp->headerOptions));
+ // This is a status line. We are only interested in header options.
+ return dataToWrite;
+ }
+ }
+
+
+ // A header line can have CR LF NULL and spaces at end. Make endOfHeader point to last
+ // character in header value
+ char* endOfHeader = buffer + dataToWrite;
+ while ((endOfHeader > buffer) && (*endOfHeader == '\r' || *endOfHeader == '\n'
+ || *endOfHeader == ' ' || *endOfHeader == '\0'))
+ {
+ endOfHeader--;
+ }
+
+ /* curl might not send the buffer NULL terminated and copying each header is too much overhead
+ * hence use mem family of function to search */
+ char* ptr = (char*) memchr(buffer, ':', dataToWrite);
+ // There is a colon and its not the first character
+ if(ptr && ptr != buffer && ptr <= endOfHeader)
+ {
+ size_t headerFieldLen = ptr - buffer;
+ size_t headerValueLen;
+ char* headerValuePtr;
+
+ /* Skip any white spaces */
+ ptr++;
+ while(ptr <= endOfHeader && *ptr == ' ')
+ {
+ ptr++;
+ }
+
+ if(ptr > endOfHeader)
+ {
+ headerValueLen = 0;
+ headerValuePtr = NULL;
+ }
+ else
+ {
+ // endOfHeader is pointing to last header value character hence +1
+ headerValueLen = endOfHeader - ptr + 1;
+ headerValuePtr = ptr;
+ }
+
+ if (!(resp->headerOptions))
+ {
+ // First header callback, assign storage for header options
+ resp->headerOptions = u_arraylist_create();
+ if (!(resp->headerOptions))
+ {
+ OIC_LOG(ERROR, TAG, "Memory failed!");
+ return 0;
+ }
+ }
+
+ HttpHeaderOption_t *option = OICCalloc(1, sizeof(HttpHeaderOption_t));
+ if (!option)
+ {
+ OIC_LOG(ERROR, TAG, "Memory failed!");
+ return 0;
+ }
+
+ headerFieldLen = headerFieldLen > (sizeof(option->optionName) - 1) ?
+ (sizeof(option->optionName) - 1): headerFieldLen;
+ memcpy(option->optionName, buffer, headerFieldLen);
+ option->optionName[headerFieldLen] = '\0';
+
+ if(headerValueLen)
+ {
+ headerValueLen = headerValueLen > (sizeof(option->optionData) - 1) ?
+ (sizeof(option->optionData) - 1): headerValueLen;
+ memcpy(option->optionData, headerValuePtr, headerValueLen);
+ option->optionData[headerValueLen] = '\0';
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "%s:: %s: %s", __func__, option->optionName, option->optionData);
+ // Add to header option list
+ if(!u_arraylist_add(resp->headerOptions, option))
+ {
+ OIC_LOG(ERROR, TAG, "u_arraylist_add failed!");
+ OICFree(option);
+ return 0;
+ }
+ }
+
+ // ignore else as this might be CRLF header lines.
+ return dataToWrite;
+}
+
+static OCStackResult CHPInitializeEasyHandle(CURL** easyHandle, HttpRequest_t *req,
+ CHPContext_t* handleContext)
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
+ VERIFY_NON_NULL_RET(easyHandle, TAG, "easyHandle", OC_STACK_INVALID_PARAM);
+ VERIFY_NON_NULL_RET(handleContext, TAG, "handleContext", OC_STACK_INVALID_PARAM);
+
+ CURL *e = curl_easy_init();
+ if(!e)
+ {
+ OIC_LOG(ERROR, TAG, "easy init failed!");
+ return OC_STACK_ERROR;
+ }
+
+ /* Set http resource uri */
+ curl_easy_setopt(e, CURLOPT_URL, req->resourceUri);
+ /* Default protocol when scheme is not available in uri */
+ // curl version 7.22 don't support this option.
+ //curl_easy_setopt(e, CURLOPT_DEFAULT_PROTOCOL, "http");
+ /* Set handle context */
+ curl_easy_setopt(e, CURLOPT_PRIVATE, handleContext);
+ curl_easy_setopt(e, CURLOPT_WRITEFUNCTION, CHPEasyHandleWriteCb);
+ curl_easy_setopt(e, CURLOPT_WRITEDATA, handleContext);
+ curl_easy_setopt(e, CURLOPT_READFUNCTION, CHPEasyHandleReadCb);
+ curl_easy_setopt(e, CURLOPT_READDATA, handleContext);
+ curl_easy_setopt(e, CURLOPT_HEADERFUNCTION, CHPEasyHandleHeaderCb);
+ curl_easy_setopt(e, CURLOPT_HEADERDATA, handleContext);
+
+ /* Allow access to only http server's */
+ curl_easy_setopt(e, CURLOPT_PROTOCOLS,
+ CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ /* complete connection within 15 seconds */
+ curl_easy_setopt(e, CURLOPT_CONNECTTIMEOUT, 15L);
+ /* Abort transaction if getting less than 1kbps for 60 seconds */
+ curl_easy_setopt(e, CURLOPT_LOW_SPEED_LIMIT, 1024L);
+ curl_easy_setopt(e, CURLOPT_LOW_SPEED_TIME, 60L);
+ curl_easy_setopt(e, CURLOPT_USERAGENT, DEFAULT_USER_AGENT);
+ /* Close connection once done with transaction */
+ curl_easy_setopt(e, CURLOPT_FORBID_REUSE, 1L);
+ /* Allow redirect */
+ curl_easy_setopt(e, CURLOPT_FOLLOWLOCATION, 1L);
+ /* Only redirect to http servers */
+ curl_easy_setopt(e, CURLOPT_REDIR_PROTOCOLS,
+ CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ /* Limit maximum redirects */
+ curl_easy_setopt(e, CURLOPT_MAXREDIRS, 10L);
+
+ handleContext->writeOffset = 0;
+ handleContext->readOffset = 0;
+ switch(req->method)
+ {
+ case CHP_GET:
+ OIC_LOG(DEBUG, TAG, "Sending GET request");
+ curl_easy_setopt(e, CURLOPT_HTTPGET, 1);
+ break;
+ case CHP_POST:
+ OIC_LOG(DEBUG, TAG, "Sending POST request");
+ curl_easy_setopt(e, CURLOPT_POST, 1);
+ curl_easy_setopt(e, CURLOPT_POSTFIELDS, NULL);
+ curl_easy_setopt(e, CURLOPT_POSTFIELDSIZE, req->payloadLength);
+ handleContext->payloadLength = req->payloadLength;
+ handleContext->payload = req->payload;
+ req->payloadCached = true;
+ break;
+ case CHP_PUT:
+ OIC_LOG(DEBUG, TAG, "Sending PUT request");
+ curl_easy_setopt(e, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(e, CURLOPT_INFILESIZE, req->payloadLength);
+ handleContext->payloadLength = req->payloadLength;
+ handleContext->payload = req->payload;
+ req->payloadCached = true;
+ break;;
+ case CHP_DELETE:
+ OIC_LOG(DEBUG, TAG, "Sending DELETE request");
+ /* libcurl don't have direct option for sending DELETE */
+ curl_easy_setopt(e, CURLOPT_CUSTOMREQUEST, "DELETE");
+ break;
+ default:
+ return OC_STACK_INVALID_METHOD;
+ }
+
+ // Add header options from request
+ struct curl_slist *list = NULL;
+ char buffer[CHP_MAX_HF_NAME_LENGTH + CHP_MAX_HF_DATA_LENGTH + 2]; // extra 2 bytes for ": "
+
+ if (req->headerOptions)
+ {
+ HttpHeaderOption_t *option = NULL;
+ int headerCount = u_arraylist_length(req->headerOptions);
+ for(int i = 0; i < headerCount; i++)
+ {
+ option = u_arraylist_get(req->headerOptions, i);
+ if(option)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Adding header option: %s", buffer);
+ snprintf(buffer, sizeof(buffer), "%s: %s", option->optionName, option->optionData);
+ list = curl_slist_append(list, buffer);
+ }
+ }
+ }
+
+ /* Add content-type and accept header */
+ snprintf(buffer, sizeof(buffer), "Accept: %s", req->acceptFormat);
+ list = curl_slist_append(list, buffer);
+ snprintf(buffer, sizeof(buffer), "Content-Type: %s", req->payloadFormat);
+ curl_easy_setopt(e, CURLOPT_HTTPHEADER, list);
+
+ *easyHandle = e;
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
+OCStackResult CHPPostHttpRequest(HttpRequest_t *req, CHPResponseCallback httpcb,
+ void *context)
+{
+ OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
+ VERIFY_NON_NULL_RET(req, TAG, "req", OC_STACK_INVALID_PARAM);
+ VERIFY_NON_NULL_RET(httpcb, TAG, "httpcb", OC_STACK_INVALID_PARAM);
+
+ CHPContext_t *ctxt = OICCalloc(1, sizeof(CHPContext_t));
+ if (!ctxt)
+ {
+ OIC_LOG(ERROR, TAG, "Memory failed!");
+ return OC_STACK_NO_MEMORY;
+ }
+
+ ctxt->cb = httpcb;
+ ctxt->context = context;
+ OCStackResult ret = CHPInitializeEasyHandle(&ctxt->easyHandle, req, ctxt);
+ if(ret != OC_STACK_OK)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to initialize easy handle [%d]", ret);
+ OICFree(ctxt);
+ return ret;
+ }
+
+ // Add easy_handle to multi_handle
+ CHPParserLockMutex();
+ curl_multi_add_handle(g_multiHandle, ctxt->easyHandle);
+ g_activeConnections++;
+ CHPParserUnlockMutex();
+ // Notify refreshfd
+ ssize_t len = 0;
+ do
+ {
+ len = write(g_refreshFds[1], "w", 1);
+ } while ((len == -1) && (errno == EINTR));
+
+ if ((len == -1) && (errno != EINTR) && (errno != EPIPE))
+ {
+ OIC_LOG_V(DEBUG, TAG, "refresh failed: %s", strerror(errno));
+ }
+
+ OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
+ return OC_STACK_OK;
+}
+
%{!?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}
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}
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}