This is how I implemented REST API with uIP in NuttX some time ago.
http.c
int format_header(char* buff, int status, int body_len) { if
(body_len > 0) { return snprintf(buff, HTTPD_MAX_HEADERLEN,
"HTTP/1.0 %d %s\r\n" "Connection: %s\r\n" "Content-Length:
%d\r\n" "%s" "\r\n", status, status >= 400 ? "Error" :
"OK", "close", body_len, status < 400 ? "Content-type:
application/json\r\n" : "" ); } else { return snprintf(buff,
HTTPD_MAX_HEADERLEN, "HTTP/1.0 %d %s\r\n" "Connection:
%s\r\n" "%s" "\r\n", status, status >= 400 ? "Error" :
"OK", "close", status < 400 ? "Content-type:
application/json\r\n" : "" ); }}
void send_header(struct httpd_state* pstate, int status, int body_len)
{ char header[HTTPD_MAX_HEADERLEN]; int header_len;
header_len = format_header(header, status, body_len);
send(pstate->ht_sockfd, header, header_len, 0);}
void send_body(struct httpd_state* pstate, char* body, int body_len) {
send(pstate->ht_sockfd, body, body_len, 0);}
void send_reply(struct httpd_state* pstate, int status, char* body,
int body_len) { send_header(pstate, status, body_len);
if (body_len > 0) { send_body(pstate, body, body_len); }}
void send_code(struct httpd_state* pstate, int status) {
send_reply(pstate, status, nullptr, 0);}
static uint8_t already_initialized_http = false;
void http_serve(struct httpd_cgi_call* routes, int n) { if
(!already_initialized_http) { httpd_init();
for (int i = 0; i < n; i++) { httpd_cgi_register(&routes[i]); }
already_initialized_http = true; }
httpd_listen(settings_webserver_port.get());}
rest.c
static void sensors_route(struct httpd_state* pstate, char* ptr) {
static const int sensors_answer_max_size = 524;
char body[sensors_answer_max_size];
int body_len = snprintf(body, sensors_answer_max_size, ""\"{"\
"\"battery\": \"%s\","\ "\"inputs_count\": %d,"\ "\"inputs\": ["\
"{\"name\": \"%s\", \"state\": \"%s\", \"reading\": \"%s\", \"text\":
\"%s\"},"\ "{\"name\": \"%s\", \"state\": \"%s\", \"reading\":
\"%s\", \"text\": \"%s\"},"\ "{\"name\": \"%s\", \"state\": \"%s\",
\"reading\": \"%s\", \"text\": \"%s\"},"\ "{\"name\": \"%s\",
\"state\": \"%s\", \"reading\": \"%s\", \"text\": \"%s\"},"\
"{\"name\": \"%s\", \"state\": \"%s\", \"reading\": \"%s\", \"text\":
\"%s\"},"\ "{\"name\": \"%s\", \"state\": \"%s\", \"reading\":
\"%s\", \"text\": \"%s\"}"\ "]"\"}\n",
power_state_stringify((power_state_t)global_battery_level.get()),
(unsigned char)settings_inputs_count.get(),
sensors_stringify_gas_type((settings_gas_type_t)settings_inputs_gas[0].get()),
sensors_stringify_state((global_sensors_state_t)global_sensors_state[0].get()),
formatted_readings[0], global_inputs_label[0],
... );
send_reply(pstate, 200, body, body_len);}
static struct httpd_cgi_call routes[] = { { nullptr, "/api/info",
info_route }, { nullptr, "/api/channel", set_channel_label }, {
nullptr, "/api/sensors", sensors_route }, { nullptr,
"/api/history_list", history_list_route }, { nullptr, "/api/auth",
auth_route }, { nullptr, "/api/change-password",
change_password_route }
...};
static void backend_worker(void *arg) {
pthread_setname_np(pthread_self(), "backend");
while (true) { http_serve(routes, (sizeof routes / sizeof *routes));
sleep(1);
printf("backend.cxx : restarting backend\n"); }}
void backend_start(void) { pthread_attr_t attr;
pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 5 * 512);
int ret = pthread_create(&backend_thread, &attr,
(pthread_startroutine_t)backend_worker, nullptr);
ASSERT(ret >= 0);}
And then you call backend_start, for example in your application entrypoint.
I am not sure if it is how it should be done, but it was working for me.
Am Di., 3. Juni 2025 um 22:57 Uhr schrieb Tim Hardisty <
timhardist...@gmail.com>:
To try and answer my own question...
It seems (and probably obvious, doh, to all but me) the uIP server only
serves pre-formatted html. No matter what "send" option you choose
(pre-formatted/sendfile/mmap) or the URL/CGI mapping option) the html is
pre-rendered. So NO chance of a form action that calls a CGI function.
CGI functions are not called on-the-fly. It just does not work.
I have now moved to THTTP...and although it serves my pages nicely, I
can't get ssi or other cgi functions to work: so yet more day after day
after day of trying to reverse engineer stuff to find that magic
incantation. Grrr.
FYI thttp example doesn't work either (using BINFS not NXFLAT)
On 31/05/2025 11:16, Tim Hardisty wrote:
I'm using the netutils uIP webserver to provide a simple interface,
served by my board, for configuration, log downloads, firmware
updates, etc.
Forgive me if the terminology is wrong here, but I am trying to find
documentation - NuttX or elsewhere - about the %! "tag" that denotes a
call to a CGI function. Specifically, I am trying to add a form that
calls a script from a button, where do-firmware-update is my CGI
function:
<form action="%! do-firmware-update" method="post"
enctype="multipart/form-data" accept-charset="UTF-8">
This gets served with nothing after the first opening quote character
on this line so i am assuming I am "calling" the script incorrectly
but can't find anything anywhere to tell me how to do this.
Can anyone point me in the right direction?
Thanks!
TimH