Browse Source

tcp server

pull/1/head
Ivan Polyakov 2 years ago
parent
commit
3745d2003a
  1. 3
      Makefile
  2. 1
      README.md
  3. 51
      c/response.c
  4. 10
      config.mk
  5. 2
      docs/src/development.dox
  6. 17
      docs/src/get_started.dox
  7. 8
      docs/src/index.dox
  8. 5
      examples/c/example.c
  9. 9
      examples/c/minimal.c
  10. 4
      examples/cxx/example.cxx
  11. 4
      examples/cxx/minimal.cxx
  12. 3
      include/App.hxx
  13. 1
      include/Response.hxx
  14. 3
      include/app.h
  15. 13
      include/response.h
  16. 30
      include/servers/tcp.h
  17. 3
      servers/fcgi.c
  18. 102
      servers/tcp.c

3
Makefile

@ -2,6 +2,9 @@ include config.mk
CXXSRC := $(shell find cxx -type f -name '*.cxx') CXXSRC := $(shell find cxx -type f -name '*.cxx')
CSRC += $(shell find c -type f -name '*.c') CSRC += $(shell find c -type f -name '*.c')
ifeq ($(TCP_SERVER), 1)
CSRC += servers/tcp.c
endif
ifeq ($(FCGI_SERVER), 1) ifeq ($(FCGI_SERVER), 1)
CSRC += servers/fcgi.c CSRC += servers/fcgi.c
endif endif

1
README.md

@ -8,6 +8,7 @@ Web framework written in C and C++.
Dependencies Dependencies
------------ ------------
* mongoose (if you need a TCP server)
* libfcgi (if you need a FastCGI server) * libfcgi (if you need a FastCGI server)
* doxygen (to make docs) * doxygen (to make docs)
* catch2 (to run tests) * catch2 (to run tests)

51
c/response.c

@ -6,7 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
static size_t calc_res_buff_sz(const rpd_res *res); static size_t calc_res_headers_sz(const rpd_res *res);
void rpd_res_init(rpd_res *dest) void rpd_res_init(rpd_res *dest)
{ {
@ -17,27 +17,54 @@ void rpd_res_init(rpd_res *dest)
rpd_keyval_init(&dest->cookie, 0); rpd_keyval_init(&dest->cookie, 0);
} }
int rpd_res_headers_str(char **dest, const rpd_res *src)
{
size_t size = calc_res_headers_sz(src);
char *ptr;
*dest = (char *) malloc(sizeof(char) * size);
if (!*dest) {
perror("malloc");
return 1;
}
ptr = *dest;
if (src->content_type) {
ptr += sprintf(ptr, "Content-Type: %s\r\n", src->content_type);
}
if (src->location) {
ptr += sprintf(ptr, "Location: %s\r\n", src->location);
}
return 0;
}
int rpd_res_str(char **dest, const rpd_res *res) int rpd_res_str(char **dest, const rpd_res *res)
{ {
size_t size = calc_res_buff_sz(res); size_t headers_size, size;
char *ptr, *headers;
size = headers_size = calc_res_headers_sz(res);
if (res->body)
size += 2 + strlen(res->body);
*dest = (char *) malloc(sizeof(char) * size); *dest = (char *) malloc(sizeof(char) * size);
if (!*dest) { if (!*dest) {
perror("malloc");
return 1; return 1;
} }
/* header */ /* header */
char *ptr = *dest; ptr = *dest;
ptr += sprintf(ptr, "Status: %d\r\n", res->status); ptr += sprintf(ptr, "Status: %d\r\n", res->status);
if (res->content_type) { if (rpd_res_headers_str(&headers, res)) {
ptr += sprintf(ptr, "Content-Type: %s\r\n", res->content_type); return 1;
}
if (res->location) {
ptr += sprintf(ptr, "Location: %s\r\n", res->location);
} }
memcpy(ptr, headers, headers_size);
free(headers);
ptr += headers_size;
memcpy(ptr, "\r\n", 2); memcpy(ptr, "\r\n", 2);
ptr += 2; ptr += 2;
@ -51,9 +78,9 @@ int rpd_res_str(char **dest, const rpd_res *res)
return 0; return 0;
} }
static size_t calc_res_buff_sz(const rpd_res *res) static size_t calc_res_headers_sz(const rpd_res *res)
{ {
size_t size = res->body ? strlen(res->body) + 2 : 0; size_t size = 0;
size += strlen("Status: \r\n") + 3; size += strlen("Status: \r\n") + 3;
if (res->location) { if (res->location) {
@ -72,8 +99,6 @@ static size_t calc_res_buff_sz(const rpd_res *res)
} }
} }
size += 2;
return size; return size;
} }

