Skip to content
Snippets Groups Projects
pathComp_RESTapi.c 75.4 KiB
Newer Older
martinezric's avatar
martinezric committed
			memcpy(&d->power_idle, &powerIdleObj->valuedouble, sizeof(gdouble));
			DEBUG_PC("Power Idle: %f", d->power_idle);
		}

		// Get the operational state
		cJSON* opeStatusObj = cJSON_GetObjectItem(item, "operational_status");
		if (cJSON_IsNumber(opeStatusObj)) {
			d->operational_status = (gint)(opeStatusObj->valuedouble);
			DEBUG_PC("Operational Status: %d (0 Undefined, 1 Disabled, 2 Enabled", d->operational_status);
		}

		// Get the device UUID
		cJSON* deviceUuidObj = cJSON_GetObjectItem(item, "device_Id");
		if (cJSON_IsString(deviceUuidObj)) {
			duplicate_string(d->deviceId, deviceUuidObj->valuestring);
			DEBUG_PC("Device (%d) -- Id: %s (uuid string format)", i + 1, d->deviceId);
		}

		// Get the device Type
		cJSON* deviceTypeObj = cJSON_GetObjectItem(item, "device_type");
		if (cJSON_IsString(deviceTypeObj)) {
			duplicate_string(d->deviceType, deviceTypeObj->valuestring);
			//DEBUG_PC("  Device Type: %s ---", d->deviceType);
		}
		DEBUG_PC("DeviceId: %s, Device Type: %s", d->deviceId, d->deviceType);

		// get the device endPoints
		cJSON* deviceEndpointsArray = cJSON_GetObjectItem(item, "device_endpoints");
		if (cJSON_IsArray(deviceEndpointsArray)) {
			parse_json_device_endpoints_array(deviceEndpointsArray, d);
		}
martinezric's avatar
martinezric committed
		// append the device into the deviceList
		deviceList = g_list_append(deviceList, d);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON objects the endPoint of a link
 *
 * 	@param endPointsLinkObj
 *  @param l
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parse_json_link_endpoints_array(cJSON *endPointsLinkObj, struct link_t* l) {
	for (gint i = 0; i < cJSON_GetArraySize(endPointsLinkObj); i++) {
		//DEBUG_PC("link: %s has %d endPointIds", l->linkId, l->numLinkEndPointIds);
		l->numLinkEndPointIds++;
		struct link_endpointId_t* endPointLink = &(l->linkEndPointId[i]);

		cJSON* item = cJSON_GetArrayItem(endPointsLinkObj, i);

		// Get endPoint attributes (topologyId, deviceId, endpoint_uuid)
		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)) {
martinezric's avatar
martinezric committed
				parse_topology_Id(topologyIdObj, &endPointLink->topology_id);				
			}
			// Get the deviceId
			cJSON* deviceIdObj = cJSON_GetObjectItem(endPointIdObj, "device_id");
			if (cJSON_IsString(deviceIdObj)) {
				duplicate_string(endPointLink->deviceId, deviceIdObj->valuestring);
				DEBUG_PC("   Link Endpoint[%d] -- DeviceId: %s", i + 1, endPointLink->deviceId);
			}
			// Get the endpoint_uuid
			cJSON* endPointUuidObj = cJSON_GetObjectItem(endPointIdObj, "endpoint_uuid");
			if (cJSON_IsString(endPointUuidObj)) {
				duplicate_string(endPointLink->endPointId, endPointUuidObj->valuestring);
				//DEBUG_PC("Link Endpoint (%d) -- EndPoint Uuid: %s (uuid)", i + 1, endPointLink->endPointId);
			}
		}
	}
	//DEBUG_PC("link id: %s has %d endpoints", l->linkId, l->numLinkEndPointIds);
	return;
}

