|
|
|
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
|
|
/* Copyright 2022 Ivan Polyakov */
|
|
|
|
|
|
|
|
#include "servers/tcp.h"
|
|
|
|
#include "../c/utils.h"
|
|
|
|
#include <mongoose.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
/* Handle interrupts, like Ctrl-C */
|
|
|
|
static int s_signo;
|
|
|
|
static void signal_handler(int signo)
|
|
|
|
{
|
|
|
|
s_signo = signo;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mg_str_alloc(char **dest, const struct mg_str str)
|
|
|
|
{
|
|
|
|
*dest = (char *) realloc(*dest, sizeof(char) * (str.len + 1));
|
|
|
|
if (!*dest) {
|
|
|
|
perror("realloc");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
memcpy(*dest, str.ptr, str.len);
|
|
|
|
(*dest)[str.len] = '\0';
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mg_to_rpd_req(rpd_req *req, struct mg_http_message *msg)
|
|
|
|
{
|
|
|
|
char *tmp = NULL;
|
|
|
|
if (mg_str_alloc(&tmp, msg->method))
|
|
|
|
return 1;
|
|
|
|
req->method = rpd_req_smethod(tmp);
|
|
|
|
|
|
|
|
req->body = msg->body.len ? rpd_strdup(msg->body.ptr) : NULL;
|
|
|
|
|
|
|
|
if (mg_str_alloc(&tmp, msg->uri))
|
|
|
|
return 1;
|
|
|
|
rpd_url_parse(&req->path, tmp);
|
|
|
|
|
|
|
|
rpd_keyval_init(&req->query, 0);
|
|
|
|
if (msg->query.len) {
|
|
|
|
if (mg_str_alloc(&tmp, msg->query))
|
|
|
|
return 1;
|
|
|
|
rpd_query_parse(&req->query, tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t i, max = sizeof(msg->headers) / sizeof(msg->headers[0]);
|
|
|
|
rpd_keyval_init(&req->headers, max);
|
|
|
|
req->headers.unique = 0;
|
|
|
|
|
|
|
|
// Iterate over request headers
|
|
|
|
char *key = NULL, *val = NULL;
|
|
|
|
for (i = 0; i < max && msg->headers[i].name.len > 0; i++) {
|
|
|
|
struct mg_str *k = &msg->headers[i].name, *v = &msg->headers[i].value;
|
|
|
|
|
|
|
|
mg_str_alloc(&key, *k);
|
|
|
|
mg_str_alloc(&val, *v);
|
|
|
|
|
|
|
|
rpd_keyval_insert(&req->headers, key, val);
|
|
|
|
}
|
|
|
|
free(key);
|
|
|
|
free(val);
|
|
|
|
|
|
|
|
free(tmp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_request(struct mg_connection *conn, int ev, void *ev_data, void *app)
|
|
|
|
{
|
|
|
|
if (ev == MG_EV_HTTP_MSG) {
|
|
|
|
rpd_req *req;
|
|
|
|
rpd_res *res;
|
|
|
|
char *headers_buff;
|
|
|
|
|
|
|
|
req = (rpd_req *) malloc(sizeof(rpd_req));
|
|
|
|
res = (rpd_res *) malloc(sizeof(rpd_res));
|
|
|
|
|
|
|
|
mg_to_rpd_req(req, (struct mg_http_message *) ev_data);
|
|
|
|
rpd_res_init(res);
|
|
|
|
|
|
|
|
rpd_app_handle_request((rpd_app *) app, req, res);
|
|
|
|
rpd_res_headers_str(&headers_buff, res);
|
|
|
|
mg_http_reply(conn, res->status, headers_buff, res->body ? res->body : "");
|
|
|
|
|
|
|
|
free(headers_buff);
|
|
|
|
rpd_req_cleanup(req);
|
|
|
|
rpd_res_cleanup(res);
|
|
|
|
free(req);
|
|
|
|
free(res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int rpd_tcp_server_start(rpd_app *app, const char *addr)
|
|
|
|
{
|
|
|
|
struct mg_mgr mgr;
|
|
|
|
struct mg_connection *c;
|
|
|
|
app->running = 1;
|
|
|
|
|
|
|
|
/* setup signals handler */
|
|
|
|
signal(SIGINT, signal_handler);
|
|
|
|
signal(SIGTERM, signal_handler);
|
|
|
|
mg_mgr_init(&mgr);
|
|
|
|
|
|
|
|
if ((c = mg_http_listen(&mgr, addr, handle_request, app)) == NULL) {
|
|
|
|
MG_ERROR(("Cannot listen on %s. Use http://ADDR:PORT or :PORT", addr));
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
MG_INFO(("Mongoose version : v%s", MG_VERSION));
|
|
|
|
MG_INFO(("Rapida version : v%s", RPD_VERSION));
|
|
|
|
MG_INFO(("Listening on : %s", addr));
|
|
|
|
while (s_signo == 0 && app->running)
|
|
|
|
mg_mgr_poll(&mgr, 1000);
|
|
|
|
|
|
|
|
app->running = 0;
|
|
|
|
MG_INFO(("Exiting on signal %d", s_signo));
|
|
|
|
mg_mgr_free(&mgr);
|
|
|
|
return 0;
|
|
|
|
}
|