10
config.mk

@ -1,9 +1,9 @@
VERSION=0.2.2 VERSION=0.3.0
#arg Installation prefix #arg Installation prefix
PREFIX=/usr/local PREFIX=/usr/local
CC=gcc CC=gcc
CFLAGS=-std=c99 -pedantic -Iinclude CFLAGS=-std=c99 -pedantic -Iinclude -DRPD_VERSION=\"$(VERSION)\"
CXX=c++ CXX=c++
CXXFLAGS=-pedantic -Iinclude CXXFLAGS=-pedantic -Iinclude
CXXSTD=-ansi CXXSTD=-ansi
@ -19,6 +19,12 @@ else
CXXFLAGS+=-O3 CXXFLAGS+=-O3
endif endif
#flag Add TCP server
TCP_SERVER ?= 1
ifeq ($(TCP_SERVER), 1)
LDFLAGS += -lmongoose
endif
#flag Add FastCGI server #flag Add FastCGI server
FCGI_SERVER ?= 1 FCGI_SERVER ?= 1
ifeq ($(FCGI_SERVER), 1) ifeq ($(FCGI_SERVER), 1)

2
docs/src/development.dox

@ -2,6 +2,8 @@
\page development Development \page development Development
[TOC]
Overview Overview
-------- --------
We use: We use:

17
docs/src/get_started.dox

@ -2,6 +2,8 @@
\page get_started Get Started \page get_started Get Started
[TOC]
Installation Installation
------------ ------------
Run the following commands: Run the following commands:
@ -37,4 +39,19 @@ gcc myapp.c -lrapida # link C library
g++ myapp.cxx -lrapidaxx # link C++ library g++ myapp.cxx -lrapidaxx # link C++ library
``` ```
Servers
-------
Rapida has focused on the implementation of the framework,
and servers are logically separated form the core.
By default, when you building Rapida, all servers will be linked
to the library, but you can change this by setting the _make_ flags.
For example, to disable FastCGI server, you need to pass `FCGI_SERVER=0`
flag to `make` command like this:
```sh
make FCGI_SERVER=0
```
To see other flags you can run `make help`.
*/ */

8
docs/src/index.dox

@ -1,11 +1,11 @@
/** /**
\mainpage Rapida Manual \mainpage Manual
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
*/ */

5
examples/c/example.c

@ -1,5 +1,5 @@
#include "../../include/rapida.h" #include "../../include/rapida.h"
#include "../../include/servers/fcgi.h" #include "../../include/servers/tcp.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -52,6 +52,5 @@ int main()
rpd_app_add_route(&app, "/products/:category/:id", &products_handler, NULL); rpd_app_add_route(&app, "/products/:category/:id", &products_handler, NULL);
return rpd_fcgi_server_start(&app, "/tmp/webapp.socket"); return rpd_tcp_server_start(&app, "http://localhost:8080");
return 0;
} }

9
examples/c/minimal.c

@ -1,8 +1,6 @@
#include "../../include/rapida.h" #include "../../include/rapida.h"
#include "../../include/servers/fcgi.h" #include "../../include/servers/tcp.h"
#include <string.h> /* for strdup() */ #include <string.h> /* for strdup() */
#include <stdio.h>
/* /*
* \brief Process home page request. * \brief Process home page request.
@ -16,7 +14,7 @@ static void home_page_handler(rpd_req *req, rpd_res *res, void *userdata)
{ {
/* Check request method */ /* Check request method */
switch (req->method) { switch (req->method) {
case GET: case HEAD:
/* Process GET request */ /* Process GET request */
res->status = rpd_res_st_ok; res->status = rpd_res_st_ok;
@ -25,6 +23,7 @@ static void home_page_handler(rpd_req *req, rpd_res *res, void *userdata)
* Rapida will free it all. * Rapida will free it all.
*/ */
res->content_type = strdup("text/plain"); res->content_type = strdup("text/plain");
case GET:
res->body = strdup("Hello World!"); res->body = strdup("Hello World!");
break; break;
default: default:
@ -51,5 +50,5 @@ int main()
rpd_app_add_route(&app, "/", &home_page_handler, NULL); rpd_app_add_route(&app, "/", &home_page_handler, NULL);
/* Run the application and return its status code. */ /* Run the application and return its status code. */
return rpd_fcgi_server_start(&app, "/tmp/webapp.socket"); return rpd_tcp_server_start(&app, "http://localhost:8080");
} }

