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.
186 lines
4.1 KiB
186 lines
4.1 KiB
2 years ago
|
#include "../include/servers/fcgi.h"
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#ifdef MT_ENABLED
|
||
|
#include <pthread.h>
|
||
|
#endif /* MT_ENABLED */
|
||
|
|
||
|
typedef struct {
|
||
|
rpd_app *app;
|
||
|
int sock_id;
|
||
|
} thread_data;
|
||
|
|
||
|
/*!
|
||
|
* \brief Run FastCGI requests listening in loop.
|
||
|
* \param data \see thread_data.
|
||
|
* \return NULL;
|
||
|
*/
|
||
|
static void *listen_requests(void *data);
|
||
|
|
||
|
/*!
|
||
|
* \brief Handle Request.
|
||
|
*
|
||
|
* This function converts FastCGI request into the Rapida request
|
||
|
* and calls Rapida request handler.
|
||
|
*
|
||
|
* \param app Application instance.
|
||
|
* \param fcgx_req FastCGI Request.
|
||
|
*/
|
||
|
static void handle_request(rpd_app *app, FCGX_Request *fcgx_req);
|
||
|
|
||
|
/*!
|
||
|
* \brief Convert FastCGI request into the Rapida request.
|
||
|
*
|
||
|
* \param dest Rapida request.
|
||
|
* \param req FastCGI request.
|
||
|
*
|
||
|
* \return Status code. 0 is success.
|
||
|
*/
|
||
|
static int fcgx_to_rpd_req(rpd_req *dest, FCGX_Request *req);
|
||
|
|
||
|
/*!
|
||
|
* Read FastCGI request body.
|
||
|
* \param dest Destination buffer.
|
||
|
* \param req FastCGI request.
|
||
|
* \return Status code. 0 is success.
|
||
|
*/
|
||
|
static int read_fcgx_req_body(char **dest, FCGX_Request *req);
|
||
|
|
||
|
/*!
|
||
|
* \brief Sends response to client.
|
||
|
* \param res Response instance.
|
||
|
* \param out Output stream.
|
||
|
*/
|
||
|
static void send_response(rpd_res *res, FCGX_Stream *out);
|
||
|
|
||
|
int rpd_fcgi_server_start(rpd_app *app, const char *sock_path)
|
||
|
{
|
||
|
FCGX_Init();
|
||
|
int sock_id = 0;
|
||
|
if ((sock_id = FCGX_OpenSocket(sock_path, 10)) < 0) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
thread_data data = { app, sock_id };
|
||
|
#ifdef MT_ENABLED
|
||
|
pthread_t threads[NTHREADS];
|
||
|
for (int i = 0; i < NTHREADS; i++) {
|
||
|
pthread_create(&threads[i], 0, &listen_requests, (void *) &data);
|
||
|
pthread_join(threads[i], 0);
|
||
|
}
|
||
|
#else
|
||
|
listen_requests((void *) &data);
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void *listen_requests(void *data)
|
||
|
{
|
||
|
thread_data *hdata = (thread_data *) data;
|
||
|
FCGX_Request req;
|
||
|
if (FCGX_InitRequest(&req, hdata->sock_id, 0))
|
||
|
return 0;
|
||
|
|
||
|
hdata->app->running = 1;
|
||
|
while (hdata->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(hdata->app, &req);
|
||
|
|
||
|
#ifdef MT_ENABLED
|
||
|
pthread_mutex_unlock(&handle_mutex);
|
||
|
#endif
|
||
|
|
||
|
FCGX_Finish_r(&req);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void handle_request(rpd_app *app, FCGX_Request *fcgx_req)
|
||
|
{
|
||
|
rpd_req *req;
|
||
|
rpd_res *res;
|
||
|
|
||
|
req = (rpd_req *) malloc(sizeof(rpd_req));
|
||
|
res = (rpd_res *) malloc(sizeof(rpd_res));
|
||
|
|
||
|
fcgx_to_rpd_req(req, fcgx_req);
|
||
|
rpd_res_init(res);
|
||
|
|
||
|
rpd_app_handle_request(app, req, res);
|
||
|
send_response(res, fcgx_req->out);
|
||
|
|
||
|
rpd_req_cleanup(req);
|
||
|
rpd_res_cleanup(res);
|
||
|
free(req);
|
||
|
free(res);
|
||
|
}
|
||
|
|
||
|
static void send_response(rpd_res *res, FCGX_Stream *out)
|
||
|
{
|
||
|
char *buff;
|
||
|
if (rpd_res_str(&buff, res)) {
|
||
|
FCGX_PutS("Status: 500\r\n\r\n", out);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
FCGX_PutS(buff, out);
|
||
|
free(buff);
|
||
|
}
|
||
|
|
||
|
static int fcgx_to_rpd_req(rpd_req *dest, FCGX_Request *req)
|
||
|
{
|
||
|
rpd_query_parse(&dest->query, FCGX_GetParam("QUERY_STRING", req->envp));
|
||
|
|
||
|
dest->method = rpd_req_smethod(FCGX_GetParam("REQUEST_METHOD", req->envp));
|
||
|
rpd_url_parse(&dest->path, FCGX_GetParam("DOCUMENT_URI", req->envp));
|
||
|
dest->auth = FCGX_GetParam("HTTP_AUTHORIZATION", req->envp);
|
||
|
dest->cookie = FCGX_GetParam("HTTP_COOKIE", req->envp);
|
||
|
rpd_keyval_init(&dest->params, 0);
|
||
|
|
||
|
if (dest->method != GET) {
|
||
|
if (read_fcgx_req_body(&dest->body, req)) {
|
||
|
return 2;
|
||
|
}
|
||
|
} else {
|
||
|
dest->body = NULL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int read_fcgx_req_body(char **dest, FCGX_Request *req)
|
||
|
{
|
||
|
char *clen = FCGX_GetParam("CONTENT_LENGTH", req->envp);
|
||
|
if (!clen)
|
||
|
return 1;
|
||
|
size_t len = atoll(clen);
|
||
|
|
||
|
*dest = (char *) malloc(sizeof(char) * (len + 1));
|
||
|
if (!*dest)
|
||
|
return 2;
|
||
|
|
||
|
*dest[len] = '\0';
|
||
|
FCGX_GetStr(*dest, len, req->in);
|
||
|
|
||
|
return 0;
|
||
|
}
|