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