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