30 changed files with 578 additions and 259 deletions
@ -1,11 +1,11 @@ |
|||||||
/** |
/** |
||||||
|
|
||||||
\mainpage Rapida Manual |
\mainpage Manual |
||||||
|
|
||||||
Rapida is the fast web framework written in C and C++. |
Web framework written in C and C++. |
||||||
|
|
||||||
Table of contents: |
[TOC] |
||||||
1. \subpage get_started |
- \subpage get_started |
||||||
2. \subpage development |
- \subpage development |
||||||
|
|
||||||
*/ |
*/ |
||||||
|
@ -0,0 +1,31 @@ |
|||||||
|
/* SPDX-License-Identifier: GPL-3.0-or-later */ |
||||||
|
/* Copyright 2022 Ivan Polyakov */ |
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file fcgi.h |
||||||
|
* \brief Rapida FastCGI server |
||||||
|
*/ |
||||||
|
#ifndef RAPIDA_SERVERS_FCGI_H_ENTRY |
||||||
|
#define RAPIDA_SERVERS_FCGI_H_ENTRY |
||||||
|
|
||||||
|
#include "../app.h" |
||||||
|
#include <fcgiapp.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Starts Rapida FastCGI server. |
||||||
|
* \param app Application instance. |
||||||
|
* \param sock_path UNIX Socket path. |
||||||
|
* |
||||||
|
* \return Status. 0 is success. |
||||||
|
*/ |
||||||
|
int rpd_fcgi_server_start(rpd_app *app, const char *sock_path); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* RAPIDA_SERVERS_FCGI_H_ENTRY */ |
@ -0,0 +1,30 @@ |
|||||||
|
/* SPDX-License-Identifier: GPL-3.0-or-later */ |
||||||
|
/* Copyright 2022 Ivan Polyakov */ |
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file tcp.h |
||||||
|
* \brief Rapida TCP server |
||||||
|
*/ |
||||||
|
#ifndef RAPIDA_SERVERS_TCP_H_ENTRY |
||||||
|
#define RAPIDA_SERVERS_TCP_H_ENTRY |
||||||
|
|
||||||
|
#include "../app.h" |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Starts Rapida TCP server. |
||||||
|
* \param app Application instance. |
||||||
|
* \param addr URL address to listen. |
||||||
|
* |
||||||
|
* \return Status. 0 is success. |
||||||
|
*/ |
||||||
|
int rpd_tcp_server_start(rpd_app *app, const char *addr); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* RAPIDA_SERVERS_TCP_H_ENTRY */ |
@ -0,0 +1,188 @@ |
|||||||
|
/* SPDX-License-Identifier: GPL-3.0-or-later */ |
||||||
|
/* Copyright 2022 Ivan Polyakov */ |
||||||
|
|
||||||
|
#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; |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
/* SPDX-License-Identifier: GPL-3.0-or-later */ |
||||||
|
/* Copyright 2022 Ivan Polyakov */ |
||||||
|
|
||||||
|
#include "servers/tcp.h" |
||||||
|
#include "../c/utils.h" |
||||||
|
#include <mongoose.h> |
||||||
|
#include <signal.h> |
||||||
|
|
||||||
|
/* Handle interrupts, like Ctrl-C */ |
||||||
|
static int s_signo; |
||||||
|
static void signal_handler(int signo) |
||||||
|
{ |
||||||
|
s_signo = signo; |
||||||
|
} |
||||||
|
|
||||||
|
static int mg_str_alloc(char **dest, const struct mg_str str) |
||||||
|
{ |
||||||
|
*dest = (char *) realloc(*dest, sizeof(char) * (str.len + 1)); |
||||||
|
if (!*dest) { |
||||||
|
perror("realloc"); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
memcpy(*dest, str.ptr, str.len); |
||||||
|
(*dest)[str.len] = '\0'; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int mg_to_rpd_req(rpd_req *req, struct mg_http_message *msg) |
||||||
|
{ |
||||||
|
char *tmp = NULL; |
||||||
|
if (mg_str_alloc(&tmp, msg->method)) |
||||||
|
return 1; |
||||||
|
req->method = rpd_req_smethod(tmp); |
||||||
|
|
||||||
|
req->body = msg->body.len ? rpd_strdup(msg->body.ptr) : NULL; |
||||||
|
|
||||||
|
if (mg_str_alloc(&tmp, msg->uri)) |
||||||
|
return 1; |
||||||
|
rpd_url_parse(&req->path, tmp); |
||||||
|
|
||||||
|
rpd_keyval_init(&req->query, 0); |
||||||
|
if (msg->query.len) { |
||||||
|
if (mg_str_alloc(&tmp, msg->query)) |
||||||
|
return 1; |
||||||
|
rpd_query_parse(&req->query, tmp); |
||||||
|
} |
||||||
|
|
||||||
|
free(tmp); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void handle_request(struct mg_connection *conn, int ev, void *ev_data, void *app) |
||||||
|
{ |
||||||
|
if (ev == MG_EV_HTTP_MSG) { |
||||||
|
rpd_req *req; |
||||||
|
rpd_res *res; |
||||||
|
char *headers_buff; |
||||||
|
|
||||||
|
req = (rpd_req *) malloc(sizeof(rpd_req)); |
||||||
|
res = (rpd_res *) malloc(sizeof(rpd_res)); |
||||||
|
|
||||||
|
mg_to_rpd_req(req, (struct mg_http_message *) ev_data); |
||||||
|
rpd_res_init(res); |
||||||
|
|
||||||
|
rpd_app_handle_request((rpd_app *) app, req, res); |
||||||
|
rpd_res_headers_str(&headers_buff, res); |
||||||
|
mg_http_reply(conn, res->status, headers_buff, res->body ? res->body : ""); |
||||||
|
|
||||||
|
free(headers_buff); |
||||||
|
rpd_req_cleanup(req); |
||||||
|
rpd_res_cleanup(res); |
||||||
|
free(req); |
||||||
|
free(res); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int rpd_tcp_server_start(rpd_app *app, const char *addr) |
||||||
|
{ |
||||||
|
struct mg_mgr mgr; |
||||||
|
struct mg_connection *c; |
||||||
|
app->running = 1; |
||||||
|
|
||||||
|
/* setup signals handler */ |
||||||
|
signal(SIGINT, signal_handler); |
||||||
|
signal(SIGTERM, signal_handler); |
||||||
|
mg_mgr_init(&mgr); |
||||||
|
|
||||||
|
if ((c = mg_http_listen(&mgr, addr, handle_request, app)) == NULL) { |
||||||
|
MG_ERROR(("Cannot listen on %s. Use http://ADDR:PORT or :PORT", addr)); |
||||||
|
return EXIT_FAILURE; |
||||||
|
} |
||||||
|
MG_INFO(("Mongoose version : v%s", MG_VERSION)); |
||||||
|
MG_INFO(("Rapida version : v%s", RPD_VERSION)); |
||||||
|
MG_INFO(("Listening on : %s", addr)); |
||||||
|
while (s_signo == 0 && app->running) |
||||||
|
mg_mgr_poll(&mgr, 1000); |
||||||
|
|
||||||
|
app->running = 0; |
||||||
|
MG_INFO(("Exiting on signal %d", s_signo)); |
||||||
|
mg_mgr_free(&mgr); |
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue