replace : iotivity -> iotivity-sec
[platform/upstream/iotivity.git] / service / coap-http-proxy / src / CoapHttpHandler.c
1 /* ****************************************************************
2  *
3  * Copyright 2016 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************/
20
21 #include "CoapHttpHandler.h"
22 #include "oic_malloc.h"
23 #include "oic_string.h"
24 #include "logger.h"
25 #include <coap/pdu.h>
26 #include "ocpayload.h"
27 #include "uarraylist.h"
28 #include "CoapHttpParser.h"
29 #include "CoapHttpMap.h"
30 #if defined (__TIZENRT__)
31 #include <apps/netutils/cJSON.h>
32 #else
33 #include "cJSON.h"
34 #endif
35
36 #define TAG "CHPHandler"
37
38 #define CHP_RESOURCE_TYPE_NAME "core.chp"
39 #define CHP_RESOURCE_INTF_NAME "oc.mi.def"
40
41 /**
42  * CoAP request tracking.
43  * This structure is used to hold CoAP request information
44  */
45 typedef struct
46 {
47     OCMethod method;
48     OCRequestHandle requestHandle;
49 } CHPRequest_t;
50
51 /**
52  * Pointer to handle of the newly created proxy resource.
53  */
54 static OCResourceHandle g_proxyHandle = NULL;
55 static int g_isCHProxyInitialized = false;
56
57 /**
58  * Function to hand over CoAP request handling to Proxy.
59  */
60 OCStackResult CHPHandleOCFRequest(const OCEntityHandlerRequest* requestInfo,
61                                    const char* proxyUri);
62
63 /**
64  * Entity handler to receive requests from csdk.
65  */
66 OCEntityHandlerResult CHPEntityHandler(OCEntityHandlerFlag flag,
67                                        OCEntityHandlerRequest* entityHandlerRequest,
68                                        void* callbackParam);
69 bool CHPIsInitialized()
70 {
71     return g_isCHProxyInitialized;
72 }
73
74 OCStackResult CHPInitialize(bool secure)
75 {
76     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
77     if (g_isCHProxyInitialized)
78     {
79         OIC_LOG(DEBUG, TAG, "CH Proxy already initialized");
80         return OC_STACK_OK;
81     }
82
83     OCStackResult result = CHPParserInitialize();
84     if (OC_STACK_OK != result)
85     {
86         OIC_LOG_V(ERROR, TAG, "Parser initialization failed[%d]", result);
87         return result;
88     }
89
90     result = OCSetProxyURI(OC_RSRVD_PROXY_URI);
91     if (OC_STACK_OK != result)
92     {
93         OIC_LOG_V(ERROR, TAG, "Setting proxy uri failed[%d]", result);
94         CHPParserTerminate();
95         return result;
96     }
97
98     uint8_t prop = OC_ACTIVE | OC_DISCOVERABLE | OC_SLOW;
99     if(secure)
100     {
101         prop |= OC_SECURE;
102     }
103     result = OCCreateResource(&g_proxyHandle,
104                                CHP_RESOURCE_TYPE_NAME,
105                                CHP_RESOURCE_INTF_NAME,
106                                OC_RSRVD_PROXY_URI,
107                                CHPEntityHandler,
108                                NULL,
109                                prop);
110
111     if (OC_STACK_OK != result)
112     {
113         OIC_LOG_V(ERROR, TAG, "Create resource for proxy failed[%d]", result);
114         CHPParserTerminate();
115         return result;
116     }
117
118     g_isCHProxyInitialized = true;
119     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
120     return OC_STACK_OK;
121 }
122
123 OCStackResult CHPTerminate()
124 {
125     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
126     OCStackResult result = CHPParserTerminate();
127     if (OC_STACK_OK != result)
128     {
129         OIC_LOG_V(ERROR, TAG, "Parser termination failed[%d]", result);
130     }
131
132     result = OCDeleteResource(g_proxyHandle);
133     if (OC_STACK_OK != result)
134     {
135         OIC_LOG_V(ERROR, TAG, "Delete resource for proxy failed[%d]", result);
136     }
137
138     g_proxyHandle = NULL;
139     g_isCHProxyInitialized = false;
140     return result;
141     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
142 }
143
144 static void CHPGetProxyURI(OCHeaderOption* options, uint8_t *numOptions, char* uri,
145                            size_t uriLength)
146 {
147     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
148     if (!uri || uriLength <= 0)
149     {
150         OIC_LOG (INFO, TAG, "Invalid uri buffer");
151         return;
152     }
153
154     if (!options || !numOptions || 0 == *numOptions)
155     {
156         OIC_LOG (INFO, TAG, "No options present");
157         return;
158     }
159
160     for (int count = 0; count < *numOptions; count++)
161     {
162         if (options[count].protocolID == OC_COAP_ID &&
163                 options[count].optionID == COAP_OPTION_PROXY_URI)
164         {
165             OIC_LOG(DEBUG, TAG, "Proxy URI is present");
166             // Extract proxy-uri and delete it from headeroptions.
167             OICStrcpy(uri, uriLength, (char *)options[count].optionData);
168             for (int fwd = count; fwd < *numOptions-1; fwd++)
169             {
170                 options[count] = options[count+1];
171             }
172             *numOptions -= 1;
173             return;
174         }
175     }
176
177     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
178     return;
179 }
180
181 // TODO: Will be moved to OCProxyPayload
182 static OCRepPayload* CHPGetDiscoveryPayload()
183 {
184     OCRepPayload* payload = OCRepPayloadCreate();
185     if (!payload)
186     {
187         OIC_LOG(ERROR, TAG, PCF("Failed to create Payload"));
188         return NULL;
189     }
190
191     OCRepPayloadSetUri(payload, OC_RSRVD_PROXY_URI);
192     OCRepPayloadSetPropString(payload, OC_RSRVD_DEVICE_ID, OCGetServerInstanceIDString());
193     return payload;
194 }
195
196 OCEntityHandlerResult CHPEntityHandler(OCEntityHandlerFlag flag,
197                                        OCEntityHandlerRequest* entityHandlerRequest,
198                                        void* callbackParam)
199 {
200     OIC_LOG(INFO, TAG, "Proxy request received");
201     UNUSED(callbackParam);
202
203     if (!g_isCHProxyInitialized)
204     {
205         OIC_LOG (ERROR, TAG, "Proxy not initialized");
206         return OC_EH_INTERNAL_SERVER_ERROR;
207     }
208
209     if (!entityHandlerRequest)
210     {
211         OIC_LOG (ERROR, TAG, "Invalid request pointer");
212         return OC_EH_ERROR;
213     }
214
215     if (flag & OC_OBSERVE_FLAG)
216     {
217         OIC_LOG_V (ERROR, TAG, "Proxy is not observable");
218         return OC_EH_BAD_REQ;
219     }
220     else if (flag & OC_REQUEST_FLAG)
221     {
222         /*
223          *  A proxy can handle two type of requests:
224          *  1. Discovery request in which case proxy-uri option will not be present.
225          *  2. Request for HTTP resource with proxy-uri option.
226          */
227         char proxyUri[MAX_HEADER_OPTION_DATA_LENGTH] = {'\0'};
228         CHPGetProxyURI(entityHandlerRequest->rcvdVendorSpecificHeaderOptions,
229                        &(entityHandlerRequest->numRcvdVendorSpecificHeaderOptions),
230                        proxyUri, sizeof(proxyUri));
231
232         if (proxyUri[0] != '\0')
233         {
234             // A request for HTTP resource. Response will be sent asynchronously
235             if (OC_STACK_OK == CHPHandleOCFRequest(entityHandlerRequest,
236                                                    proxyUri) )
237             {
238                 return OC_EH_SLOW;
239             }
240         }
241         else
242         {
243             OCEntityHandlerResult ehResult = OC_EH_ERROR;
244             switch (entityHandlerRequest->method)
245             {
246                 case OC_REST_GET:
247                 case OC_REST_DISCOVER:
248                 {
249                     // Generate discovery payload
250                     OIC_LOG (INFO, TAG, "Discovery request from client");
251                     ehResult = OC_EH_OK;
252                     OCEntityHandlerResponse response =
253                                 { .requestHandle = entityHandlerRequest->requestHandle,
254                                   .resourceHandle = entityHandlerRequest->resource,
255                                   .ehResult = ehResult};
256
257                     response.payload = (OCPayload *)CHPGetDiscoveryPayload();
258                     // Indicate that response is NOT in a persistent buffer
259                     response.persistentBufferFlag = 0;
260
261                     // Send the response
262                     if (OCDoResponse(&response) != OC_STACK_OK)
263                     {
264                         OIC_LOG(ERROR, TAG, "Error sending response");
265                         ehResult = OC_EH_ERROR;
266                     }
267
268                     OCPayloadDestroy(response.payload);
269                     break;
270                 }
271                 default:
272                     // Other methods are not supported
273                     OIC_LOG (INFO, TAG, "Invalid method from client");
274                     ehResult = OC_EH_METHOD_NOT_ALLOWED;
275                     break;
276             }
277             return ehResult;
278         }
279     }
280
281     return OC_EH_ERROR;
282 }
283
284 void CHPHandleHttpResponse(const HttpResponse_t *httpResponse, void *context)
285 {
286     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
287     if (!httpResponse || !context)
288     {
289         OIC_LOG(ERROR, TAG, "Invalid arguements");
290         return;
291     }
292
293     CHPRequest_t *ctxt = (CHPRequest_t *)context;
294     OCEntityHandlerResponse response = { .requestHandle = ctxt->requestHandle,
295                                          .resourceHandle = g_proxyHandle};
296     response.persistentBufferFlag = 0;
297
298     OCStackResult result = CHPGetOCCode(httpResponse->status, ctxt->method,
299                                         &response.ehResult);
300     if (OC_STACK_OK != result)
301     {
302         OIC_LOG_V(ERROR, TAG, "%s failed[%d]", __func__, result);
303         response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
304         if (OCDoResponse(&response) != OC_STACK_OK)
305         {
306             OIC_LOG(ERROR, TAG, "Error sending response");
307         }
308         OICFree(ctxt);
309         return;
310     }
311
312     // ctxt not required now.
313     OICFree(ctxt);
314
315     if (httpResponse->dataFormat[0] != '\0')
316     {
317         OCPayloadFormat format = CHPGetOCContentType(httpResponse->dataFormat);
318         switch (format)
319         {
320             case OC_FORMAT_CBOR:
321                 OIC_LOG(DEBUG, TAG, "Payload format is CBOR");
322                 result = OCParsePayload(&response.payload, PAYLOAD_TYPE_REPRESENTATION,
323                                         httpResponse->payload, httpResponse->payloadLength);
324                 if (result != OC_STACK_OK)
325                 {
326                     OIC_LOG(ERROR, TAG, "Error parsing payload");
327                     response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
328                     if (OCDoResponse(&response) != OC_STACK_OK)
329                     {
330                         OIC_LOG(ERROR, TAG, "Error sending response");
331                     }
332                     return;
333                 }
334                 break;
335             case OC_FORMAT_JSON:
336                 OIC_LOG(DEBUG, TAG, "Payload format is JSON");
337                 cJSON *payloadJson = cJSON_Parse((char *)httpResponse->payload);
338                 if (!payloadJson)
339                 {
340                     OIC_LOG(ERROR, TAG, "Unable to parse json response");
341                     response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
342                     if (OCDoResponse(&response) != OC_STACK_OK)
343                     {
344                         OIC_LOG(ERROR, TAG, "Error sending response");
345                     }
346                     return;
347                 }
348                 OCRepPayload* payloadCbor = OCRepPayloadCreate();
349                 if (!payloadCbor)
350                 {
351                     response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
352                     if (OCDoResponse(&response) != OC_STACK_OK)
353                     {
354                         OIC_LOG(ERROR, TAG, "Error sending response");
355                     }
356                     cJSON_Delete(payloadJson);
357                     return;
358                 }
359
360                 CHPJsonToRepPayload(payloadJson, payloadCbor);
361                 response.payload = (OCPayload *)payloadCbor;
362                 cJSON_Delete(payloadJson);
363                 break;
364             default:
365                 OIC_LOG(ERROR, TAG, "Payload format is not supported");
366                 response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
367                 if (OCDoResponse(&response) != OC_STACK_OK)
368                 {
369                     OIC_LOG(ERROR, TAG, "Error sending response");
370                 }
371                 return;
372         }
373     }
374     // Header Options parsing
375     response.numSendVendorSpecificHeaderOptions = 0;
376     OCHeaderOption *optionsPointer = response.sendVendorSpecificHeaderOptions;
377
378     uint8_t tempOptionNumber = u_arraylist_length(httpResponse->headerOptions);
379     for (int numOptions = 0; numOptions < tempOptionNumber &&
380                              response.numSendVendorSpecificHeaderOptions < MAX_HEADER_OPTIONS;
381                              numOptions++)
382     {
383         HttpHeaderOption_t *httpOption = u_arraylist_get(httpResponse->headerOptions, numOptions);
384         result = CHPGetOCOption(httpOption, optionsPointer);
385         if (OC_STACK_OK != result)
386         {
387             OIC_LOG_V(ERROR, TAG, "CHPGetCoAPOption failed[%d][%d]", result,
388                                                 response.numSendVendorSpecificHeaderOptions);
389             continue;
390         }
391
392         response.numSendVendorSpecificHeaderOptions++;
393         optionsPointer += 1;
394     }
395
396     if (OCDoResponse(&response) != OC_STACK_OK)
397     {
398         OIC_LOG(ERROR, TAG, "Error sending response");
399     }
400
401     //OICFree(coapResponseInfo.info.payload);
402     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
403 }
404
405 OCStackResult CHPHandleOCFRequest(const OCEntityHandlerRequest* requestInfo,
406                                    const char* proxyUri)
407 {
408     OIC_LOG_V(DEBUG, TAG, "%s IN", __func__);
409
410     HttpRequest_t httpRequest = { .httpMajor = 1,
411                                   .httpMinor = 1};
412
413     OCEntityHandlerResponse response = { .requestHandle = requestInfo->requestHandle,
414                                          .resourceHandle = requestInfo->resource};
415     OCStackResult result = CHPGetHttpMethod(requestInfo->method, &httpRequest.method);
416     if (OC_STACK_OK != result)
417     {
418         OIC_LOG(ERROR, TAG, "Method not found in HTTP");
419         response.ehResult = OC_EH_BAD_REQ;
420         if (OCDoResponse(&response) != OC_STACK_OK)
421         {
422             OIC_LOG(ERROR, TAG, "Error sending response");
423         }
424
425         return OC_STACK_ERROR;
426     }
427
428     uint8_t vendorOptions = requestInfo->numRcvdVendorSpecificHeaderOptions;
429     if (vendorOptions)
430     {
431         httpRequest.headerOptions = u_arraylist_create();
432         for (int option = 0; option < vendorOptions; option++)
433         {
434             HttpHeaderOption_t *httpOption = NULL;
435             result = CHPGetHttpOption(requestInfo->rcvdVendorSpecificHeaderOptions + option,
436                                       &httpOption);
437             if (OC_STACK_OK != result || NULL == httpOption )
438             {
439                 OIC_LOG_V(ERROR, TAG, "CHPGetHttpOption failed [%d]", result);
440                 continue;
441             }
442             u_arraylist_add(httpRequest.headerOptions, (void *)httpOption);
443         }
444     }
445
446     OICStrcpy(httpRequest.resourceUri, sizeof(httpRequest.resourceUri), proxyUri);
447
448     if (requestInfo->payload && requestInfo->payload->type == PAYLOAD_TYPE_REPRESENTATION)
449     {
450         // Conversion from cbor to json.
451         cJSON *payloadJson = CHPRepPayloadToJson((OCRepPayload *)requestInfo->payload);
452         if (!payloadJson)
453         {
454             response.ehResult = OC_EH_BAD_REQ;
455             if (OCDoResponse(&response) != OC_STACK_OK)
456             {
457                 OIC_LOG(ERROR, TAG, "Error sending response");
458             }
459
460             return OC_STACK_ERROR;
461
462         }
463         httpRequest.payload = (void *)cJSON_Print(payloadJson);
464         httpRequest.payloadLength = strlen(httpRequest.payload);
465         OICStrcpy(httpRequest.payloadFormat, sizeof(httpRequest.payloadFormat),
466                   CBOR_CONTENT_TYPE);
467         cJSON_Delete(payloadJson);
468     }
469
470     OICStrcpy(httpRequest.acceptFormat, sizeof(httpRequest.acceptFormat),
471               ACCEPT_MEDIA_TYPE);
472     CHPRequest_t *chpRequest = (CHPRequest_t *)OICCalloc(1, sizeof(CHPRequest_t));
473     if (!chpRequest)
474     {
475         OIC_LOG(ERROR, TAG, "Calloc failed");
476         response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
477         if (OCDoResponse(&response) != OC_STACK_OK)
478         {
479             OIC_LOG(ERROR, TAG, "Error sending response");
480         }
481
482         OICFree(httpRequest.payload);
483         u_arraylist_destroy(httpRequest.headerOptions);
484         return OC_STACK_NO_MEMORY;
485     }
486
487     chpRequest->requestHandle = requestInfo->requestHandle;
488     chpRequest->method = requestInfo->method;
489
490     result = CHPPostHttpRequest(&httpRequest, CHPHandleHttpResponse,
491                                 (void *)chpRequest);
492     if (OC_STACK_OK != result)
493     {
494         OIC_LOG_V(ERROR, TAG, "CHPPostHttpRequest failed[%d]", result);
495         switch (result)
496         {
497             case OC_STACK_INVALID_URI:
498                 response.ehResult = OC_EH_BAD_REQ;
499                 break;
500             default:
501                 response.ehResult = OC_EH_INTERNAL_SERVER_ERROR;
502         }
503
504         if (OCDoResponse(&response) != OC_STACK_OK)
505         {
506             OIC_LOG(ERROR, TAG, "Error sending response");
507         }
508
509         OICFree(httpRequest.payload);
510         OICFree(chpRequest);
511         u_arraylist_destroy(httpRequest.headerOptions);
512         return OC_STACK_ERROR;
513     }
514
515     if (!httpRequest.payloadCached)
516     {
517         // Free only if parser has not cached it.
518         OICFree(httpRequest.payload);
519     }
520     u_arraylist_destroy(httpRequest.headerOptions);
521     OIC_LOG_V(DEBUG, TAG, "%s OUT", __func__);
522     return OC_STACK_OK;
523 }