/* SPDX-License-Identifier: GPL-3.0-or-later */ /* Copyright 2022 Ivan Polyakov */ #include "app.h" #include #include #ifdef MT_ENABLED #include #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; }