| /* |
| * Copyright (c) 2019 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(net_http_client_sample, LOG_LEVEL_DBG); |
| |
| #include <net/net_ip.h> |
| #include <net/socket.h> |
| #include <net/tls_credentials.h> |
| #include <net/http_client.h> |
| |
| #include "ca_certificate.h" |
| |
| #define HTTP_PORT 8000 |
| #define HTTPS_PORT 4443 |
| |
| #if defined(CONFIG_NET_CONFIG_PEER_IPV6_ADDR) |
| #define SERVER_ADDR6 CONFIG_NET_CONFIG_PEER_IPV6_ADDR |
| #else |
| #define SERVER_ADDR6 "" |
| #endif |
| |
| #if defined(CONFIG_NET_CONFIG_PEER_IPV4_ADDR) |
| #define SERVER_ADDR4 CONFIG_NET_CONFIG_PEER_IPV4_ADDR |
| #else |
| #define SERVER_ADDR4 "" |
| #endif |
| |
| #define MAX_RECV_BUF_LEN 512 |
| |
| static uint8_t recv_buf_ipv4[MAX_RECV_BUF_LEN]; |
| static uint8_t recv_buf_ipv6[MAX_RECV_BUF_LEN]; |
| |
| static int setup_socket(sa_family_t family, const char *server, int port, |
| int *sock, struct sockaddr *addr, socklen_t addr_len) |
| { |
| const char *family_str = family == AF_INET ? "IPv4" : "IPv6"; |
| int ret = 0; |
| |
| memset(addr, 0, addr_len); |
| |
| if (family == AF_INET) { |
| net_sin(addr)->sin_family = AF_INET; |
| net_sin(addr)->sin_port = htons(port); |
| inet_pton(family, server, &net_sin(addr)->sin_addr); |
| } else { |
| net_sin6(addr)->sin6_family = AF_INET6; |
| net_sin6(addr)->sin6_port = htons(port); |
| inet_pton(family, server, &net_sin6(addr)->sin6_addr); |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) { |
| sec_tag_t sec_tag_list[] = { |
| CA_CERTIFICATE_TAG, |
| }; |
| |
| *sock = socket(family, SOCK_STREAM, IPPROTO_TLS_1_2); |
| if (*sock >= 0) { |
| ret = setsockopt(*sock, SOL_TLS, TLS_SEC_TAG_LIST, |
| sec_tag_list, sizeof(sec_tag_list)); |
| if (ret < 0) { |
| LOG_ERR("Failed to set %s secure option (%d)", |
| family_str, -errno); |
| ret = -errno; |
| } |
| |
| ret = setsockopt(*sock, SOL_TLS, TLS_HOSTNAME, |
| TLS_PEER_HOSTNAME, |
| sizeof(TLS_PEER_HOSTNAME)); |
| if (ret < 0) { |
| LOG_ERR("Failed to set %s TLS_HOSTNAME " |
| "option (%d)", family_str, -errno); |
| ret = -errno; |
| } |
| } |
| } else { |
| *sock = socket(family, SOCK_STREAM, IPPROTO_TCP); |
| } |
| |
| if (*sock < 0) { |
| LOG_ERR("Failed to create %s HTTP socket (%d)", family_str, |
| -errno); |
| } |
| |
| return ret; |
| } |
| |
| static int payload_cb(int sock, struct http_request *req, void *user_data) |
| { |
| const char *content[] = { |
| "foobar", |
| "chunked", |
| "last" |
| }; |
| char tmp[64]; |
| int i, pos = 0; |
| |
| for (i = 0; i < ARRAY_SIZE(content); i++) { |
| pos += snprintk(tmp + pos, sizeof(tmp) - pos, |
| "%x\r\n%s\r\n", |
| (unsigned int)strlen(content[i]), |
| content[i]); |
| } |
| |
| pos += snprintk(tmp + pos, sizeof(tmp) - pos, "0\r\n\r\n"); |
| |
| (void)send(sock, tmp, pos, 0); |
| |
| return pos; |
| } |
| |
| static void response_cb(struct http_response *rsp, |
| enum http_final_call final_data, |
| void *user_data) |
| { |
| if (final_data == HTTP_DATA_MORE) { |
| LOG_INF("Partial data received (%zd bytes)", rsp->data_len); |
| } else if (final_data == HTTP_DATA_FINAL) { |
| LOG_INF("All the data received (%zd bytes)", rsp->data_len); |
| } |
| |
| LOG_INF("Response to %s", (const char *)user_data); |
| LOG_INF("Response status %s", rsp->http_status); |
| } |
| |
| static int connect_socket(sa_family_t family, const char *server, int port, |
| int *sock, struct sockaddr *addr, socklen_t addr_len) |
| { |
| int ret; |
| |
| ret = setup_socket(family, server, port, sock, addr, addr_len); |
| if (ret < 0 || *sock < 0) { |
| return -1; |
| } |
| |
| ret = connect(*sock, addr, addr_len); |
| if (ret < 0) { |
| LOG_ERR("Cannot connect to %s remote (%d)", |
| family == AF_INET ? "IPv4" : "IPv6", |
| -errno); |
| ret = -errno; |
| } |
| |
| return ret; |
| } |
| |
| static int run_queries(void) |
| { |
| struct sockaddr_in6 addr6; |
| struct sockaddr_in addr4; |
| int sock4 = -1, sock6 = -1; |
| int32_t timeout = 3 * MSEC_PER_SEC; |
| int ret = 0; |
| int port = HTTP_PORT; |
| |
| if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) { |
| ret = tls_credential_add(CA_CERTIFICATE_TAG, |
| TLS_CREDENTIAL_CA_CERTIFICATE, |
| ca_certificate, |
| sizeof(ca_certificate)); |
| if (ret < 0) { |
| LOG_ERR("Failed to register public certificate: %d", |
| ret); |
| return ret; |
| } |
| |
| port = HTTPS_PORT; |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_IPV4)) { |
| (void)connect_socket(AF_INET, SERVER_ADDR4, port, |
| &sock4, (struct sockaddr *)&addr4, |
| sizeof(addr4)); |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_IPV6)) { |
| (void)connect_socket(AF_INET6, SERVER_ADDR6, port, |
| &sock6, (struct sockaddr *)&addr6, |
| sizeof(addr6)); |
| } |
| |
| if (sock4 < 0 && sock6 < 0) { |
| LOG_ERR("Cannot create HTTP connection."); |
| return -ECONNABORTED; |
| } |
| |
| if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) { |
| struct http_request req; |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.method = HTTP_GET; |
| req.url = "/"; |
| req.host = SERVER_ADDR4; |
| req.protocol = "HTTP/1.1"; |
| req.response = response_cb; |
| req.recv_buf = recv_buf_ipv4; |
| req.recv_buf_len = sizeof(recv_buf_ipv4); |
| |
| ret = http_client_req(sock4, &req, timeout, "IPv4 GET"); |
| |
| close(sock4); |
| } |
| |
| if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) { |
| struct http_request req; |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.method = HTTP_GET; |
| req.url = "/"; |
| req.host = SERVER_ADDR6; |
| req.protocol = "HTTP/1.1"; |
| req.response = response_cb; |
| req.recv_buf = recv_buf_ipv6; |
| req.recv_buf_len = sizeof(recv_buf_ipv6); |
| |
| ret = http_client_req(sock6, &req, timeout, "IPv6 GET"); |
| |
| close(sock6); |
| } |
| |
| sock4 = -1; |
| sock6 = -1; |
| |
| if (IS_ENABLED(CONFIG_NET_IPV4)) { |
| (void)connect_socket(AF_INET, SERVER_ADDR4, port, |
| &sock4, (struct sockaddr *)&addr4, |
| sizeof(addr4)); |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_IPV6)) { |
| (void)connect_socket(AF_INET6, SERVER_ADDR6, port, |
| &sock6, (struct sockaddr *)&addr6, |
| sizeof(addr6)); |
| } |
| |
| if (sock4 < 0 && sock6 < 0) { |
| LOG_ERR("Cannot create HTTP connection."); |
| return -ECONNABORTED; |
| } |
| |
| if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) { |
| struct http_request req; |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.method = HTTP_POST; |
| req.url = "/foobar"; |
| req.host = SERVER_ADDR4; |
| req.protocol = "HTTP/1.1"; |
| req.payload = "foobar"; |
| req.payload_len = strlen(req.payload); |
| req.response = response_cb; |
| req.recv_buf = recv_buf_ipv4; |
| req.recv_buf_len = sizeof(recv_buf_ipv4); |
| |
| ret = http_client_req(sock4, &req, timeout, "IPv4 POST"); |
| |
| close(sock4); |
| } |
| |
| if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) { |
| struct http_request req; |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.method = HTTP_POST; |
| req.url = "/"; |
| req.host = SERVER_ADDR6; |
| req.protocol = "HTTP/1.1"; |
| req.payload = "foobar"; |
| req.payload_len = strlen(req.payload); |
| req.response = response_cb; |
| req.recv_buf = recv_buf_ipv6; |
| req.recv_buf_len = sizeof(recv_buf_ipv6); |
| |
| ret = http_client_req(sock6, &req, timeout, "IPv6 POST"); |
| |
| close(sock6); |
| } |
| |
| /* Do a chunked POST request */ |
| |
| sock4 = -1; |
| sock6 = -1; |
| |
| if (IS_ENABLED(CONFIG_NET_IPV4)) { |
| (void)connect_socket(AF_INET, SERVER_ADDR4, port, |
| &sock4, (struct sockaddr *)&addr4, |
| sizeof(addr4)); |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_IPV6)) { |
| (void)connect_socket(AF_INET6, SERVER_ADDR6, port, |
| &sock6, (struct sockaddr *)&addr6, |
| sizeof(addr6)); |
| } |
| |
| if (sock4 < 0 && sock6 < 0) { |
| LOG_ERR("Cannot create HTTP connection."); |
| return -ECONNABORTED; |
| } |
| |
| if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) { |
| struct http_request req; |
| const char *headers[] = { |
| "Transfer-Encoding: chunked\r\n", |
| NULL |
| }; |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.method = HTTP_POST; |
| req.url = "/chunked-test"; |
| req.host = SERVER_ADDR4; |
| req.protocol = "HTTP/1.1"; |
| req.payload_cb = payload_cb; |
| req.header_fields = headers; |
| req.response = response_cb; |
| req.recv_buf = recv_buf_ipv4; |
| req.recv_buf_len = sizeof(recv_buf_ipv4); |
| |
| ret = http_client_req(sock4, &req, timeout, "IPv4 POST"); |
| |
| close(sock4); |
| } |
| |
| if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) { |
| struct http_request req; |
| const char *headers[] = { |
| "Transfer-Encoding: chunked\r\n", |
| NULL |
| }; |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.method = HTTP_POST; |
| req.url = "/chunked-test"; |
| req.host = SERVER_ADDR6; |
| req.protocol = "HTTP/1.1"; |
| req.payload_cb = payload_cb; |
| req.header_fields = headers; |
| req.response = response_cb; |
| req.recv_buf = recv_buf_ipv6; |
| req.recv_buf_len = sizeof(recv_buf_ipv6); |
| |
| ret = http_client_req(sock6, &req, timeout, "IPv6 POST"); |
| |
| close(sock6); |
| } |
| |
| return ret; |
| } |
| |
| void main(void) |
| { |
| int iterations = CONFIG_NET_SAMPLE_SEND_ITERATIONS; |
| int i = 0; |
| int ret; |
| |
| while (iterations == 0 || i < iterations) { |
| ret = run_queries(); |
| if (ret < 0) { |
| exit(1); |
| } |
| |
| if (iterations > 0) { |
| i++; |
| if (i >= iterations) { |
| break; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| if (iterations == 0) { |
| k_sleep(K_FOREVER); |
| } |
| |
| exit(0); |
| } |