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