1 /*
2  * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "restapi_location_strategy.h"
8 
9 #include <assert.h>
10 #include <string.h>
11 #include <unistd.h>
12 
13 #include "restapi_location.h"
14 #include "restapi_service_context.h"
15 #include "rpc/http/caller/http_caller.h"
16 #include "service/locator/service_name.h"
17 #include "trace.h"
18 
19 /* Maximum wait for an unresponsive API server. During testing, this period
20  * may need to accommodate the boot time for a device under test.
21  */
22 #ifndef RESTAPI_LOCATOR_MAX_API_WAIT
23 #define RESTAPI_LOCATOR_MAX_API_WAIT (120)
24 #endif
25 
probe_api_server(void)26 static bool probe_api_server(void)
27 {
28 	unsigned int seconds_waited = 0;
29 
30 	do {
31 		long http_code = 0;
32 
33 		if (http_caller_probe(RESTAPI_LOCATOR_API_URL, &http_code))
34 			return true;
35 
36 		/* Failed to reach API or received an error response */
37 		if (http_code == 0) {
38 			/* It's possible that the device hosting the API server is in the
39 			 * process of booting up so it's worth waiting and trying again.
40 			 */
41 			sleep(1);
42 			++seconds_waited;
43 
44 		} else {
45 			/* The server was reached but it returned an error */
46 			EMSG("API server HTTP error: %ld", http_code);
47 			return false;
48 		}
49 
50 	} while (seconds_waited < RESTAPI_LOCATOR_MAX_API_WAIT);
51 
52 	IMSG("API server not reachable");
53 
54 	return false;
55 }
56 
prepare_service_url(const char * sn,char * url_buf,size_t url_buf_size)57 static void prepare_service_url(const char *sn, char *url_buf, size_t url_buf_size)
58 {
59 	strncpy(url_buf, RESTAPI_LOCATOR_API_URL, url_buf_size);
60 
61 	size_t url_len = strnlen(url_buf, url_buf_size);
62 
63 	assert(url_len < url_buf_size);
64 
65 	size_t remaining_space = url_buf_size - url_len;
66 
67 	url_len += sn_read_service(sn, &url_buf[url_len], remaining_space);
68 
69 	assert(url_len < url_buf_size - 1);
70 
71 	url_buf[url_len] = '/';
72 	url_buf[url_len + 1] = '\0';
73 }
74 
query(const char * sn,int * status)75 static struct service_context *query(const char *sn, int *status)
76 {
77 	*status = -1;
78 
79 	if (!probe_api_server())
80 		return NULL;
81 
82 	/* API server reachable so check if service endpoint exists */
83 	char service_url[HTTP_CALLER_MAX_URL_LEN];
84 	long http_code = 0;
85 
86 	prepare_service_url(sn, service_url, sizeof(service_url));
87 
88 	if (!http_caller_probe(service_url, &http_code)) {
89 		if (http_code != 404)
90 			EMSG("Unexpected HTTP error: %ld", http_code);
91 
92 		/* Service endpoint not reachable */
93 		return NULL;
94 	}
95 
96 	return restapi_service_context_create(service_url);
97 }
98 
restapi_location_strategy(void)99 const struct service_location_strategy *restapi_location_strategy(void)
100 {
101 	static const struct service_location_strategy strategy = { query };
102 
103 	return &strategy;
104 }
105