///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON objects describing the set of links
 *
 * 	@param linkListArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_linkList_array(cJSON* linkListArray) {
	DEBUG_PC("");
	DEBUG_PC("======= PARSING OF THE LINK LIST ARRAY ==========");
martinezric's avatar
martinezric committed
	for (gint i = 0; i < cJSON_GetArraySize(linkListArray); i++) {		
		struct link_t* l = g_malloc0(sizeof(struct link_t));
		if (l == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(linkListArray, i);
martinezric's avatar
martinezric committed

		// Get the link Id (uuid)
		cJSON* linkIdObj = cJSON_GetObjectItem(item, "link_Id");
			duplicate_string(l->linkId, linkIdObj->valuestring);
			DEBUG_PC(" * Link (%d) -- Id: %s (uuid)", i + 1, l->linkId);
martinezric's avatar
martinezric committed

		// Get the link endpoints (assumed to be p2p)
		cJSON* endPointsLinkObj = cJSON_GetObjectItem(item, "link_endpoint_ids");
		if (cJSON_IsArray(endPointsLinkObj)) {
			//DEBUG_PC("number linkEndPointIds: %d", l->numLinkEndPointIds);
			parse_json_link_endpoints_array(endPointsLinkObj, l);
		}
		// get the fowarding direction
		cJSON* fwdDirObj = cJSON_GetObjectItem(item, "forwarding_direction");
		if (cJSON_IsNumber(fwdDirObj)) {
			l->forwarding_direction = (guint)(fwdDirObj->valuedouble);
			print_link_forwarding_direction(l->forwarding_direction);
		}
		// total potential capacity
		cJSON* totalPotentialCapacityObj = cJSON_GetObjectItem(item, "total-potential-capacity");
		if (cJSON_IsObject(totalPotentialCapacityObj))
		{
			parse_capacity_object(totalPotentialCapacityObj, &l->potential_capacity);
			//DEBUG_PC("Link (%d) -- Potential Capacity: %f", i + 1, l->potential_capacity.value);
			print_capacity_unit(l->potential_capacity.unit);
		}
		// total available capacity
		cJSON* availableCapacityObj = cJSON_GetObjectItem(item, "available-capacity");
		if (cJSON_IsObject(availableCapacityObj))
		{
			parse_capacity_object(availableCapacityObj, &l->available_capacity);
			//DEBUG_PC("Link (%d) -- Available Capacity: %f", i + 1, l->available_capacity.value);
			print_capacity_unit(l->available_capacity.unit);
		}
		// Cost Characteristics
		cJSON* costCharacObj = cJSON_GetObjectItem(item, "cost-characteristics");
		if (cJSON_IsObject(costCharacObj)) {
			// Cost Name
			cJSON* costNameObj = cJSON_GetObjectItem(costCharacObj, "cost-name");
			if (cJSON_IsString(costNameObj)) {
				duplicate_string(l->cost_characteristics.cost_name, costNameObj->valuestring);
				//DEBUG_PC("Link (%d) -- Cost Name: %s", i + 1, l->cost_characteristics.cost_name);
			}
			// Cost value
			cJSON* costValueObj = cJSON_GetObjectItem(costCharacObj, "cost-value");
			if (cJSON_IsString(costValueObj)) {
				char* endpr;
				l->cost_characteristics.cost_value = (gdouble)(strtod(costValueObj->valuestring, &endpr));
				//DEBUG_PC("Link (%d) -- Cost Value: %f", i + 1, l->cost_characteristics.cost_value);
			}
			// Cost Algorithm
			cJSON* costAlgObj = cJSON_GetObjectItem(costCharacObj, "cost-algorithm");
			if (cJSON_IsString(costAlgObj)) {
				char* endpr;
				l->cost_characteristics.cost_algorithm = (gdouble)(strtod(costAlgObj->valuestring, &endpr));
				//DEBUG_PC("Link (%d) -- Cost Algorithm: %f", i + 1, l->cost_characteristics.cost_algorithm);
			}
		}
		// Latency Characteristics
		cJSON* latencyCharacObj = cJSON_GetObjectItem(item, "latency-characteristics");
		if (cJSON_IsObject(latencyCharacObj)) {
			cJSON* fixedLatencyCharacObj = cJSON_GetObjectItem(latencyCharacObj, "fixed-latency-characteristic");
			if (cJSON_IsString(fixedLatencyCharacObj)) {
				char* endpr;
				l->latency_characteristics.fixed_latency = (gdouble)(strtod(fixedLatencyCharacObj->valuestring, &endpr));
				//DEBUG_PC("Link (%d) -- Latency: %f", i + 1, l->latency_characteristics.fixed_latency);
			}	
		}
martinezric's avatar
martinezric committed
		linkList = g_list_append(linkList, l);
///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to generate the reverse (unidirecitonal) link from those being learnt
 *  from the received context
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 ////////////////////////////////////////////////////////////////////////////////////////
void generate_reverse_linkList() {
	DEBUG_PC("");
	DEBUG_PC("CREATION OF REVERSE LINKS");
martinezric's avatar
martinezric committed
	gint numLinks = g_list_length (linkList);
	DEBUG_PC("Initial Number of links in the main List: %d", numLinks);
	gint i = 0;
	for (GList* ln = g_list_first(linkList);
		(ln) && (i < numLinks);
		ln = g_list_next(ln), i++)
	{
		struct link_t* refLink = (struct link_t*)(ln->data);
		struct link_t* newLink = g_malloc0(sizeof(struct link_t));
		if (newLink == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		// Copy the linkId + appending "_rev"
		duplicate_string(newLink->linkId, refLink->linkId);
martinezric's avatar
martinezric committed
		strcat(newLink->linkId, "_rev"); 		

		//DEBUG_PC("refLink: %s // newLink: %s", refLink->linkId, newLink->linkId);

		// Assumption: p2p links. The newLink endpoints are the reversed ones form the reference Link (refLink)
		// i.e., refLink A->B, then newLink B->A
		//DEBUG_PC("ref: %s has %d endpoints", refLink->linkId, refLink->numLinkEndPointIds);
#if 1
		if (refLink->numLinkEndPointIds != 2) {
			DEBUG_PC("To construct the new Link from ref: %s, 2 EndPoints are a MUST", refLink->linkId);
martinezric's avatar
martinezric committed
		//DEBUG_PC(" * Link[%d] -- Id: %s", numLinks + i, newLink->linkId);
		//DEBUG_PC("Number of Endpoints in Link: %d", refLink->numLinkEndPointIds);
		for (gint j = refLink->numLinkEndPointIds - 1, m = 0; j >= 0; j--, m++) {			
			struct link_endpointId_t* refEndPId = &(refLink->linkEndPointId[j]);
			struct link_endpointId_t* newEndPId = &(newLink->linkEndPointId[m]);
			// Duplicate the topologyId information, i.e., contextId and topology_uuid
			duplicate_string(newEndPId->topology_id.contextId, refEndPId->topology_id.contextId);
			duplicate_string(newEndPId->topology_id.topology_uuid, refEndPId->topology_id.topology_uuid);
			//duplicate the deviceId and endPoint_uuid
martinezric's avatar
martinezric committed
			duplicate_string(newEndPId->deviceId, refEndPId->endPointId);
			duplicate_string(newEndPId->endPointId, refEndPId->deviceId);
			//DEBUG_PC("refLink Endpoint[%d]: %s(%s)", j, refEndPId->deviceId, refEndPId->endPointId);
			//DEBUG_PC("newLink Endpoint[%d]: %s(%s)", m, newEndPId->deviceId, newEndPId->endPointId);
			newLink->numLinkEndPointIds++;
		}

		// duplicate forwarding direction
		newLink->forwarding_direction = refLink->forwarding_direction;

		// duplicate capacity attributes
		memcpy(&newLink->potential_capacity.value, &refLink->potential_capacity.value, sizeof(gdouble));
		newLink->potential_capacity.unit = refLink->potential_capacity.unit;

		memcpy(&newLink->available_capacity.value, &refLink->available_capacity.value, sizeof(gdouble));
		newLink->available_capacity.unit = refLink->available_capacity.unit;

		// duplicate cost characteristics
		memcpy(&newLink->cost_characteristics.cost_value, &refLink->cost_characteristics.cost_value, sizeof(gdouble));
		memcpy(&newLink->cost_characteristics.cost_algorithm, &refLink->cost_characteristics.cost_algorithm, sizeof(gdouble));
		duplicate_string(newLink->cost_characteristics.cost_name, refLink->cost_characteristics.cost_name);

		// duplicate latency characteristics
		memcpy(&newLink->latency_characteristics.fixed_latency, &refLink->latency_characteristics.fixed_latency, sizeof(gdouble));
martinezric's avatar
martinezric committed
		// Append in the linkList the new creted Link
		linkList = g_list_append(linkList, newLink);
martinezric's avatar
martinezric committed
	DEBUG_PC("Terminating Reverse Links [total links: %d]", g_list_length(linkList));
martinezric's avatar
martinezric committed
///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON object/s for active services
 *
 * 	@param actServiceArray
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_activeService_array(cJSON* actServiceArray) {
	DEBUG_PC("");
	DEBUG_PC("====== PARSING THE JSON CONTENTS OF THE ACTIVE SERVICES =======");
	
	for (gint i = 0; i < cJSON_GetArraySize(actServiceArray); i++) {
		struct activeService_t* actServ = g_malloc0(sizeof(struct activeService_t));
		if (actServ == NULL) {
			DEBUG_PC("Memory Allocation Failure");
			exit(-1);
		}
		cJSON* item = cJSON_GetArrayItem(actServiceArray, i);
		// ServiceId
		cJSON* serviceIdObj = cJSON_GetObjectItem(item, "serviceId");
		if (cJSON_IsObject(serviceIdObj)) {
			parse_json_serviceId(serviceIdObj, &actServ->serviceId);
		}
		// Service Type
		cJSON* serviceTypeObj = cJSON_GetObjectItem(item, "serviceType");
		if (cJSON_IsNumber(serviceTypeObj))
		{
			actServ->service_type = (guint)(serviceTypeObj->valuedouble);
			print_service_type(actServ->service_type);
		}
		// Service Endpoints
		cJSON* endPointIdsArray = cJSON_GetObjectItem(item, "service_endpoints_ids");
		if (cJSON_IsArray(endPointIdsArray)) {
			parse_act_service_endPointsIds_array(endPointIdsArray, actServ);
		}
		// Parsing the active service path
		actServ->activeServPath = NULL;
		cJSON* actServPathArray = cJSON_GetObjectItem(item, "devices");
		if (cJSON_IsArray(endPointIdsArray)) {
			for (gint j = 0; j < cJSON_GetArraySize(actServPathArray); j++) {
				struct activeServPath_t* actServPath = g_malloc0(sizeof(struct activeServPath_t));
				if (actServPath == NULL) {
					DEBUG_PC("Memory Allocation Failure");
					exit(-1);
				}
				cJSON* item2 = cJSON_GetArrayItem(item, j);
				// Topology Id
				cJSON* topologyIdObj = cJSON_GetObjectItem(item2, "topology_id");
				if (cJSON_IsObject(topologyIdObj)) {
					parse_topology_Id(topologyIdObj, &actServPath->topology_id);
				}
				// Device Id
				cJSON* deviceIdObj = cJSON_GetObjectItem(item2, "device_id");
				if (cJSON_IsString(deviceIdObj)) {
					duplicate_string(actServPath->deviceId, deviceIdObj->valuestring);
				}
				// EndPointId
				cJSON* endPointUUIDObj = cJSON_GetObjectItem(item2, "endpoint_uuid");
				if (cJSON_IsString(endPointUUIDObj)) {
					duplicate_string(actServPath->endPointId, endPointUUIDObj->valuestring);
				}
				// Append element from the Active Service Path (i.e.,topologyId, deviceId and endpointId)
				actServ->activeServPath = g_list_append(actServ->activeServPath, actServPath);
			}
		}
		// append into the Actice Service List
		activeServList = g_list_append(activeServList, actServ);
	}
	return;
}
///////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to parse the JSON object/s for the PATH COMP request (i.e. service
 *  requests, device and links)
 * 	
 * 	@param root
 * 	@param source
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
void parsing_json_obj_pathComp_request(cJSON * root, GIOChannel * source)
{
	// Set of services to seek their path and resource selection
	cJSON* serviceListArray = cJSON_GetObjectItem(root, "serviceList");
	if (cJSON_IsArray(serviceListArray)) {
		parsing_json_serviceList_array(serviceListArray);
	}   
    
	// Get the deviceList
	cJSON* deviceListArray = cJSON_GetObjectItem(root, "deviceList");
	if (cJSON_IsArray(deviceListArray)) {
		parsing_json_deviceList_array(deviceListArray);
	}

	// Get the linkList
	cJSON* linkListArray = cJSON_GetObjectItem(root, "linkList");
	if (cJSON_IsArray(linkListArray)) {
		parsing_json_linkList_array(linkListArray);

		// In the context information, if solely the list of links are passed for a single direction, 
		// the reverse direction MUST be created sythetically 
		// LGR: deactivated; link duplication needs to be done smartly with TAPI. done manually in topology by now
martinezric's avatar
martinezric committed
		//generate_reverse_linkList();
	}

	// Get the list of active services
	cJSON* actServiceArray = cJSON_GetObjectItem(root, "activeServList");
	if (cJSON_IsArray(actServiceArray)) {
		parsing_json_activeService_array(actServiceArray);
	}
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used parse the JSON object/s 
 * 	
 * 	@param data
 *  @param source
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint parsing_json_obj (guchar *data, GIOChannel *source) {
    cJSON * root = cJSON_Parse((const char *)data);
    char * print = cJSON_Print(root);  

	DEBUG_PC("STARTING PARSING JSON CONTENTS");
	parsing_json_obj_pathComp_request (root, source);
	DEBUG_PC("ENDING PARSING JSON CONTENTS");
	// Release the root JSON object variable
	cJSON_free (root);
	g_free(print);
    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Create new tcp client connected to PATH COMP
 * 
 * 	@param channel_client, GIOChannel
 *  @param fd
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
struct pathComp_client * RESTapi_client_create (GIOChannel * channel_client, gint fd) {
	/** check values */
	g_assert(channel_client != NULL); 

	struct pathComp_client* client = g_malloc0 (sizeof (struct pathComp_client));
	if (client == NULL )
	{
		DEBUG_PC ("Malloc for the client failed");
		exit(-1);
	}  

	/** Make client input/output buffer. */
	client->channel = channel_client;	
	client->obuf = stream_new(MAXLENGTH);
	client->ibuf = stream_new(MAXLENGTH);
	client->fd = fd;

	// Clients connected to the PATH COMP SERVER
	CLIENT_ID++;
	client->type = CLIENT_ID;

	//DEBUG_PC ("Client Id: %u is created (%p)", client->type, client);
	//DEBUG_PC ("Client ibuf: %p || obuf: %p", client->ibuf, client->obuf);

	// Add the tcp client to the list
	RESTapi_tcp_client_list = g_list_append (RESTapi_tcp_client_list, client);
	//DEBUG_PC ("Num of TCP Clients: %d", g_list_length (RESTapi_tcp_client_list));
	return client;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Close the tcp client, removing from the rapi_tcp_client_list
 * 
 * 	@param client
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
void RESTapi_client_close (struct pathComp_client* client) {
	//DEBUG_PC("Closing the client (Id: %d) %p", client->type, client);
	//DEBUG_PC("Client ibuf: %p || obuf: %p", client->ibuf, client->obuf);
	
martinezric's avatar
martinezric committed
	if (client->ibuf != NULL) {
		//DEBUG_PC("Client ibuf: %p", client->ibuf);
		stream_free(client->ibuf);
		client->ibuf = NULL;
	}
martinezric's avatar
martinezric committed
	if (client->obuf != NULL) {
		//DEBUG_PC("Client obuf: %p", client->obuf);
		stream_free(client->obuf);
		client->obuf = NULL;
	}
	// Remove from the list
	RESTapi_tcp_client_list = g_list_remove (RESTapi_tcp_client_list, client);
	//DEBUG_PC ("TCP Client List: %d", g_list_length(RESTapi_tcp_client_list));
	 
	g_free (client);
	client = NULL;	 
	DEBUG_PC ("client has been removed ...");	 
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Close operations over the passed tcp channel
 * 
 * 	@param source
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
void RESTapi_close_operations (GIOChannel * source)	{
	gint fd = g_io_channel_unix_get_fd (source);
	
	//DEBUG_PC ("Stop all the operations over the fd: %d", fd);	
	g_io_channel_flush(source, NULL);
	GError *error = NULL;    
	g_io_channel_shutdown (source, TRUE, &error);
martinezric's avatar
martinezric committed
	if(error) {
		DEBUG_PC ("An error occurred ...");
	}
	g_io_channel_unref (source);
	return;	
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Remove the client and close operations over the TCP connection
 * 
 * 	@param client
 *  @param source
 *  @param fd
 * 	
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
void RESTapi_stop (struct pathComp_client* client, GIOChannel * source, gint fd) {
	
	DEBUG_PC("Client Socket: %d is Stopped", fd);
	// remove client
	RESTapi_client_close(client);
	// Stop operations over that channel
	RESTapi_close_operations(source);
	close (fd);
	return;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the different lines ending up in \r\n
 * 	
 * 	@param s
 * 	@param buf
 * 	@param size
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
gint RESTapi_get_line (GIOChannel *channel, gchar *buf, gint size) {
    gint i = 0;
    //DEBUG_PC ("\n");
    //DEBUG_PC ("----- Read REST API Line(\r\n) ------");
    gint n = 0;
    guchar c = '\0'; // END OF FILE    
    gboolean cr = FALSE;
martinezric's avatar
martinezric committed
    while (i < size - 1) {
		n = read_channel (channel, &c, 1);		
martinezric's avatar
martinezric committed
		if (n == -1) {
			//DEBUG_PC ("Close the channel and eliminate the client");
			return -1;			
		}	
martinezric's avatar
martinezric committed
		if (n > 0) {
			//DEBUG_PC ("%c", c);
			buf[i] = c;
			i++;	
martinezric's avatar
martinezric committed
			if (c == '\r') 			{
martinezric's avatar
martinezric committed
			if ((c == '\n') && (cr == TRUE)) 			{	   
martinezric's avatar
martinezric committed
		else {
			c = '\n';
			buf[i] = c;
			i++;
			break;
		}
    }
    buf[i] = '\0';    
    //DEBUG_PC ("Line (size: %d) buf: %s", i, buf);
    return i;
}  

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the HTTP method
 * 	
 * 	@param buf
 * 	@param j
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
guint RESTapi_get_method (gchar *buf, gint *j)
{
	guint RestApiMethod = 0;
	gchar method[255];
	gint i = 0;	
martinezric's avatar
martinezric committed
	while (!ISspace(buf[*j]) && (i < sizeof(method) - 1)) {
		method[i] = buf[*j];
		i++; 
		*j = *j + 1;
	}
	method[i] = '\0';
	DEBUG_PC ("REST API METHOD: %s", method);	
	
	// Check that the methods are GET, POST or PUT
	if (strcasecmp((const char *)method, "GET") && strcasecmp((const char *)method, "POST") && 
martinezric's avatar
martinezric committed
		strcasecmp ((const char *)method, "HTTP/1.1") && strcasecmp ((const char *)method, "PUT")) {
		DEBUG_PC ("%s is not a method ...", method);
		return RestApiMethod;	
	}
	// Method selector
martinezric's avatar
martinezric committed
	if (strncmp ((const char*)method, "GET", 3) == 0) {
		RestApiMethod = REST_API_METHOD_GET;		
	}
martinezric's avatar
martinezric committed
	else if (strncmp ((const char*)method, "POST", 4) == 0) {
		RestApiMethod = REST_API_METHOD_POST;
	}	
martinezric's avatar
martinezric committed
	else if (strncmp ((const char *)method, "HTTP/1.1", 8) == 0) {
		RestApiMethod = REST_API_METHOD_HTTP;
	}
martinezric's avatar
martinezric committed
	else if (strncmp ((const char *)method, "PUT", 3) == 0) {
		RestApiMethod = REST_API_METHOD_PUT;
martinezric's avatar
martinezric committed
	}	
martinezric's avatar
martinezric committed
////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to check whether it is a supported method, and return the associated numerical id
 *
 * 	@param method
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
 /////////////////////////////////////////////////////////////////////////////////////////
guint is_rest_api_method(char *method) {
	guint RestApiMethod = 0;
	if (strcasecmp((const char*)method, "GET") && strcasecmp((const char*)method, "POST") &&
		strcasecmp((const char*)method, "HTTP/1.1") && strcasecmp((const char*)method, "PUT")) {
		DEBUG_PC("The method: %s is not currently supported ...", method);
		return RestApiMethod;
	}
	// Method selector
	if (strncmp((const char*)method, "GET", 3) == 0) {
		RestApiMethod = REST_API_METHOD_GET;
	}
	else if (strncmp((const char*)method, "POST", 4) == 0) {
		RestApiMethod = REST_API_METHOD_POST;
	}
	else if (strncmp((const char*)method, "HTTP/1.1", 8) == 0) {
		RestApiMethod = REST_API_METHOD_HTTP;
	}
	else if (strncmp((const char*)method, "PUT", 3) == 0) {
		RestApiMethod = REST_API_METHOD_PUT;
	}
	return RestApiMethod;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the url
 * 	
 * 	@param buf
 * 	@param j
 *  @param url
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint get_url (gchar *buf, gint *j, gchar *url)
{
	// Skip space char
	while (ISspace(buf[*j]) && (*j < strlen(buf))) {
		*j = *j + 1;
	}
	
	//DEBUG_PC ("buf[%d]: %c", *j, buf[*j]);
	int result = isspace (buf[*j]);	
	*buf = *buf + *j;
	gint numChar = 0;
	gint initChar = *j;
	result = 0;
	while (result == 0)	{
		*j = *j + 1;
		result = isspace (buf[*j]);
		numChar++;
	}
	//DEBUG_PC ("numChar: %d", numChar);
	memcpy (url, buf + initChar, numChar);
	url[numChar] = '\0';
	//DEBUG_PC ("url: %s", url);
	return numChar;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used read the version
 * 	
 * 	@param buf
 * 	@param j
 *  @param version
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint get_version (gchar *buf, gint *j, gchar *version) {
	// Skip space char
martinezric's avatar
martinezric committed
	while (ISspace(buf[*j]) && (*j < strlen(buf))) {
		*j = *j + 1;
	}	
	//DEBUG_PC ("buf[%d]: %c", *j, buf[*j]);
	int result = isspace (buf[*j]);	
	*buf = *buf + *j;
	gint numChar = 0;
	gint initChar = *j;
	result = 0;
	while (result == 0)	{
		*j = *j + 1;
		result = isspace (buf[*j]);
		numChar++;
	}
	//DEBUG_PC ("numChar: %d", numChar);
	memcpy (version, buf + initChar, numChar);
	version[numChar] = '\0';
	//DEBUG_PC ("version: %s", version);
	return numChar;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to trigger the route computation for the network connectivity service
 *  List and retrieve the result
 * 	
 * 	@param compRouteList
 * 	@param raId
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
gint triggering_routeComp (struct compRouteOutputList_t *compRouteList, gchar *algId) {
	g_assert (compRouteList);	
	gint httpCode = HTTP_RETURN_CODE_OK;
	DEBUG_PC("Requested Algorithm: %s", algId);
	//////////////////// Algorithm Selector (RAId)//////////////////////////////////////	
	// KSP algorithm
martinezric's avatar
martinezric committed
	if (strncmp ((const char*)algId, "KSP", 3) == 0) {
		DEBUG_PC ("Alg Id: KSP");
		httpCode = pathComp_ksp_alg(compRouteList);
	}
	// simple SP algorithm
	else if (strncmp((const char*)algId, "SP", 2) == 0) {
		DEBUG_PC("Alg Id: SP");
		httpCode = pathComp_sp_alg(compRouteList);
	}
martinezric's avatar
martinezric committed
	// energy-aware routing
	else if (strncmp((const char*)algId, "EAR", 3) == 0) {
		DEBUG_PC("Alg Id: Energy Aware Routing, EAR");
		httpCode = pathComp_ear_alg(compRouteList);
	}
	return httpCode;
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to process the REST API commands
 * 	
 * 	@param source
 * 	@param cond
 * 	@param data
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
gboolean RESTapi_activity(GIOChannel *source, GIOCondition cond, gpointer data) {  
	/** some checks */
	g_assert(source != NULL);
	g_assert(data != NULL);	
	
	gchar buf[1024];
	gchar version[255];
	gchar http_result[255];
	gint body_length = 0;	

	struct pathComp_client *client = (struct pathComp_client*)(data);
	DEBUG_PC (" ************************************************************************** ");    
	DEBUG_PC ("                      REST API ACTIVITY Triggered ");
	DEBUG_PC (" ************************************************************************** ");   

	gint fd = g_io_channel_unix_get_fd (source);
	DEBUG_PC ("fd: %d, cond: %d", fd, cond);

martinezric's avatar
martinezric committed
	if (cond != G_IO_IN) {
		DEBUG_PC ("Something happening with the channel and fd ... (cond: %d)", cond);
		RESTapi_stop(client, source, fd);
		return FALSE;
	}	
martinezric's avatar
martinezric committed
	// Clear input buffer
	stream_reset (client->ibuf);

	// get line
	gint nbytes = RESTapi_get_line (source, buf, sizeof (buf));
martinezric's avatar
martinezric committed
	if (nbytes == -1) {
		DEBUG_PC ("nbytes -1 ... CLOSE CLIENT FD and eliminate CLIENT");						
		RESTapi_stop(client, source, fd);
		return FALSE;						
martinezric's avatar
martinezric committed
	}	
	if ((buf[0] == '\n') && (nbytes  == 1))
	{
		//DEBUG_PC (" -- buf[0] = newline --");
		RESTapi_stop(client, source, fd);
		return FALSE;
	}
	
	gint i = 0, j = 0;

martinezric's avatar
martinezric committed
	while (1) {
		DEBUG_PC("%s", buf);
		char word[255];		
		while (!ISspace(buf[j]) && (i < sizeof(word) - 1)) {
			word[i] = buf[j]; i++; j++;
martinezric's avatar
martinezric committed
		word[i] = '\0';
		// Check if word is bound to a Method, i.e., POST, GET, HTTP/1.1.
		guint method = is_rest_api_method(word);
		if (method == 0) {
			 // ignore other REST fields i.e., Host:, User-Agent:, Accept: ....			
			break;
martinezric's avatar
martinezric committed
		// word is bound to a known / supported REST Method
		else {
			gchar url[255];
			i = get_url(buf, &j, url);
			url[i] = '\0';
			// GET - used for checking status of pathComp ... used url /pathComp/api/v1/health
			if (method == REST_API_METHOD_GET) {
				if (strncmp((const char*)url, "/health", 7) != 0) {
					DEBUG_PC("unknown url [%s] for GET method -- Heatlh function", url);
					RESTapi_stop(client, source, fd);
					exit(-1);
				}
				else {
					DEBUG_PC("Sending API Response OK to health requests");
					rapi_response_ok(source, HTTP_RETURN_CODE_OK, NULL);
					return TRUE;
				}
			}
			// for method POST, PUT check that the url is "/pathComp"
			if (method == REST_API_METHOD_POST) {
				if (strncmp((const char*)url, "/pathComp/api/v1/compRoute", 26) != 0) {
					DEBUG_PC("Unknown url: %s", url);
					RESTapi_stop(client, source, fd);
					exit(-1);
				}
			}
			// get the version	
			i = get_version(buf, &j, version);
			version[i] = '\0';
			break;
	}
	// Assume HTTP/1.1, then there is Host Header
	memset(buf, '\0', sizeof(buf));        
	nbytes = RESTapi_get_line(source, buf, sizeof (buf));
martinezric's avatar
martinezric committed
	if (nbytes == -1) {
		DEBUG_PC ("nbytes -1 ... then close the fd and eliminate associated client");			
		RESTapi_stop(client, source, fd);
		return FALSE;					
martinezric's avatar
martinezric committed
	}	
	
	// Headers --- The Header Fields ends up with a void line (i.e., \r\n)
martinezric's avatar
martinezric committed
	while ((nbytes > 0) && (strcmp ("\r\n", (const char *)buf) != 0)) {	
		/* read & discard headers */
		memset(buf, '\0', sizeof(buf));  
		nbytes = RESTapi_get_line (source, buf, sizeof (buf));
martinezric's avatar
martinezric committed
		if (nbytes == -1) {
			DEBUG_PC ("nbytes -1 ... then close the fd and eliminate associated client");	
			RESTapi_stop(client, source, fd);
			return FALSE;
		}
		//DEBUG_PC ("Header: %s", buf);	  
martinezric's avatar
martinezric committed
		if (strncmp ((const char *)buf, "Content-Length:", 15) == 0) {
			//DEBUG_PC ("Header Content-Length Found");
			gchar str[20];
	  
			gint i = 15, k = 0;  // "Content-Length:" We skip the first 16 characters to directly retrieve the length in bytes of the Body of Request
			gchar contentLength[255];
			memset (contentLength, '\0', sizeof (contentLength));			
martinezric's avatar
martinezric committed
			while (buf[i] != '\r') {
				//DEBUG_PC ("%c", buf[i]);
				str[k] = buf[i];
				k++, i++;
			}
			str[k] = '\0';			
			j = 0, i = 0;
martinezric's avatar
martinezric committed
			while (ISspace(str[j]) && (j < strlen(str))) {
martinezric's avatar
martinezric committed
			while (j < strlen(str)) {
				contentLength[i] = str[j];
				i++; j++;
			}
			contentLength[i] = '\0';			
			body_length = atoi (contentLength);
			DEBUG_PC ("Body length: %d (%s) in Bytes", body_length, contentLength);
	DEBUG_PC("Read Entire HTTP Header");
martinezric's avatar
martinezric committed
	if (body_length == 0) {
		DEBUG_PC ("--- NO REST API Body length (length = %d) ---", body_length);
		return TRUE;
	}       
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Processing Body of the Request
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////
	DEBUG_PC ("REST API Request - Body -");
	nbytes = read_channel (source, (guchar *)(client->ibuf->data + client->ibuf->putp), body_length);
martinezric's avatar
martinezric committed
	if ((nbytes < 0) && (body_length > 0)) 	{
		DEBUG_PC ("nbytes: %d; body_length: %d", nbytes, body_length);
		exit (-1);
martinezric's avatar
martinezric committed
	}	
	client->ibuf->putp += nbytes;
	client->ibuf->endp += nbytes;		
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Parsing the contents of the Request
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
	// build the device list	
	deviceList = NULL;
martinezric's avatar
martinezric committed
	linkList = NULL;
	// Create the network connectivity service list
martinezric's avatar
martinezric committed
	serviceList = NULL;
	// Create the active service List
	activeServList = NULL;
	DEBUG_PC("Parsing JSON...");
	// Process the json contents and store relevant information at Device, Link,
	// and network connectivity service
	gint ret = parsing_json_obj (client->ibuf->data, source);	
	if (ret == -1) 	{
		DEBUG_PC ("Something wrong with the JSON Objects ... ");		
		RESTapi_stop(client, source, fd);
		return FALSE;
	}
	DEBUG_PC("Parsing done");
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////		
	// Trigger the path computation	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	DEBUG_PC ("Triggering the computation");
	struct compRouteOutputList_t *compRouteOutputList = create_route_list ();
	gint httpCode = triggering_routeComp (compRouteOutputList, algId);	

	// Send the response to the REST  API Client
	if (httpCode != HTTP_RETURN_CODE_OK) {
		DEBUG_PC ("HTTP CODE: %d -- NO OK", httpCode);
		rapi_response (source, httpCode);
	}
martinezric's avatar
martinezric committed
	else {
		DEBUG_PC ("HTTP CODE: %d -- OK", httpCode);
		rapi_response_ok (source, httpCode, compRouteOutputList);            
	}
	
	// Release the variables		
martinezric's avatar
martinezric committed
	g_free (compRouteOutputList);	
	g_list_free_full(g_steal_pointer(&linkList), (GDestroyNotify)destroy_link);
	g_list_free_full(g_steal_pointer(&deviceList), (GDestroyNotify)destroy_device);
	g_list_free_full(g_steal_pointer(&serviceList), (GDestroyNotify)destroy_requested_service);
	g_list_free_full(g_steal_pointer(&activeServList), (GDestroyNotify)destroy_active_service);
	return TRUE;  
}

////////////////////////////////////////////////////////////////////////////////////////
/**
 * 	@file pathComp_RESTapi.c
 * 	@brief Function used to accept a new connection and add the client to list of clients
 * 
 * 	@param source, GIOChannel
 * 	@param cond, GIOCondition
 * 	@param data, gpointer
 *
 *	@author Ricardo Martínez <ricardo.martinez@cttc.es>
 *	@date 2022
 */
/////////////////////////////////////////////////////////////////////////////////////////
martinezric's avatar
martinezric committed
gboolean RESTapi_tcp_new_connection(GIOChannel *source, GIOCondition cond, gpointer data) {
	DEBUG_PC (" ****** New TCP Connection (REST API) ******");
	/** get size of client_addre structure */