4
examples/cxx/example.cxx

@ -1,5 +1,5 @@
#include "../../include/rapida.hxx" #include "../../include/rapida.hxx"
#include "../../include/servers/fcgi.h" #include "../../include/servers/tcp.h"
#include <sstream> #include <sstream>
class ProductRoute : public rpd::Route { class ProductRoute : public rpd::Route {
@ -49,5 +49,5 @@ int main()
*/ */
app.add_route("/products/:category/:id", new ProductRoute()); app.add_route("/products/:category/:id", new ProductRoute());
return rpd_fcgi_server_start(app.c_app(), "/tmp/webapp.socket"); return rpd_tcp_server_start(app.c_app(), "http://localhost:8080");
} }

4
examples/cxx/minimal.cxx

@ -1,5 +1,5 @@
#include "../../include/rapida.hxx" #include "../../include/rapida.hxx"
#include "../../include/servers/fcgi.h" #include "../../include/servers/tcp.h"
/* /*
@ -39,5 +39,5 @@ int main()
app.add_route("/", new Home()); app.add_route("/", new Home());
return rpd_fcgi_server_start(app.c_app(), "/tmp/webapp.socket"); return rpd_tcp_server_start(app.c_app(), "http://localhost:8080");
} }

3
include/App.hxx

@ -16,9 +16,8 @@ namespace rpd {
class App { class App {
public: public:
/*! /*!
* Creates an rpd app and initializes FastCGI. * Creates a Rapida application instance.
* \brief Default constructor * \brief Default constructor
* \param socket_path UNIX Socket path.
*/ */
App() App()
{ {

1
include/Response.hxx

@ -114,7 +114,6 @@ public:
* \brief Render data to HTML template. * \brief Render data to HTML template.
* \param path Path to HTML template relative to dist location. * \param path Path to HTML template relative to dist location.
* \param data Template data to interpolate. * \param data Template data to interpolate.
* \param out FastCGI output stream.
*/ */
void render(const char *path, nlohmann::json data); void render(const char *path, nlohmann::json data);
#endif #endif

3
include/app.h

@ -35,7 +35,8 @@ int rpd_app_create(rpd_app *app);
/*! /*!
* Handle accepted request. * Handle accepted request.
* \param app Application instance. * \param app Application instance.
* \param req FastCGI request. * \param req Request.
* \param res Response.
*/ */
void rpd_app_handle_request(rpd_app *app, rpd_req *req, rpd_res *res); void rpd_app_handle_request(rpd_app *app, rpd_req *req, rpd_res *res);

13
include/response.h

@ -93,13 +93,22 @@ typedef struct {
} rpd_res; } rpd_res;
/*! /*!
* \brief Initialize response data from FastCGI request. * \brief Initialize response data.
* *
* \param dest Response instance. * \param dest Response instance.
* \param req FastCGI request.
*/ */
void rpd_res_init(rpd_res *dest); void rpd_res_init(rpd_res *dest);
/*!
* \brief Write response headers to string buffer.
*
* \param dest Destination buffer.
* \param src Response.
*
* \return Status code. 0 is success.
*/
int rpd_res_headers_str(char **dest, const rpd_res *src);
/*! /*!
* \brief Write response to string buffer. * \brief Write response to string buffer.
* *

30
include/servers/tcp.h

@ -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 */

3
servers/fcgi.c

@ -1,3 +1,6 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright 2022 Ivan Polyakov */
#include "../include/servers/fcgi.h" #include "../include/servers/fcgi.h"
#include <stdlib.h> #include <stdlib.h>

102
servers/tcp.c

@ -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…
Cancel
Save