1 /*
2  * Copyright (c) 2022 Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_INCLUDE_NET_HTTP_SERVICE_H_
8 #define ZEPHYR_INCLUDE_NET_HTTP_SERVICE_H_
9 
10 /**
11  * @file service.h
12  *
13  * @brief HTTP service API
14  *
15  * @defgroup http_service HTTP service API
16  * @since 3.4
17  * @version 0.1.0
18  * @ingroup networking
19  * @{
20  */
21 
22 #include "zephyr/net/http/server.h"
23 #include <stdint.h>
24 #include <stddef.h>
25 
26 #include <zephyr/sys/util_macro.h>
27 #include <zephyr/sys/iterable_sections.h>
28 #include <zephyr/net/tls_credentials.h>
29 
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 /** HTTP resource description */
35 struct http_resource_desc {
36 	/** Resource name */
37 	const char *resource;
38 	/** Detail associated with this resource */
39 	void *detail;
40 };
41 
42 /**
43  * @brief Define a static HTTP resource
44  *
45  * A static HTTP resource is one that is known prior to system initialization. In contrast,
46  * dynamic resources may be discovered upon system initialization. Dynamic resources may also be
47  * inserted, or removed by events originating internally or externally to the system at runtime.
48  *
49  * @note The @p _resource is the URL without the associated protocol, host, or URL parameters. E.g.
50  * the resource for `http://www.foo.com/bar/baz.html#param1=value1` would be `/bar/baz.html`. It
51  * is often referred to as the "path" of the URL. Every `(service, resource)` pair should be
52  * unique. The @p _resource must be non-NULL.
53  *
54  * @param _name Name of the resource.
55  * @param _service Name of the associated service.
56  * @param _resource Pathname-like string identifying the resource.
57  * @param _detail Implementation-specific detail associated with the resource.
58  */
59 #define HTTP_RESOURCE_DEFINE(_name, _service, _resource, _detail)                                  \
60 	const STRUCT_SECTION_ITERABLE_ALTERNATE(http_resource_desc_##_service, http_resource_desc, \
61 						_name) = {                                         \
62 		.resource = _resource,                                                             \
63 		.detail = (void *)(_detail),                                                       \
64 	}
65 
66 /** @cond INTERNAL_HIDDEN */
67 
68 struct http_service_desc {
69 	const char *host;
70 	uint16_t *port;
71 	int *fd;
72 	void *detail;
73 	size_t concurrent;
74 	size_t backlog;
75 	struct http_resource_desc *res_begin;
76 	struct http_resource_desc *res_end;
77 	struct http_resource_detail *res_fallback;
78 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
79 	const sec_tag_t *sec_tag_list;
80 	size_t sec_tag_list_size;
81 #endif
82 };
83 
84 #define __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail,               \
85 				_res_fallback, _res_begin,                                         \
86 				_res_end, ...)                                                     \
87 	static int _name##_fd = -1;                                                                \
88 	const STRUCT_SECTION_ITERABLE(http_service_desc, _name) = {                                \
89 		.host = _host,                                                                     \
90 		.port = (uint16_t *)(_port),                                                       \
91 		.fd = &_name##_fd,                                                                 \
92 		.detail = (void *)(_detail),                                                       \
93 		.concurrent = (_concurrent),                                                       \
94 		.backlog = (_backlog),                                                             \
95 		.res_begin = (_res_begin),                                                         \
96 		.res_end = (_res_end),                                                             \
97 		.res_fallback = (_res_fallback),                                                   \
98 		COND_CODE_1(CONFIG_NET_SOCKETS_SOCKOPT_TLS,                                        \
99 			    (.sec_tag_list = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (NULL),  \
100 							 (GET_ARG_N(1, __VA_ARGS__))),), ())       \
101 		COND_CODE_1(CONFIG_NET_SOCKETS_SOCKOPT_TLS,                                        \
102 			    (.sec_tag_list_size = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (0),\
103 					     (GET_ARG_N(1, GET_ARGS_LESS_N(1, __VA_ARGS__))))), ())\
104 	}
105 
106 /** @endcond */
107 
108 /**
109  * @brief Define an HTTP service without static resources.
110  *
111  * @note The @p _host parameter is used to specify an IP address either in
112  * IPv4 or IPv6 format a fully-qualified hostname or a virtual host. If left NULL, the listening
113  * port will listen on all addresses.
114  *
115  * @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
116  * number to use for the service. If the specified port number is zero, then an ephemeral port
117  * number will be used and the actual port number assigned will be written back to memory. For
118  * ephemeral port numbers, the memory pointed to by @p _port must be writeable.
119  *
120  * @param _name Name of the service.
121  * @param _host IP address or hostname associated with the service.
122  * @param[inout] _port Pointer to port associated with the service.
123  * @param _concurrent Maximum number of concurrent clients.
124  * @param _backlog Maximum number queued connections.
125  * @param _detail User-defined detail associated with the service.
126  * @param _res_fallback Fallback resource to be served if no other resource matches path
127  */
128 #define HTTP_SERVICE_DEFINE_EMPTY(_name, _host, _port, _concurrent, _backlog, _detail,             \
129 				  _res_fallback)                                                   \
130 	__z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail,               \
131 				_res_fallback, NULL, NULL)
132 
133 /**
134  * @brief Define an HTTPS service without static resources.
135  *
136  * @note The @p _host parameter is used to specify an IP address either in
137  * IPv4 or IPv6 format a fully-qualified hostname or a virtual host. If left NULL, the listening
138  * port will listen on all addresses.
139  *
140  * @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
141  * number to use for the service. If the specified port number is zero, then an ephemeral port
142  * number will be used and the actual port number assigned will be written back to memory. For
143  * ephemeral port numbers, the memory pointed to by @p _port must be writeable.
144  *
145  * @param _name Name of the service.
146  * @param _host IP address or hostname associated with the service.
147  * @param[inout] _port Pointer to port associated with the service.
148  * @param _concurrent Maximum number of concurrent clients.
149  * @param _backlog Maximum number queued connections.
150  * @param _detail User-defined detail associated with the service.
151  * @param _res_fallback Fallback resource to be served if no other resource matches path
152  * @param _sec_tag_list TLS security tag list used to setup a HTTPS socket.
153  * @param _sec_tag_list_size TLS security tag list size used to setup a HTTPS socket.
154  */
155 #define HTTPS_SERVICE_DEFINE_EMPTY(_name, _host, _port, _concurrent, _backlog, _detail,          \
156 				   _res_fallback, _sec_tag_list, _sec_tag_list_size)             \
157 	__z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail,             \
158 				_res_fallback, NULL, NULL,                                       \
159 				_sec_tag_list, _sec_tag_list_size);				 \
160 	BUILD_ASSERT(IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS),				 \
161 		     "TLS is required for HTTP secure (CONFIG_NET_SOCKETS_SOCKOPT_TLS)")
162 
163 /**
164  * @brief Define an HTTP service with static resources.
165  *
166  * @note The @p _host parameter is used to specify an IP address either in
167  * IPv4 or IPv6 format a fully-qualified hostname or a virtual host. If left NULL, the listening
168  * port will listen on all addresses.
169  *
170  * @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
171  * number to use for the service. If the specified port number is zero, then an ephemeral port
172  * number will be used and the actual port number assigned will be written back to memory. For
173  * ephemeral port numbers, the memory pointed to by @p _port must be writeable.
174  *
175  * @param _name Name of the service.
176  * @param _host IP address or hostname associated with the service.
177  * @param[inout] _port Pointer to port associated with the service.
178  * @param _concurrent Maximum number of concurrent clients.
179  * @param _backlog Maximum number queued connections.
180  * @param _detail User-defined detail associated with the service.
181  * @param _res_fallback Fallback resource to be served if no other resource matches path
182  */
183 #define HTTP_SERVICE_DEFINE(_name, _host, _port, _concurrent, _backlog, _detail, _res_fallback)    \
184 	extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_start)[];      \
185 	extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_end)[];        \
186 	__z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail,               \
187 				_res_fallback,                                                     \
188 				&_CONCAT(_http_resource_desc_##_name, _list_start)[0],             \
189 				&_CONCAT(_http_resource_desc_##_name, _list_end)[0])
190 
191 /**
192  * @brief Define an HTTPS service with static resources.
193  *
194  * @note The @p _host parameter is used to specify an IP address either in
195  * IPv4 or IPv6 format a fully-qualified hostname or a virtual host. If left NULL, the listening
196  * port will listen on all addresses.
197  *
198  * @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
199  * number to use for the service. If the specified port number is zero, then an ephemeral port
200  * number will be used and the actual port number assigned will be written back to memory. For
201  * ephemeral port numbers, the memory pointed to by @p _port must be writeable.
202  *
203  * @param _name Name of the service.
204  * @param _host IP address or hostname associated with the service.
205  * @param[inout] _port Pointer to port associated with the service.
206  * @param _concurrent Maximum number of concurrent clients.
207  * @param _backlog Maximum number queued connections.
208  * @param _detail User-defined detail associated with the service.
209  * @param _res_fallback Fallback resource to be served if no other resource matches path
210  * @param _sec_tag_list TLS security tag list used to setup a HTTPS socket.
211  * @param _sec_tag_list_size TLS security tag list size used to setup a HTTPS socket.
212  */
213 #define HTTPS_SERVICE_DEFINE(_name, _host, _port, _concurrent, _backlog, _detail,              \
214 			     _res_fallback, _sec_tag_list, _sec_tag_list_size)                 \
215 	extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_start)[];  \
216 	extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_end)[];    \
217 	__z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail,           \
218 				_res_fallback,                                                 \
219 				&_CONCAT(_http_resource_desc_##_name, _list_start)[0],         \
220 				&_CONCAT(_http_resource_desc_##_name, _list_end)[0],           \
221 				_sec_tag_list, _sec_tag_list_size);                            \
222 	BUILD_ASSERT(IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS),                               \
223 		     "TLS is required for HTTP secure (CONFIG_NET_SOCKETS_SOCKOPT_TLS)")
224 
225 /**
226  * @brief Count the number of HTTP services.
227  *
228  * @param[out] _dst Pointer to location where result is written.
229  */
230 #define HTTP_SERVICE_COUNT(_dst) STRUCT_SECTION_COUNT(http_service_desc, _dst)
231 
232 /**
233  * @brief Count HTTP service static resources.
234  *
235  * @param _service Pointer to a service.
236  */
237 #define HTTP_SERVICE_RESOURCE_COUNT(_service) ((_service)->res_end - (_service)->res_begin)
238 
239 /**
240  * @brief Iterate over all HTTP services.
241  *
242  * @param _it Name of http_service_desc iterator
243  */
244 #define HTTP_SERVICE_FOREACH(_it) STRUCT_SECTION_FOREACH(http_service_desc, _it)
245 
246 /**
247  * @brief Iterate over static HTTP resources associated with a given @p _service.
248  *
249  * @note This macro requires that @p _service is defined with @ref HTTP_SERVICE_DEFINE.
250  *
251  * @param _service Name of HTTP service
252  * @param _it Name of iterator (of type @ref http_resource_desc)
253  */
254 #define HTTP_RESOURCE_FOREACH(_service, _it)                                                       \
255 	STRUCT_SECTION_FOREACH_ALTERNATE(http_resource_desc_##_service, http_resource_desc, _it)
256 
257 /**
258  * @brief Iterate over all static resources associated with @p _service .
259  *
260  * @note This macro is suitable for a @p _service defined with either @ref HTTP_SERVICE_DEFINE
261  * or @ref HTTP_SERVICE_DEFINE_EMPTY.
262  *
263  * @param _service Pointer to HTTP service
264  * @param _it Name of iterator (of type @ref http_resource_desc)
265  */
266 #define HTTP_SERVICE_FOREACH_RESOURCE(_service, _it)                                               \
267 	for (struct http_resource_desc *_it = (_service)->res_begin; ({                            \
268 		     __ASSERT(_it <= (_service)->res_end, "unexpected list end location");         \
269 		     _it < (_service)->res_end;                                                    \
270 	     });                                                                                   \
271 	     _it++)
272 
273 #ifdef __cplusplus
274 }
275 #endif
276 
277 /**
278  * @}
279  */
280 
281 #endif /* ZEPHYR_INCLUDE_NET_HTTP_SERVICE_H_ */
282