/* SPDX-License-Identifier: GPL-3.0-or-later */ /* Copyright 2022 Ivan Polyakov */ #include "servers/tcp.h" #include "../c/utils.h" #include #include /* 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); // 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; }