Scheduled maintenance on Saturday, 27 September 2025, from 07:00 AM to 4:00 PM GMT (09:00 AM to 6:00 PM CEST) - some services may be unavailable -

Skip to content
Snippets Groups Projects
pathComp_RESTapi.c 75.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    /*
    
     * Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
    
     *
     * 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.
     */
    
    Lluis Gifre Renom's avatar
    Lluis Gifre Renom committed
    
    
    #include <stdio.h>
    #include <stdlib.h> 
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <glib.h>
    #include <sys/time.h>
    #include <ctype.h>
    #include <strings.h>
    #include <time.h>
    #include <fcntl.h>
    #include <uuid/uuid.h>
    #include <string.h>
    
    #include "pathComp_log.h"
    #include "pathComp_tools.h"
    #include "pathComp_cjson.h"
    #include "pathComp_ksp.h"
    
    #include "pathComp_sp.h"
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    #include "pathComp_ear.h"
    
    #include "pathComp_RESTapi.h"
    
    #define ISspace(x) isspace((int)(x))
    
    #define SERVER_STRING "Server: PATHCOMP/0.1.0\r\n"
    
    // List of Clients connected to the PATH COMP
    GList *RESTapi_tcp_client_list = NULL;
    
    // Id for CLient HTTP (REST API) Connection
    guint CLIENT_ID = 0;
    guint32 paId_req = 0;
    
    // Global variables
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    GList* linkList;
    GList* deviceList;
    GList* serviceList;
    GList* activeServList;
    
    
    gchar algId[MAX_ALG_ID_LENGTH];
    gboolean syncPath = FALSE;
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Seek a connected tcp client by its fd in the RESTapi_tcp_client_list
     * 	
     * 	@param data
     *  @param userdata
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint find_rl_client_by_fd (gconstpointer data, gconstpointer userdata)
    {
    	 /** check values */
         g_assert(data != NULL);
         g_assert(userdata != NULL);
    	 
    	 struct pathComp_client *client = (struct pathComp_client*)data;
         gint fd = *(gint *)userdata; 
         
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    	 if (client->fd == fd) {
    		 return 0;
    	 }
    
        return -1;	
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to send a message to the corresponding channel
     * 	
     * 	@param source
     *  @param buf
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    gint rapi_send_message (GIOChannel *channel, char *buf)	{
    	gsize nbytes, buffersize;
    
    	//DEBUG_PC ("Msg prepared to be sent REST API RESPONSE ");
        gint len = strlen ((const char*) buf);
        //DEBUG_PC ("Targeted Length of the buffer: %d", len);       
    
        buffersize = g_io_channel_get_buffer_size (channel);
        //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);
        
        gsize newBufferSize = MAX_GIO_CHANNEL_BUFFER_SIZE;
        g_io_channel_set_buffer_size (channel, newBufferSize);
        
        buffersize = g_io_channel_get_buffer_size (channel);
        //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize);
        
    	/** Send message.  */
        GError *error = NULL;
        
        char *ptr = buf;
        gint nleft = strlen ((const char *)buf);
        
        while (nleft > 0) {
            g_io_channel_write_chars (channel, (void *)ptr, nleft, &nbytes, &error);
            if (error) {
                DEBUG_PC ("Error sending the message to TCP Client");
                return (-1);
            }
            
            //DEBUG_PC ("Sent %d bytes", (gint)nbytes);        
            nleft = nleft - nbytes;
            //DEBUG_PC ("Remaining to be sent %d", nleft);
            ptr += nbytes;        
        } 
    	DEBUG_PC("RESPONSE MSG SENT");
    	return 0;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to return when something goes wrong when processing the REST API Command
     * 	
     * 	@param source
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void RESTapi_unimplemented (GIOChannel *source)
    {
    	gint ret = 0;
    	guchar buftmp[1024];
    	char *buf = g_malloc0 (sizeof (char) * 2048000); 
    	sprintf((char *)buf, "HTTP/1.1 400 Bad request\r\n");
    
    	sprintf((char *)buftmp, SERVER_STRING);
    	strcat ((char *)buf, (const char *)buftmp);
    
    	sprintf((char *)buftmp, "Content-Type: text/plain\r\n");
    	strcat ((char *)buf, (const char *)buftmp);
    
    	sprintf((char *)buftmp, "\r\n");
    	strcat ((char *)buf, (const char *)buftmp);
    	      
    	sprintf((char *)buftmp, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
    	strcat ((char *)buf, (const char *)buftmp);
    
    	sprintf((char *)buftmp, "</TITLE></HEAD>\r\n");
    	strcat ((char *)buf, (const char *)buftmp);
    
    	sprintf((char *)buftmp, "<BODY><P>HTTP request method not supported.\r\n");
    	strcat ((char *)buf, (const char *)buftmp);	
    
    	sprintf((char *)buftmp, "</BODY></HTML>\r\n");
    	strcat ((char *)buf, (const char *)buftmp);
    
    	ret = rapi_send_message (source, buf);
    	g_free (buf);
    	(void)ret;
    
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to put in the buffer the date according to RFC 1123
     * 	
     * 	@param date
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void rapi_add_date_header (char *date)
    {
        static const char *DAYS[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
        static const char *MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    
        time_t t = time(NULL);
        struct tm *tm = NULL;
        struct tm sys;
        gmtime_r(&t, &sys);
        tm = &sys;
    
        sprintf((char *)date, "DATE: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", DAYS[tm->tm_wday], tm->tm_mday, 
    								      MONTHS[tm->tm_mon], 1900 + tm->tm_year, 
    								      tm->tm_hour, tm->tm_min, tm->tm_sec);
        
        return;
    }
    
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to add DeviceId and EndpointId forming the computed path
     *
     * 	@param pathObj
     *  @param p
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void add_comp_path_deviceId_endpointId_json(cJSON* pathObj, struct path_t* p, struct compRouteOutput_t* oElem) {
    	g_assert(p);
    	g_assert(pathObj);
    
    	// add array for the devideId and endpointIds
    	cJSON* devicesArray = cJSON_CreateArray();
    	cJSON_AddItemToObject(pathObj, "devices", devicesArray);
    
    	// Add the source endpoint
    	cJSON* sEndPointIdObj;
    	cJSON_AddItemToArray(devicesArray, sEndPointIdObj = cJSON_CreateObject());
    	// Add the topology Id Object containing the topology uuid and context uuid
    	cJSON* stopIdObj;
    	cJSON_AddItemToObject(sEndPointIdObj, "topology_id", stopIdObj = cJSON_CreateObject());
    	cJSON_AddItemToObject(stopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.contextId));
    	cJSON_AddItemToObject(stopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.topology_uuid));
    
    	// Add the device Id (uuid) 
    	cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[0].device_uuid));
    	// Add the endpoint Id (uuid)
    	cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].endpoint_uuid));
    
    
    	for (gint i = 0; i < p->numPathLinks; i++) {
    		struct pathLink_t* pL = &(p->pathLinks[i]);
    		cJSON* dElemObj; // Device Element Object of the array
    		cJSON_AddItemToArray(devicesArray, dElemObj = cJSON_CreateObject());
    
    		// Add the topologyId with the topologyuuid and contextId
    		cJSON* tIdObj;
    		cJSON_AddItemToObject(dElemObj, "topology_id", tIdObj = cJSON_CreateObject());
    		cJSON_AddItemToObject(tIdObj, "contextId", cJSON_CreateString(pL->topologyId.contextId));
    		cJSON_AddItemToObject(tIdObj, "topology_uuid", cJSON_CreateString(pL->topologyId.topology_uuid));
    
    		// Add Device Id
    		cJSON_AddItemToObject(dElemObj, "device_id", cJSON_CreateString(pL->aDeviceId));
    
    		// Add endpoint Id
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    		cJSON_AddItemToObject(dElemObj, "endpoint_uuid", cJSON_CreateString(pL->aEndPointId));  
    
    	}
    	
    	// Add the sink	endpoint
    	cJSON* sinkEndPointIdObj;
    	cJSON_AddItemToArray(devicesArray, sinkEndPointIdObj = cJSON_CreateObject());
    	// Add the topology Id Object containing the topology uuid and context uuid
    	cJSON* sinkTopIdObj;
    	cJSON_AddItemToObject(sinkEndPointIdObj, "topology_id", sinkTopIdObj = cJSON_CreateObject());
    	cJSON_AddItemToObject(sinkTopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.contextId));
    	cJSON_AddItemToObject(sinkTopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.topology_uuid));
    
    	// Add the device Id (uuid) 
    	cJSON_AddItemToObject(sinkEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[1].device_uuid));
    	// Add the endpoint Id (uuid)
    	cJSON_AddItemToObject(sinkEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].endpoint_uuid));
    	
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    	return;				 
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to add the links forming the computed path
     *
     * 	@param pathObj
     *  @param p
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void add_comp_path_link_json(cJSON* pathObj, struct path_t* p) {
    	g_assert(p);
    	g_assert(pathObj);
    
    	// Add array for the links
    	cJSON* linkArray = cJSON_CreateArray();
    	cJSON_AddItemToObject(pathObj, "link", linkArray);
    
    	for (gint i = 0; i < p->numPathLinks; i++) {
    		struct pathLink_t* pL = &(p->pathLinks[i]);
    		cJSON* lElemObj; // Link Element Object of the array
    		cJSON_AddItemToArray(linkArray, lElemObj = cJSON_CreateObject());
    
    		// Add link Id
    		cJSON_AddItemToObject(lElemObj, "link_Id", cJSON_CreateString(pL->linkId));
    
    		// Add link topologies
    		cJSON* linkTopoArray = cJSON_CreateArray();
    		cJSON_AddItemToObject(lElemObj, "topology", linkTopoArray);
    
    		for (gint j = 0; j < pL->numLinkTopologies; j++) {
    			struct linkTopology_t* linkTopo = &(pL->linkTopologies[j]);
    			cJSON* lTopoElemObj;
    			cJSON_AddItemToArray(linkTopoArray, lTopoElemObj = cJSON_CreateObject());
    			cJSON_AddItemToObject(lTopoElemObj, "topology_uuid", cJSON_CreateString(linkTopo->topologyId));
    		}
    	}
    	return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Compose the JSON Body of the succesfully network connectivity service
     * 	
     * 	@param body
     *  @param length
     *  @param compRouteOutputList
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void rapi_response_json_contents (char *body, gint *length, struct compRouteOutputList_t *compRouteOutputList)
    {
        char *buftmp;    
        cJSON *root = cJSON_CreateObject();     
    	DEBUG_PC ("Creating the JSON body of the response"); 
    
    	// Create response-list array
    	cJSON *responseListArray = cJSON_CreateArray();
    	cJSON_AddItemToObject(root, "response-list", responseListArray);
    	
    	// Add computed routes to the response-list
    	for (gint i = 0; i < compRouteOutputList->numCompRouteConnList; i++) {
    		struct compRouteOutput_t *oElem = &(compRouteOutputList->compRouteConnection[i]); // reference to output element from the list of computed routes
    		cJSON *oElemObj;
    		cJSON_AddItemToArray (responseListArray, oElemObj = cJSON_CreateObject());
    		
    		// Add the service Id Object
    		cJSON* servIdObj;
    		cJSON_AddItemToObject(oElemObj, "serviceId", servIdObj = cJSON_CreateObject());
    		cJSON_AddItemToObject(servIdObj, "contextId", cJSON_CreateString(oElem->serviceId.contextId));
    		cJSON_AddItemToObject(servIdObj, "service_uuid", cJSON_CreateString(oElem->serviceId.service_uuid));
    
    		// Add the service endpoints ids array
    		cJSON* sEndpointIdsArray = cJSON_CreateArray();
    		cJSON_AddItemToObject(oElemObj, "service_endpoints_ids", sEndpointIdsArray);
    
    		for (gint j = 0; j < oElem->num_service_endpoints_id; j++) {
    			//DEBUG_PC("parsing service endpoints ids");
    			//DEBUG_PC("endpoint: %s [%s]", oElem->service_endpoints_id[j].device_uuid, oElem->service_endpoints_id[j].endpoint_uuid);
    			//struct service_endpoints_id_t* sEndPointId = &(oElem->service_endpoints_id[j]);
    			cJSON* sEndPointIdObj;
    			cJSON_AddItemToArray(sEndpointIdsArray, sEndPointIdObj = cJSON_CreateObject());
    			// Add the topology Id Object containing the topology uuid and context uuid
    			cJSON* topIdObj;
    			cJSON_AddItemToObject(sEndPointIdObj, "topology_id", topIdObj = cJSON_CreateObject());
    			cJSON_AddItemToObject(topIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.contextId));
    			cJSON_AddItemToObject(topIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.topology_uuid));
    
    			// Add the device Id (uuid) 
    			cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[j].device_uuid));
    			// Add the endpoint Id (uuid)
    			cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].endpoint_uuid));
    		}
    		// Add no path issue
    		if (oElem->noPathIssue == NO_PATH_CONS_ISSUE) { // Error on finding the route, e.g., no feasible path
    			DEBUG_PC("NO PATH FOUND, AN ISSUE OCCURRED: %d", oElem->noPathIssue);
    			cJSON* noPathObj;
    			cJSON_AddItemToObject(oElemObj, "noPath", noPathObj = cJSON_CreateObject());
    			char str[5];
    			sprintf(str, "%d", oElem->noPathIssue);
    			cJSON_AddItemToObject(noPathObj, "issue", cJSON_CreateString(str));
    			continue;
    		}
    		
    		// Create the array to parse the computed path from the oElemObj
    		cJSON* pathArray = cJSON_CreateArray();
    		cJSON_AddItemToObject(oElemObj, "path", pathArray);
    		for (gint k = 0; k < oElem->numPaths; k++) {
    			struct path_t* p = &(oElem->paths[k]);
    			cJSON* pathObj;
    			cJSON_AddItemToArray(pathArray, pathObj = cJSON_CreateObject());
    
    			// Add path capacity
    			cJSON* pathCapObj;
    			cJSON_AddItemToObject(pathObj, "path-capacity", pathCapObj = cJSON_CreateObject());
    			cJSON* totalSizeObj;
    			cJSON_AddItemToObject(pathCapObj, "total-size", totalSizeObj = cJSON_CreateObject());
    			cJSON_AddItemToObject(totalSizeObj, "value", cJSON_CreateNumber(p->path_capacity.value));
    			cJSON_AddItemToObject(totalSizeObj, "unit", cJSON_CreateNumber(p->path_capacity.unit));
    
    			// Add path latency
    			cJSON* pathLatObj;
    			char lat[16];
    			sprintf(lat, "%lf", p->path_latency.fixed_latency);
    			cJSON_AddItemToObject(pathObj, "path-latency", pathLatObj= cJSON_CreateObject());
    			cJSON_AddItemToObject(pathLatObj, "fixed-latency-characteristic", cJSON_CreateString(lat));
    
    			// Add path cost
    			cJSON* pathCostObj;
    			cJSON_AddItemToObject(pathObj, "path-cost", pathCostObj = cJSON_CreateObject());
    			cJSON_AddItemToObject(pathCostObj, "cost-name", cJSON_CreateString(p->path_cost.cost_name));
    			char value[16];
    			sprintf(value, "%lf", p->path_cost.cost_value);
    			cJSON_AddItemToObject(pathCostObj, "cost-value", cJSON_CreateString(value));
    			char algorithm[16];
    			sprintf(algorithm, "%lf", p->path_cost.cost_algorithm);
    			cJSON_AddItemToObject(pathCostObj, "cost-algorithm", cJSON_CreateString(algorithm));
    
    			// Add the links
    
    			//add_comp_path_link_json(pathObj, p);
    			// Add deviceId, endpointId
    			add_comp_path_deviceId_endpointId_json(pathObj, p, oElem);
    
    		}	
    	}
    	
        //DEBUG_PC ("JSON Body Response DONE");	
        buftmp = (char *)cJSON_Print(root);
        strcat (body, (const char*) buftmp);
        *length = strlen ((const char*)body);    
        //DEBUG_PC ("JSON Body (length: %d)", *length);
        //DEBUG_PC ("%s", body);    
    	cJSON_Delete (root);
    	g_free(buftmp);
        return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to return response OK via REST API with the computed serviceId
     * 	
     * 	@param source
     *  @param httpCode
     *  @param compRouteOutputList
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    void rapi_response_ok (GIOChannel *source, gint httpCode, struct compRouteOutputList_t *compRouteOutputList) {
    	gint ret = 0;
        
        //DEBUG_PC ("Creating the JSON Body and sending the response of the computed Route List");
        
    
        char buftmp[1024];
        char *buf = g_malloc0 (sizeof (char) * 2048000); 
    
        // Create the Body of the Response 
    
        char * msgBody = g_malloc0 (sizeof (char) * 2048000);    
    
    	
    	//  If path computation was requested, the resulting computation is returned in the msg body
    	if (compRouteOutputList != NULL) {
    		rapi_response_json_contents(msgBody, &length, compRouteOutputList);
    	}
    	// no path computation was requested, then a basic msg is just added
    	else {
    		cJSON* root = cJSON_CreateObject();
    		char status[3];
    		sprintf(status, "OK");
    		cJSON_AddItemToObject(root, "Status", cJSON_CreateString(status));
    		msgBody = (char*)cJSON_Print(root);
    		length = strlen((const char*)msgBody);
    		cJSON_Delete(root);
    	}
    
    		
    	sprintf((char *)buf, "HTTP/1.1 200 OK\r\n");    
    	
        sprintf((char *)buftmp, SERVER_STRING);
        strcat ((char *)buf, (const char *)buftmp);    
      
        sprintf ((char *)buftmp, "Content-Type: application/json\r\n");
        strcat ((char *)buf, (const char *)buftmp);    
        
        // Add the length of the JSON enconding to the Content_Length
    
        char buff_length[16];    
    
        sprintf(buff_length, "%d", length);
        
        sprintf ((char *)buftmp, "Content-Length: ");
        strcat ((char *)buftmp, (const char *)buff_length);
        strcat ((char *)buftmp, "\r\n");
        strcat ((char *)buf, (const char *)buftmp);    
        
        // Add DATE header
        rapi_add_date_header ((char *)buftmp);
        strcat ((char *)buf, (const char *)buftmp);     
        sprintf((char *)buftmp, "\r\n");
        strcat ((char *)buf, (const char *)buftmp);
    
    
    	strcat((char*)buf, (const char*)msgBody);
    			
    
    	//DEBUG_PC ("%s", buf);	    
        ret = rapi_send_message (source, buf);    
        g_free (buf);
        memset (buftmp, '\0', sizeof ( buftmp));    
    
        g_free (msgBody);
    
        (void)ret;    
        return;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to return response OK via REST API
     * 	
     * 	@param source
     *  @param error
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
    /////////////////////////////////////////////////////////////////////////////////////////
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    void rapi_response (GIOChannel *source, gint error) {
    
    	 int ret = 0;	
    	 guchar buftmp[1024];
    	 char * buf = g_malloc0 (sizeof (char) * 2048000);
    	 if (error == HTTP_RETURN_CODE_BAD_REQUEST)
    		sprintf((char *)buf, "HTTP/1.1 400 BAD REQUEST\r\n");
    	 else if (error == HTTP_RETURN_CODE_UNAUTHORIZED)
    		sprintf((char *)buf, "HTTP/1.1 401 UNAUTHORIZED\r\n");
    	 else if (error == HTTP_RETURN_CODE_FORBIDDEN)
    		sprintf((char *)buf, "HTTP/1.1 403 FORBIDDEN\r\n");    
    	 else if (error == HTTP_RETURN_CODE_NOT_FOUND)
    		sprintf((char *)buf, "HTTP/1.1 404 NOT FOUND\r\n");
    	 
    	 sprintf((char *)buftmp, SERVER_STRING);
    	 strcat ((char *)buf, (const char *)buftmp);    
    	
    	 sprintf((char *)buftmp, "Content-Type: text/plain\r\n");
    	 strcat ((char *)buf, (const char *)buftmp);    
    	 
    	 sprintf((char *)buftmp, "Content-Length: 0/plain\r\n");
    	 strcat ((char *)buf, (const char *)buftmp);    
    	 
    	 // Add DATE header
    	 rapi_add_date_header ((char *)buftmp);
    	 strcat ((char *)buf, (const char *)buftmp);     
    	     
    	 sprintf((char *)buftmp, "\r\n");
    	 strcat ((char *)buf, (const char *)buftmp);
    	 // Print the prepared message
    	 DEBUG_PC ("%s", buf);
    	 
    	 // Send the message
    	 ret = rapi_send_message (source, buf);	 
    	 g_free (buf);	 
    	 (void)ret;
    	
    	 return;
    }
    
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief parsing topology Identifier Object (contains Context Id and Toplogy UUID) JSON object
     *
     * 	@param obj
     *  @param topology_id
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_topology_Id(cJSON* obj, struct topology_id_t* topology_id) {
    	g_assert(topology_id);
    	// Get the context Id (UUID) from the topologyIdObj
    	cJSON* contextIdObj = cJSON_GetObjectItem(obj, "contextId");
    	if (cJSON_IsString(contextIdObj)) {
    		duplicate_string(topology_id->contextId, contextIdObj->valuestring);
    	}
    	// Get the topologyId (UUID) from the topologyIdObj
    	cJSON* topologyUuidObj = cJSON_GetObjectItem(obj, "topology_uuid");
    	if (cJSON_IsString(topologyUuidObj)) {
    		duplicate_string(topology_id->topology_uuid, topologyUuidObj->valuestring);
    	}
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief parsing EndpointIds JSON object
     *
     * 	@param item
     *  @param serviceEndPointId
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_endPointsIds(cJSON* item, struct service_endpoints_id_t* serviceEndPointId) {
    	// Get the topology Id Object
    	cJSON* topologyIdObj = cJSON_GetObjectItem(item, "topology_id");
    	if (cJSON_IsObject(topologyIdObj)) {
    		parse_topology_Id(topologyIdObj, &serviceEndPointId->topology_id);
    	}
    	// Get the deviceId (UUID)
    	cJSON* deviceIdObj = cJSON_GetObjectItem(item, "device_id");
    	if (cJSON_IsString(deviceIdObj)) {
    		duplicate_string(serviceEndPointId->device_uuid, deviceIdObj->valuestring);
    		DEBUG_PC("DeviceId: %s", serviceEndPointId->device_uuid);
    	}
    	// Get the endpointId (UUID)
    	cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_uuid");
    	if (cJSON_IsString(endPointIdObj)) {
    		duplicate_string(serviceEndPointId->endpoint_uuid, endPointIdObj->valuestring);
    		DEBUG_PC("EndPointId: %s", serviceEndPointId->endpoint_uuid);
    	}
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to parse the array of Endpoint Ids	of the active services
     *
     * 	@param endPointArray
     *  @param actServ
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_act_service_endPointsIds_array(cJSON* endPointIdArray, struct activeService_t* actServ) {
    	g_assert(actServ);
    
    	for (gint i = 0; i < cJSON_GetArraySize(endPointIdArray); i++) {
    		actServ->num_service_endpoints_id++;
    		struct service_endpoints_id_t* serviceEndPointId = &(actServ->service_endpoints_id[i]);
    		cJSON* item = cJSON_GetArrayItem(endPointIdArray, i);
    		parse_endPointsIds(item, serviceEndPointId);
    	}
    	return;
    }
    
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
     * 	@brief Function used to parse the array of Endpoint Ids	of the requested services
    
     *
     * 	@param endPointArray
     *  @param s
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_service_endPointsIds_array(cJSON* endPointIdArray, struct service_t* s) {
    
    	for (gint i = 0; i < cJSON_GetArraySize(endPointIdArray); i++) {
    		s->num_service_endpoints_id++;
    		struct service_endpoints_id_t* serviceEndPointId = &(s->service_endpoints_id[i]);
    
    		cJSON* item = cJSON_GetArrayItem(endPointIdArray, i);
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    		parse_endPointsIds(item, serviceEndPointId);
    
    	}
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to parse the array with the required service constraints
     *
     * 	@param constraintArray
     *  @param s
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_service_constraints(cJSON* constraintArray, struct service_t* s) {
    	for (gint i = 0; i < cJSON_GetArraySize(constraintArray); i++) {
    		s->num_service_constraints++;
    		struct constraint_t* constraint = &(s->constraints[i]);
    
    		cJSON* item = cJSON_GetArrayItem(constraintArray, i);
    
    		// Get the constraint type
    		cJSON* typeObj = cJSON_GetObjectItem(item, "constraint_type");
    		if (cJSON_IsString(typeObj)) {
    			duplicate_string(constraint->constraint_type, typeObj->valuestring);
    		}
    		// Get the constraint value
    		cJSON* valueObj = cJSON_GetObjectItem(item, "constraint_value");
    		if (cJSON_IsString(valueObj)) {
    			 duplicate_string(constraint->constraint_value, valueObj->valuestring);
    		} 
    		DEBUG_PC("Service Reqs [%d] -- Type: %s | Value: %s", i+1, constraint->constraint_type, constraint->constraint_value);
    	}
    	return;
    }
    
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to parse the serviceId information from a JSON obj
     *
     * 	@param obj
     *  @param serviceId
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_json_serviceId(cJSON* obj, struct serviceId_t* serviceId) {
    	g_assert(obj);
    	g_assert(serviceId);
    
    	// Get context Id uuid
    	cJSON* contextIdObj = cJSON_GetObjectItem(obj, "contextId");
    	if (cJSON_IsString(contextIdObj)) {
    		// convert the string in contextId->valuestring in uuid binary format
    		duplicate_string(serviceId->contextId, contextIdObj->valuestring);
    		DEBUG_PC("ContextId: %s (uuid string format)", serviceId->contextId);
    	}
    	// Get service Id uuid
    	cJSON* serviceUuidObj = cJSON_GetObjectItem(obj, "service_uuid");
    	if (cJSON_IsString(serviceUuidObj)) {
    		duplicate_string(serviceId->service_uuid, serviceUuidObj->valuestring);
    		DEBUG_PC("Service UUID: %s (uuid string format)", serviceId->service_uuid);
    	}
    	return;
    }
    
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to parse the array with the different
     * network services
     *
     * 	@param serviceArray
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parsing_json_serviceList_array(cJSON* serviceArray) {
    
    	for (gint i = 0; i < cJSON_GetArraySize(serviceArray); i++)
    	{
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    		struct service_t* service = g_malloc0(sizeof(struct service_t));
    		if (service == NULL) {
    			DEBUG_PC("Memory allocation error ...");
    			exit(-1);
    		}
    
    		cJSON* item = cJSON_GetArrayItem(serviceArray, i);
    		// Get the algorithm Id
    		cJSON* algIdItem = cJSON_GetObjectItem(item, "algId");
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    		if (cJSON_IsString(algIdItem)) {
    
    			duplicate_string(service->algId, algIdItem->valuestring);
    			DEBUG_PC ("algId: %s", service->algId);
    			// assumed that all the services request the same algId
    			duplicate_string(algId, service->algId);
    		}
    
    		// Get the syncPaths
    		cJSON* synchPathObj = cJSON_GetObjectItemCaseSensitive(item, "syncPaths");
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    		if (cJSON_IsBool(synchPathObj)) {
    
    			// Check Synchronization of multiple Paths to attain e.g. global concurrent optimization
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    			if (cJSON_IsTrue(synchPathObj)) {
    
    				syncPath = TRUE;
    				DEBUG_PC("Path Synchronization is required");
    			}
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    			if (cJSON_IsFalse(synchPathObj)) {
    
    				syncPath = FALSE;
    				DEBUG_PC("No Path Synchronization");
    			}
    		}
    
    		// Get service Id in terms of contextId and service uuids
    		cJSON* serviceIdObj = cJSON_GetObjectItem(item, "serviceId");
    		if (cJSON_IsObject(serviceIdObj)) {
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    			parse_json_serviceId(serviceIdObj, &service->serviceId);
    
    		}		
    
    		// Get de service type
    		cJSON* serviceTypeObj = cJSON_GetObjectItem(item, "serviceType");
    		if (cJSON_IsNumber(serviceTypeObj))
    		{
    			service->service_type = (guint)(serviceTypeObj->valuedouble);
    			print_service_type(service->service_type);
    		}
    
    		// Get the endPoints array of the service
    		cJSON* endPointIdsArray = cJSON_GetObjectItem(item, "service_endpoints_ids");
    		if (cJSON_IsArray(endPointIdsArray)) {
    			parse_service_endPointsIds_array(endPointIdsArray, service);
    		}	
    
    		// Get the service constraints
    		cJSON* constraintArray = cJSON_GetObjectItem(item, "service_constraints");
    		if (cJSON_IsArray(constraintArray)) {
    			parse_service_constraints(constraintArray, service);
    		}		
    
    
    		// Get the maximum number of paths to be computed (kPaths) inspected/explored
    		cJSON* kPathsInspObj = cJSON_GetObjectItemCaseSensitive(item, "kPaths_inspection");
    		if (cJSON_IsNumber(kPathsInspObj)){
    			service->kPaths_inspected = (guint)(kPathsInspObj->valuedouble);
    
    		// Get the maximum number of paths to be computed (kPaths) returned
    		cJSON* kPathsRetpObj = cJSON_GetObjectItemCaseSensitive(item, "kPaths_return");
    		if (cJSON_IsNumber(kPathsRetpObj)){
    			service->kPaths_returned = (guint)(kPathsRetpObj->valuedouble);
    		}	
    		
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    		// Append the requested service to the serviceList
    		serviceList = g_list_append(serviceList, service);
    
    	}
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function parsing the capacity attributes in the endpoint
     *
     * 	@param capacity
     *  @param c
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_capacity_object(cJSON* capacity, struct capacity_t* c) {
    	cJSON* totalSizeObj = cJSON_GetObjectItem(capacity, "total-size");
    	if (cJSON_IsObject(totalSizeObj)) {
    		//Get the capacity value
    		cJSON* valueObj = cJSON_GetObjectItem(totalSizeObj, "value");
    		if (cJSON_IsNumber(valueObj)) {
    			memcpy(&c->value, &valueObj->valuedouble, sizeof (gdouble));
    		}
    		// Get the Units
    		cJSON* unitObj = cJSON_GetObjectItem(totalSizeObj, "unit");
    		if (cJSON_IsNumber(unitObj)) {
    			c->unit = (guint)(unitObj->valuedouble);
    		}	
    	}
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function parsing the device endpoints
     *
     * 	@param endPointsArray
     *  @param d
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parse_json_device_endpoints_array(cJSON* endPointsArray, struct device_t* d) {
    	for (gint i = 0; i < cJSON_GetArraySize(endPointsArray); i++) {
    		d->numEndPoints++;
    
    		if (d->numEndPoints >= MAX_DEV_ENDPOINT_LENGTH) {
    			DEBUG_PC("d->numEndPoints(%d) exceeded MAX_DEV_ENDPOINT_LENGTH(%d)", d->numEndPoints, MAX_DEV_ENDPOINT_LENGTH);
    		}
    
    		struct endPoint_t* endpoint = &(d->endPoints[i]);
    
    		cJSON* item = cJSON_GetArrayItem(endPointsArray, i);
    
    		// Get the Endpoint Identifier: topology, context, device and endpointId
    		cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_id");
    		if (cJSON_IsObject(endPointIdObj)) {
    			// Get the topology Id Object
    			cJSON* topologyIdObj = cJSON_GetObjectItem(endPointIdObj, "topology_id");
    			if (cJSON_IsObject(topologyIdObj)) {
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    				parse_topology_Id(topologyIdObj, &endpoint->endPointId.topology_id);
    
    			}
    			// Get the deviceId
    			cJSON* deviceIdObj = cJSON_GetObjectItem(endPointIdObj, "device_id");
    			if (cJSON_IsString(deviceIdObj)) {				
    				duplicate_string(endpoint->endPointId.device_id, deviceIdObj->valuestring);
    			}
    			// Get the endpoint_uuid
    			cJSON* endPointUuidObj = cJSON_GetObjectItem(endPointIdObj, "endpoint_uuid");
    			if (cJSON_IsString(endPointUuidObj)) {				
    				duplicate_string(endpoint->endPointId.endpoint_uuid, endPointUuidObj->valuestring);
    			}
    		}
    		// Get the EndPoint Type
    		cJSON* endPointTypeObj = cJSON_GetObjectItem(item, "endpoint_type");
    		if (cJSON_IsString(endPointTypeObj)) {
    			duplicate_string(endpoint->endpointType, endPointTypeObj->valuestring);
    			//DEBUG_PC("Device Endpoint (%d) -- EndPoint Type: %s", i + 1, endpoint->endpointType);
    		}
    		// Link Port Direction
    		cJSON* linkPortDirectionObj = cJSON_GetObjectItem(item, "link_port_direction");
    		if (cJSON_IsNumber(linkPortDirectionObj)) {
    			endpoint->link_port_direction = (guint)(linkPortDirectionObj->valuedouble);
    			print_link_port_direction(endpoint->link_port_direction);
    		}
    		// EndPoint Termination Direction
    		cJSON* terminationDirectionObj = cJSON_GetObjectItem(item, "termination-direction");
    		if (cJSON_IsNumber(terminationDirectionObj)) {
    			endpoint->termination_direction = (guint)(terminationDirectionObj->valuedouble);
    			print_termination_direction(endpoint->termination_direction);
    		}
    		// Endpoint Termination State
    		cJSON* terminationStateObj = cJSON_GetObjectItem(item, "termination-state");
    		if (cJSON_IsNumber(terminationStateObj)) {
    			endpoint->termination_state = (guint)(terminationStateObj->valuedouble);
    			print_termination_state(endpoint->termination_state);
    		}
    		// total potential capacity
    		cJSON* totalPotentialCapacityObj = cJSON_GetObjectItem(item, "total-potential-capacity");
    		if (cJSON_IsObject(totalPotentialCapacityObj))
    		{
    			parse_capacity_object(totalPotentialCapacityObj, &endpoint->potential_capacity);
    			//DEBUG_PC("Device Endpoint (%d) -- Potential Capacity: %f", i + 1, endpoint->potential_capacity.value);
    			print_capacity_unit(endpoint->potential_capacity.unit);
    		}
    		// total available capacity
    		cJSON* availableCapacityObj = cJSON_GetObjectItem(item, "available-capacity");
    		if (cJSON_IsObject(availableCapacityObj))
    		{
    			parse_capacity_object(availableCapacityObj, &endpoint->available_capacity);
    			//DEBUG_PC("Device Endpoint (%d) -- Available Capacity: %f", i + 1, endpoint->available_capacity.value);
    			print_capacity_unit(endpoint->available_capacity.unit);
    		}
    		// inter-domain plug-in
    		cJSON* interDomainPlugInObj = cJSON_GetObjectItem(item, "inter-domain-plug-in");
    		if (cJSON_IsObject(interDomainPlugInObj)) {
    			// Get the local
    			cJSON* idInterDomainLocal = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-local-id");
    			if (cJSON_IsString(idInterDomainLocal)) {
    				duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id, idInterDomainLocal->valuestring);
    				//DEBUG_PC("Inter-Domain Local Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id);				
    			}
    			// Get the remote
    			cJSON* idInterDomainRemote = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-remote-id");
    			if (cJSON_IsString(idInterDomainRemote)) {
    				duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id, idInterDomainRemote->valuestring);
    				//DEBUG_PC("Inter-Domain Remote Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id);
    			}
    		}
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    
    		// Energy consumption per endPoint port
    		cJSON* energyPortObj = cJSON_GetObjectItem(item, "energy_consumption");
    		if (cJSON_IsNumber(energyPortObj)) {
    			memcpy(&endpoint->energyConsumption, &energyPortObj->valuedouble, sizeof(gdouble));
    			DEBUG_PC("Endpoint Energy Consumption: %f", endpoint->energyConsumption);
    		}
    
    		// Endpoint Operational Status
    		cJSON* operationalStatusObj = cJSON_GetObjectItem(item, "operational_status");
    		if (cJSON_IsNumber(operationalStatusObj)) {
    			endpoint->operational_status = (gint)(operationalStatusObj->valuedouble);
    			DEBUG_PC("Endpoint Operational Status: %d", endpoint->operational_status);
    		}
    
    	}
    	return;
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////
    /**
     * 	@file pathComp_RESTapi.c
     * 	@brief Function used to parse the set/list of devices forming the context/topology
     *
     * 	@param deviceArray
     *
     *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
     *	@date 2022
     */
     /////////////////////////////////////////////////////////////////////////////////////////
    void parsing_json_deviceList_array(cJSON* deviceArray) {
    
    	DEBUG_PC("");
    	DEBUG_PC("========= PARSING DEVICE LIST ============");
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    	for (gint i = 0; i < cJSON_GetArraySize(deviceArray); i++) {		
    		struct device_t* d = g_malloc0(sizeof(struct device_t));
    		if (d == NULL) {
    			DEBUG_PC("Memory Allocation Failure");
    			exit(-1);
    		}
    
    		cJSON* item = cJSON_GetArrayItem(deviceArray, i);
    
    
    Ricardo Martínez's avatar
    Ricardo Martínez committed
    		// Get the power idle of the switch
    		cJSON* powerIdleObj = cJSON_GetObjectItem(item, "power_idle");
    		if (cJSON_IsNumber(powerIdleObj)) {