Merge pull request #3423 from cesanta/singleton

Add http singleton client example
This commit is contained in:
Sergey Lyubka
2026-01-28 14:50:27 +00:00
committed by GitHub
4 changed files with 141 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options
#CFLAGS += -fsanitize=address,undefined,alignment
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1
CFLAGS_MONGOOSE += -DMG_TLS=MG_TLS_BUILTIN
#CFLAGS_MONGOOSE += -DMG_TLS=MG_TLS_OPENSSL -L/opt/homebrew/opt/openssl/lib -I/opt/homebrew/opt/openssl/include -lssl -lcrypto
#CFLAGS_MONGOOSE += -DMG_TLS=MG_TLS_MBED -L/opt/homebrew/opt/mbedtls/lib -I/opt/homebrew/opt/mbedtls/include -lmbedtls -lmbedcrypto -lmbedx509
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 -lbcrypt # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
endif
all: $(PROG) # Default target. Build and run program
$(RUN) ./$(PROG) $(ARGS)
$(PROG): $(SOURCES) # Build program from sources
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
clean: # Cleanup. Delete built program and all build artifacts
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
+111
View File
@@ -0,0 +1,111 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// This example shows how we can poll an external HTTP server periodically, and
// make sure that not more than one instance of the active request in flight.
// Each request has a configured timeout.
#include "mongoose.h"
static const char *s_url = "https://mongoose.ws/tls/api/ca";
static const char *s_post_data = "hi";
static const uint64_t s_connect_timeout_ms = 5500; // milliseconds
static struct mg_connection *s_conn = NULL;
// TLS certificate, obtained using https://mongoose.ws/tls/
#define TLS_CA \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP\n" \
"MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy\n" \
"Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa\n" \
"Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF\n" \
"bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c\n" \
"S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb\n" \
"R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB\n" \
"9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB\n" \
"MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j\n" \
"cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB\n" \
"BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE\n" \
"DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j\n" \
"ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0\n" \
"RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d\n" \
"AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8\n" \
"otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA\n" \
"aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm\n" \
"Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2\n" \
"HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1\n" \
"Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR\n" \
"xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d\n" \
"tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/\n" \
"jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS\n" \
"u1igv3OefnWjSQ==\n" \
"-----END CERTIFICATE-----"
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
uint64_t *start_time = (uint64_t *) c->data; // Stored start time
bool *success = (bool *) (start_time + 1); // Stored success flag
if (ev == MG_EV_OPEN) {
*start_time = mg_millis();
MG_DEBUG(("%lu Connection started, time: %llu", c->id, *start_time));
} else if (ev == MG_EV_POLL) {
if (*start_time + s_connect_timeout_ms < mg_millis()) {
mg_error(c, "Connect timeout");
}
} else if (ev == MG_EV_CONNECT) {
// Connected to server. Extract host name from URL
struct mg_str host = mg_url_host(s_url);
if (c->is_tls) {
struct mg_tls_opts opts = {};
opts.ca = mg_str(TLS_CA);
opts.name = host;
mg_tls_init(c, &opts);
}
// Send request
int content_length = s_post_data ? strlen(s_post_data) : 0;
mg_printf(c,
"%s %s HTTP/1.0\r\n"
"Host: %.*s\r\n"
"Content-Type: octet-stream\r\n"
"Content-Length: %d\r\n"
"\r\n",
s_post_data ? "POST" : "GET", mg_url_uri(s_url), host.len,
host.buf, content_length);
mg_send(c, s_post_data, content_length);
} else if (ev == MG_EV_HTTP_MSG) {
// Response is received. Print it
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
MG_INFO(("%lu Got response %lu bytes %lu", c->id, hm->body.len, c->rtls.len));
*success = true;
c->is_closing = 1;
} else if (ev == MG_EV_ERROR) {
MG_ERROR(("%lu Connection error: %s", c->id, ev_data));
} else if (ev == MG_EV_CLOSE) {
MG_DEBUG(("%lu Connection closed %lu", c->id, mg_millis() - *start_time));
if (*success == false) MG_ERROR(("%lu NO RESPONSE %lu", c->id, c->rtls.len));
if (c->is_draining == 0) s_conn = NULL;
}
}
static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
// If connection is already there, do nothing. Otherwise, start it
if (s_conn != NULL) {
MG_DEBUG(("Connection is already active..."));
} else {
s_conn = mg_http_connect(mgr, s_url, http_ev_handler, NULL);
}
}
int main(void) {
struct mg_mgr mgr;
mg_log_set(MG_LL_INFO);
mg_mgr_init(&mgr);
// Attempt to start a POST request frequently
mg_timer_add(&mgr, 50, MG_TIMER_REPEAT, timer_fn, &mgr);
for (;;) mg_mgr_poll(&mgr, 10);
mg_mgr_free(&mgr);
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../mongoose.h