1 /*
2  * Copyright (c) 2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_http_client_sample, LOG_LEVEL_DBG);
9 
10 #include <zephyr/net/net_ip.h>
11 #include <zephyr/net/socket.h>
12 #include <zephyr/net/tls_credentials.h>
13 #include <zephyr/net/http/client.h>
14 
15 #include "ca_certificate.h"
16 
17 #define HTTP_PORT 8000
18 #define HTTPS_PORT 4443
19 
20 #if defined(CONFIG_NET_CONFIG_PEER_IPV6_ADDR)
21 #define SERVER_ADDR6  CONFIG_NET_CONFIG_PEER_IPV6_ADDR
22 #else
23 #define SERVER_ADDR6 ""
24 #endif
25 
26 #if defined(CONFIG_NET_CONFIG_PEER_IPV4_ADDR)
27 #define SERVER_ADDR4  CONFIG_NET_CONFIG_PEER_IPV4_ADDR
28 #else
29 #define SERVER_ADDR4 ""
30 #endif
31 
32 #define MAX_RECV_BUF_LEN 512
33 
34 static uint8_t recv_buf_ipv4[MAX_RECV_BUF_LEN];
35 static uint8_t recv_buf_ipv6[MAX_RECV_BUF_LEN];
36 
setup_socket(sa_family_t family,const char * server,int port,int * sock,struct sockaddr * addr,socklen_t addr_len)37 static int setup_socket(sa_family_t family, const char *server, int port,
38 			int *sock, struct sockaddr *addr, socklen_t addr_len)
39 {
40 	const char *family_str = family == AF_INET ? "IPv4" : "IPv6";
41 	int ret = 0;
42 
43 	memset(addr, 0, addr_len);
44 
45 	if (family == AF_INET) {
46 		net_sin(addr)->sin_family = AF_INET;
47 		net_sin(addr)->sin_port = htons(port);
48 		inet_pton(family, server, &net_sin(addr)->sin_addr);
49 	} else {
50 		net_sin6(addr)->sin6_family = AF_INET6;
51 		net_sin6(addr)->sin6_port = htons(port);
52 		inet_pton(family, server, &net_sin6(addr)->sin6_addr);
53 	}
54 
55 	if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) {
56 		sec_tag_t sec_tag_list[] = {
57 			CA_CERTIFICATE_TAG,
58 		};
59 
60 		*sock = socket(family, SOCK_STREAM, IPPROTO_TLS_1_2);
61 		if (*sock >= 0) {
62 			ret = setsockopt(*sock, SOL_TLS, TLS_SEC_TAG_LIST,
63 					 sec_tag_list, sizeof(sec_tag_list));
64 			if (ret < 0) {
65 				LOG_ERR("Failed to set %s secure option (%d)",
66 					family_str, -errno);
67 				ret = -errno;
68 			}
69 
70 			ret = setsockopt(*sock, SOL_TLS, TLS_HOSTNAME,
71 					 TLS_PEER_HOSTNAME,
72 					 sizeof(TLS_PEER_HOSTNAME));
73 			if (ret < 0) {
74 				LOG_ERR("Failed to set %s TLS_HOSTNAME "
75 					"option (%d)", family_str, -errno);
76 				ret = -errno;
77 			}
78 		}
79 	} else {
80 		*sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
81 	}
82 
83 	if (*sock < 0) {
84 		LOG_ERR("Failed to create %s HTTP socket (%d)", family_str,
85 			-errno);
86 	}
87 
88 	return ret;
89 }
90 
payload_cb(int sock,struct http_request * req,void * user_data)91 static int payload_cb(int sock, struct http_request *req, void *user_data)
92 {
93 	const char *content[] = {
94 		"foobar",
95 		"chunked",
96 		"last"
97 	};
98 	char tmp[64];
99 	int i, pos = 0;
100 
101 	for (i = 0; i < ARRAY_SIZE(content); i++) {
102 		pos += snprintk(tmp + pos, sizeof(tmp) - pos,
103 				"%x\r\n%s\r\n",
104 				(unsigned int)strlen(content[i]),
105 				content[i]);
106 	}
107 
108 	pos += snprintk(tmp + pos, sizeof(tmp) - pos, "0\r\n\r\n");
109 
110 	(void)send(sock, tmp, pos, 0);
111 
112 	return pos;
113 }
114 
response_cb(struct http_response * rsp,enum http_final_call final_data,void * user_data)115 static void response_cb(struct http_response *rsp,
116 			enum http_final_call final_data,
117 			void *user_data)
118 {
119 	if (final_data == HTTP_DATA_MORE) {
120 		LOG_INF("Partial data received (%zd bytes)", rsp->data_len);
121 	} else if (final_data == HTTP_DATA_FINAL) {
122 		LOG_INF("All the data received (%zd bytes)", rsp->data_len);
123 	}
124 
125 	LOG_INF("Response to %s", (const char *)user_data);
126 	LOG_INF("Response status %s", rsp->http_status);
127 }
128 
connect_socket(sa_family_t family,const char * server,int port,int * sock,struct sockaddr * addr,socklen_t addr_len)129 static int connect_socket(sa_family_t family, const char *server, int port,
130 			  int *sock, struct sockaddr *addr, socklen_t addr_len)
131 {
132 	int ret;
133 
134 	ret = setup_socket(family, server, port, sock, addr, addr_len);
135 	if (ret < 0 || *sock < 0) {
136 		return -1;
137 	}
138 
139 	ret = connect(*sock, addr, addr_len);
140 	if (ret < 0) {
141 		LOG_ERR("Cannot connect to %s remote (%d)",
142 			family == AF_INET ? "IPv4" : "IPv6",
143 			-errno);
144 		close(*sock);
145 		*sock = -1;
146 		ret = -errno;
147 	}
148 
149 	return ret;
150 }
151 
run_queries(void)152 static int run_queries(void)
153 {
154 	struct sockaddr_in6 addr6;
155 	struct sockaddr_in addr4;
156 	int sock4 = -1, sock6 = -1;
157 	int32_t timeout = 3 * MSEC_PER_SEC;
158 	int ret = 0;
159 	int port = HTTP_PORT;
160 
161 	if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) {
162 		ret = tls_credential_add(CA_CERTIFICATE_TAG,
163 					 TLS_CREDENTIAL_CA_CERTIFICATE,
164 					 ca_certificate,
165 					 sizeof(ca_certificate));
166 		if (ret < 0) {
167 			LOG_ERR("Failed to register public certificate: %d",
168 				ret);
169 			return ret;
170 		}
171 
172 		port = HTTPS_PORT;
173 	}
174 
175 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
176 		(void)connect_socket(AF_INET, SERVER_ADDR4, port,
177 				     &sock4, (struct sockaddr *)&addr4,
178 				     sizeof(addr4));
179 	}
180 
181 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
182 		(void)connect_socket(AF_INET6, SERVER_ADDR6, port,
183 				     &sock6, (struct sockaddr *)&addr6,
184 				     sizeof(addr6));
185 	}
186 
187 	if (sock4 < 0 && sock6 < 0) {
188 		LOG_ERR("Cannot create HTTP connection.");
189 		return -ECONNABORTED;
190 	}
191 
192 	if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) {
193 		struct http_request req;
194 
195 		memset(&req, 0, sizeof(req));
196 
197 		req.method = HTTP_GET;
198 		req.url = "/";
199 		req.host = SERVER_ADDR4;
200 		req.protocol = "HTTP/1.1";
201 		req.response = response_cb;
202 		req.recv_buf = recv_buf_ipv4;
203 		req.recv_buf_len = sizeof(recv_buf_ipv4);
204 
205 		ret = http_client_req(sock4, &req, timeout, "IPv4 GET");
206 
207 		close(sock4);
208 	}
209 
210 	if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) {
211 		struct http_request req;
212 
213 		memset(&req, 0, sizeof(req));
214 
215 		req.method = HTTP_GET;
216 		req.url = "/";
217 		req.host = SERVER_ADDR6;
218 		req.protocol = "HTTP/1.1";
219 		req.response = response_cb;
220 		req.recv_buf = recv_buf_ipv6;
221 		req.recv_buf_len = sizeof(recv_buf_ipv6);
222 
223 		ret = http_client_req(sock6, &req, timeout, "IPv6 GET");
224 
225 		close(sock6);
226 	}
227 
228 	sock4 = -1;
229 	sock6 = -1;
230 
231 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
232 		(void)connect_socket(AF_INET, SERVER_ADDR4, port,
233 				     &sock4, (struct sockaddr *)&addr4,
234 				     sizeof(addr4));
235 	}
236 
237 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
238 		(void)connect_socket(AF_INET6, SERVER_ADDR6, port,
239 				     &sock6, (struct sockaddr *)&addr6,
240 				     sizeof(addr6));
241 	}
242 
243 	if (sock4 < 0 && sock6 < 0) {
244 		LOG_ERR("Cannot create HTTP connection.");
245 		return -ECONNABORTED;
246 	}
247 
248 	if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) {
249 		struct http_request req;
250 
251 		memset(&req, 0, sizeof(req));
252 
253 		req.method = HTTP_POST;
254 		req.url = "/foobar";
255 		req.host = SERVER_ADDR4;
256 		req.protocol = "HTTP/1.1";
257 		req.payload = "foobar";
258 		req.payload_len = strlen(req.payload);
259 		req.response = response_cb;
260 		req.recv_buf = recv_buf_ipv4;
261 		req.recv_buf_len = sizeof(recv_buf_ipv4);
262 
263 		ret = http_client_req(sock4, &req, timeout, "IPv4 POST");
264 
265 		close(sock4);
266 	}
267 
268 	if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) {
269 		struct http_request req;
270 
271 		memset(&req, 0, sizeof(req));
272 
273 		req.method = HTTP_POST;
274 		req.url = "/";
275 		req.host = SERVER_ADDR6;
276 		req.protocol = "HTTP/1.1";
277 		req.payload = "foobar";
278 		req.payload_len = strlen(req.payload);
279 		req.response = response_cb;
280 		req.recv_buf = recv_buf_ipv6;
281 		req.recv_buf_len = sizeof(recv_buf_ipv6);
282 
283 		ret = http_client_req(sock6, &req, timeout, "IPv6 POST");
284 
285 		close(sock6);
286 	}
287 
288 	/* Do a chunked POST request */
289 
290 	sock4 = -1;
291 	sock6 = -1;
292 
293 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
294 		(void)connect_socket(AF_INET, SERVER_ADDR4, port,
295 				     &sock4, (struct sockaddr *)&addr4,
296 				     sizeof(addr4));
297 	}
298 
299 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
300 		(void)connect_socket(AF_INET6, SERVER_ADDR6, port,
301 				     &sock6, (struct sockaddr *)&addr6,
302 				     sizeof(addr6));
303 	}
304 
305 	if (sock4 < 0 && sock6 < 0) {
306 		LOG_ERR("Cannot create HTTP connection.");
307 		return -ECONNABORTED;
308 	}
309 
310 	if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) {
311 		struct http_request req;
312 		const char *headers[] = {
313 			"Transfer-Encoding: chunked\r\n",
314 			NULL
315 		};
316 
317 		memset(&req, 0, sizeof(req));
318 
319 		req.method = HTTP_POST;
320 		req.url = "/chunked-test";
321 		req.host = SERVER_ADDR4;
322 		req.protocol = "HTTP/1.1";
323 		req.payload_cb = payload_cb;
324 		req.header_fields = headers;
325 		req.response = response_cb;
326 		req.recv_buf = recv_buf_ipv4;
327 		req.recv_buf_len = sizeof(recv_buf_ipv4);
328 
329 		ret = http_client_req(sock4, &req, timeout, "IPv4 POST");
330 
331 		close(sock4);
332 	}
333 
334 	if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) {
335 		struct http_request req;
336 		const char *headers[] = {
337 			"Transfer-Encoding: chunked\r\n",
338 			NULL
339 		};
340 
341 		memset(&req, 0, sizeof(req));
342 
343 		req.method = HTTP_POST;
344 		req.url = "/chunked-test";
345 		req.host = SERVER_ADDR6;
346 		req.protocol = "HTTP/1.1";
347 		req.payload_cb = payload_cb;
348 		req.header_fields = headers;
349 		req.response = response_cb;
350 		req.recv_buf = recv_buf_ipv6;
351 		req.recv_buf_len = sizeof(recv_buf_ipv6);
352 
353 		ret = http_client_req(sock6, &req, timeout, "IPv6 POST");
354 
355 		close(sock6);
356 	}
357 
358 	return ret;
359 }
360 
main(void)361 int main(void)
362 {
363 	int iterations = CONFIG_NET_SAMPLE_SEND_ITERATIONS;
364 	int i = 0;
365 	int ret = 0;
366 
367 	while (iterations == 0 || i < iterations) {
368 		ret = run_queries();
369 		if (ret < 0) {
370 			ret = 1;
371 			break;
372 		}
373 
374 		if (iterations > 0) {
375 			i++;
376 			if (i >= iterations) {
377 				ret = 0;
378 				break;
379 			}
380 		} else {
381 			ret = 0;
382 			break;
383 		}
384 	}
385 
386 	if (iterations == 0) {
387 		k_sleep(K_FOREVER);
388 	}
389 
390 	exit(ret);
391 	return ret;
392 }
393