I was wondering how I can update my current code (see below) to reuse a single socket for multiple HTTPS requests, rather than opening and closing a socket for each HTTPS request. The code is a slightly modified version of the Nordic SDK HTTPS client sample code which can be found under /nrf/samples/net/https_client. It should be noted that I am using an nRF9160 DK over an LTE connection.
Alternatively, if there is any way to speed up the current code as it is taking me at least 3-4 seconds to run a single request.
Any help is appreciated.
main.c
#include <string.h> #include <zephyr/kernel.h> #include <stdlib.h> #include <zephyr/net/socket.h> #include <zephyr/net/conn_mgr_monitor.h> #include <zephyr/net/conn_mgr_connectivity.h> #include <zephyr/net/tls_credentials.h> #if defined(CONFIG_POSIX_API) #include <zephyr/posix/arpa/inet.h> #include <zephyr/posix/netdb.h> #include <zephyr/posix/unistd.h> #include <zephyr/posix/sys/socket.h> #endif #if CONFIG_MODEM_KEY_MGMT #include <modem/modem_key_mgmt.h> #endif #define HTTPS_PORT "443" #define HTTPS_HOSTNAME "[insert url here]" #define HTTP_GET_PATH "/?mac=[insert mac address here]" #define HTTP_POST_PATH "/[...]/[...]/[insert mac address here]" #define HTTP_POST_BODY "[insert body here]" #define USE_POST 1 #define RECV_BUF_SIZE 2048 #define TLS_SEC_TAG 42 #define L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED) #define CONN_LAYER_EVENT_MASK (NET_EVENT_CONN_IF_FATAL_ERROR) static char send_buf[1024]; static char recv_buf[RECV_BUF_SIZE]; static K_SEM_DEFINE(network_connected_sem, 0, 1); static const char cert[] = { #include "DigiCertGlobalRootG2.pem.inc" IF_ENABLED(CONFIG_TLS_CREDENTIALS, (0x00)) }; static struct net_mgmt_event_callback l4_cb; static struct net_mgmt_event_callback conn_cb; BUILD_ASSERT(sizeof(cert) < KB(4), "Certificate too large"); int cert_provision(void) { int err; printk("Provisioning certificate\n"); #if CONFIG_MODEM_KEY_MGMT bool exists; int mismatch; err = modem_key_mgmt_exists(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); if (err) return err; if (exists) { mismatch = modem_key_mgmt_cmp(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, sizeof(cert)); if (!mismatch) return 0; modem_key_mgmt_delete(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN); } err = modem_key_mgmt_write(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, sizeof(cert)); #else err = tls_credential_add(TLS_SEC_TAG, TLS_CREDENTIAL_CA_CERTIFICATE, cert, sizeof(cert)); if (err == -EEXIST) err = 0; #endif return err; } int tls_setup(int fd) { int err; int verify = 2; const sec_tag_t tls_sec_tag[] = { TLS_SEC_TAG }; err = setsockopt(fd, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify)); if (err) return err; err = setsockopt(fd, SOL_TLS, TLS_SEC_TAG_LIST, tls_sec_tag, sizeof(tls_sec_tag)); if (err) return err; err = setsockopt(fd, SOL_TLS, TLS_HOSTNAME, HTTPS_HOSTNAME, strlen(HTTPS_HOSTNAME)); return err; } static void on_net_event_l4_disconnected(void) { printk("Disconnected from the network\n"); } static void on_net_event_l4_connected(void) { k_sem_give(&network_connected_sem); } static void l4_event_handler(struct net_mgmt_event_callback *cb, uint32_t event, struct net_if *iface) { if (event == NET_EVENT_L4_CONNECTED) on_net_event_l4_connected(); else if (event == NET_EVENT_L4_DISCONNECTED) on_net_event_l4_disconnected(); } static void connectivity_event_handler(struct net_mgmt_event_callback *cb, uint32_t event, struct net_if *iface) { if (event == NET_EVENT_CONN_IF_FATAL_ERROR) printk("Fatal error received from the connectivity layer\n"); } static int send_http_request(void) { int err, fd, bytes; size_t off = 0; struct addrinfo *res; struct addrinfo hints = { .ai_flags = AI_NUMERICSERV, .ai_socktype = SOCK_STREAM, }; char peer_addr[INET6_ADDRSTRLEN]; printk("Looking up %s\n", HTTPS_HOSTNAME); err = getaddrinfo(HTTPS_HOSTNAME, HTTPS_PORT, &hints, &res); if (err) return; inet_ntop(res->ai_family, &((struct sockaddr_in *)(res->ai_addr))->sin_addr, peer_addr, INET6_ADDRSTRLEN); printk("Resolved %s (%s)\n", peer_addr, net_family2str(res->ai_family)); fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TLS_1_2); if (fd == -1) goto clean_up; if (tls_setup(fd)) goto clean_up; printk("Connecting to %s\n", HTTPS_HOSTNAME); if (connect(fd, res->ai_addr, res->ai_addrlen)) goto clean_up; if (USE_POST) { snprintf(send_buf, sizeof(send_buf), "POST %s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: slm\r\n" "Accept: */*\r\n" "Content-Type: application/json\r\n" "Content-Length: %zu\r\n" "Connection: close\r\n\r\n%s", HTTP_POST_PATH, HTTPS_HOSTNAME, strlen(HTTP_POST_BODY), HTTP_POST_BODY); } else { snprintf(send_buf, sizeof(send_buf), "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n", HTTP_GET_PATH, HTTPS_HOSTNAME); } size_t total_len = strlen(send_buf); off = 0; do { bytes = send(fd, &send_buf[off], total_len - off, 0); if (bytes < 0) { printk("send() failed: errno=%d\n", errno); goto clean_up; } off += bytes; } while (off < total_len); printk("Sent %d bytes\n", off); // clear the receive buffer before use memset(recv_buf, 0, sizeof(recv_buf)); off = 0; do { bytes = recv(fd, &recv_buf[off], RECV_BUF_SIZE - off - 1, 0); if (bytes < 0) { printk("recv() failed: errno=%d\n", errno); goto clean_up; } off += bytes; // prevent buffer overflow if (off >= RECV_BUF_SIZE - 1) { printk("Warning: response too large, truncating\n"); break; } } while (bytes != 0); // 0 means peer closed connection recv_buf[off] = '\0'; printk("\nFull response:\n%s\n", recv_buf); freeaddrinfo(res); close(fd); return 0; clean_up: if (res) freeaddrinfo(res); if (fd >= 0) close(fd); return -1; } int main(void) { int err; int request_count = 0; printk("HTTPS client sample started\n\r"); net_mgmt_init_event_callback(&l4_cb, l4_event_handler, L4_EVENT_MASK); net_mgmt_add_event_callback(&l4_cb); net_mgmt_init_event_callback(&conn_cb, connectivity_event_handler, CONN_LAYER_EVENT_MASK); net_mgmt_add_event_callback(&conn_cb); printk("Bringing network interface up\n"); if (conn_mgr_all_if_up(true)) return -1; if (cert_provision()) return -1; printk("Connecting to the network\n"); if (conn_mgr_all_if_connect(true)) return -1; k_sem_take(&network_connected_sem, K_FOREVER); while (1) { request_count++; printk("\n>>> Sending POST request #%d\n", request_count); err = send_http_request(); // return 0 on success, <0 on fail if (err < 0) { printk("POST request #%d failed. Exiting loop.\n", request_count); break; } // k_sleep(K_MSEC(100)); // optional: short delay between requests } printk("Disconnecting network after %d requests\n", request_count); k_sleep(K_SECONDS(1)); conn_mgr_all_if_disconnect(true); conn_mgr_all_if_down(true); return 0; }
prj.conf
# General CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_MAIN_STACK_SIZE=8192 # Logging CONFIG_LOG=y # Network CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y CONFIG_POSIX_API=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=y CONFIG_NET_CONNECTION_MANAGER=y CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=4600