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

/* 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;
}