C and C++ web framework.
http://rapida.vilor.one/docs
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
3.9 KiB
174 lines
3.9 KiB
/* SPDX-License-Identifier: GPL-3.0-or-later */ |
|
/* Copyright 2022 Ivan Polyakov */ |
|
|
|
#include "app.h" |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
#ifdef MT_ENABLED |
|
#include <pthread.h> |
|
#endif /* MT_ENABLED */ |
|
|
|
/*! |
|
* \brief Listens new requests by FastCGI protocol. |
|
* \param app Pointer to current App instance. |
|
* \return Always returns NULL. |
|
*/ |
|
static void *listen_requests(void *app); |
|
|
|
/*! |
|
* Handle accepted request. |
|
* \param app Application instance. |
|
* \param req FastCGI request. |
|
*/ |
|
static void handle_request(rpd_app *app, FCGX_Request *fcgx_req); |
|
|
|
/*! |
|
* \brief Selects a route handler based on requested path. |
|
* \param app Application instance. |
|
* \param req Request. |
|
* \return Pointer to route handler. |
|
*/ |
|
static rpd_route *routes_fabric(rpd_app *app, rpd_req *req); |
|
|
|
int rpd_app_create(rpd_app *app, const char *sock_path) |
|
{ |
|
app->running = app->sock_id = app->routes_len = 0; |
|
app->routes = NULL; |
|
|
|
app->sock_path = strdup(sock_path); |
|
if (!app->sock_path) |
|
return 1; |
|
|
|
FCGX_Init(); |
|
|
|
return 0; |
|
} |
|
|
|
int rpd_app_start(rpd_app *app) |
|
{ |
|
if ((app->sock_id = FCGX_OpenSocket(app->sock_path, 10)) < 0) { |
|
return 1; |
|
} |
|
|
|
#ifdef MT_ENABLED |
|
pthread_t threads[NTHREADS]; |
|
for (int i = 0; i < NTHREADS; i++) { |
|
pthread_create(&threads[i], 0, listen_requests, (void *) app); |
|
pthread_join(threads[i], 0); |
|
} |
|
#else |
|
listen_requests((void *) app); |
|
#endif |
|
|
|
return 0; |
|
} |
|
|
|
int rpd_app_add_route(rpd_app *app, const char *path, rpd_route_cb cb, |
|
void *userdata) |
|
{ |
|
rpd_route route; |
|
if (rpd_route_init(&route, path, cb, userdata)) |
|
return 1; |
|
|
|
app->routes = (rpd_route *) realloc(app->routes, sizeof(rpd_route) * (app->routes_len + 1)); |
|
if (!app->routes) |
|
return 2; |
|
|
|
app->routes[app->routes_len++] = route; |
|
return 0; |
|
} |
|
|
|
static void *listen_requests(void *userdata) |
|
{ |
|
rpd_app *app = (rpd_app *) userdata; |
|
|
|
FCGX_Request req; |
|
if (FCGX_InitRequest(&req, app->sock_id, 0)) { |
|
return 0; |
|
} |
|
|
|
app->running = 1; |
|
while (app->running) { |
|
#ifdef MT_ENABLED |
|
static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
pthread_mutex_lock(&accept_mutex); |
|
#endif |
|
|
|
int rc = FCGX_Accept_r(&req); |
|
|
|
#ifdef MT_ENABLED |
|
pthread_mutex_unlock(&accept_mutex); |
|
#endif |
|
|
|
if (rc < 0) { |
|
break; |
|
} |
|
|
|
#ifdef MT_ENABLED |
|
static pthread_mutex_t handle_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
pthread_mutex_lock(&handle_mutex); |
|
#endif |
|
|
|
handle_request(app, &req); |
|
|
|
#ifdef MT_ENABLED |
|
pthread_mutex_unlock(&handle_mutex); |
|
#endif |
|
|
|
FCGX_Finish_r(&req); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void handle_request(rpd_app *app, FCGX_Request *fcgx_req) |
|
{ |
|
rpd_req req; |
|
rpd_res res; |
|
|
|
rpd_req_parse(&req, fcgx_req); |
|
rpd_res_init(&res, fcgx_req); |
|
|
|
// get route and process request |
|
rpd_route *route = routes_fabric(app, &req); |
|
if (!route) { |
|
return; |
|
} |
|
|
|
route->cb(&req, &res, route->userdata); |
|
|
|
rpd_res_send(&res); |
|
|
|
rpd_req_cleanup(&req); |
|
rpd_res_cleanup(&res); |
|
} |
|
|
|
static rpd_route *routes_fabric(rpd_app *app, rpd_req *req) |
|
{ |
|
const rpd_url *req_path, *route_path = 0; |
|
req_path = &req->path; |
|
|
|
for (int i = 0; i < app->routes_len; i++) { |
|
route_path = &app->routes[i].path; |
|
if (req_path->parts_len != route_path->parts_len) |
|
continue; |
|
|
|
int match = 1; |
|
for (int j = 0; j < route_path->parts_len && match; j++) { |
|
int cur_part_is_dyn = route_path->parts[i][0] == ':'; |
|
if (!cur_part_is_dyn && strcmp(req_path->parts[i], route_path->parts[i])) { |
|
match = 0; |
|
} |
|
} |
|
|
|
if (!match) |
|
continue; |
|
|
|
rpd_keyval_init(&req->params, 0); |
|
rpd_url_params_parse_keys(&req->params, route_path); |
|
rpd_url_params_parse_vals(&req->params, req_path, route_path); |
|
return &app->routes[i]; |
|
} |
|
return NULL; |
|
}
|
|
|