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.
175 lines
3.9 KiB
175 lines
3.9 KiB
2 years ago
|
/* 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;
|
||
|
}
|