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

1
README.md

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

51
c/response.c

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

10
config.mk

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

2
docs/src/development.dox

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

17
docs/src/get_started.dox

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
\page get_started Get Started
[TOC]
Installation
------------
Run the following commands:
@ -37,4 +39,19 @@ gcc myapp.c -lrapida # link C library @@ -37,4 +39,19 @@ gcc myapp.c -lrapida # 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 @@ @@ -1,11 +1,11 @@
/**
\mainpage Rapida Manual
\mainpage Manual
Web framework written in C and C++.
Table of contents:
1. \subpage get_started
2. \subpage development
[TOC]
- \subpage get_started
- \subpage development
*/

5
examples/c/example.c

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

9
examples/c/minimal.c

@ -1,8 +1,6 @@ @@ -1,8 +1,6 @@
#include "../../include/rapida.h"
#include "../../include/servers/fcgi.h"
#include "../../include/servers/tcp.h"
#include <string.h> /* for strdup() */
#include <stdio.h>
/*
* \brief Process home page request.
@ -16,7 +14,7 @@ static void home_page_handler(rpd_req *req, rpd_res *res, void *userdata) @@ -16,7 +14,7 @@ static void home_page_handler(rpd_req *req, rpd_res *res, void *userdata)
{
/* Check request method */
switch (req->method) {
case GET:
case HEAD:
/* Process GET request */
res->status = rpd_res_st_ok;
@ -25,6 +23,7 @@ static void home_page_handler(rpd_req *req, rpd_res *res, void *userdata) @@ -25,6 +23,7 @@ static void home_page_handler(rpd_req *req, rpd_res *res, void *userdata)
* Rapida will free it all.
*/
res->content_type = strdup("text/plain");
case GET:
res->body = strdup("Hello World!");
break;
default:
@ -51,5 +50,5 @@ int main() @@ -51,5 +50,5 @@ int main()
rpd_app_add_route(&app, "/", &home_page_handler, NULL);
/* 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 @@ @@ -1,5 +1,5 @@
#include "../../include/rapida.hxx"
#include "../../include/servers/fcgi.h"
#include "../../include/servers/tcp.h"
#include <sstream>
class ProductRoute : public rpd::Route {
@ -49,5 +49,5 @@ int main() @@ -49,5 +49,5 @@ int main()
*/
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 @@ @@ -1,5 +1,5 @@
#include "../../include/rapida.hxx"
#include "../../include/servers/fcgi.h"
#include "../../include/servers/tcp.h"
/*
@ -39,5 +39,5 @@ int main() @@ -39,5 +39,5 @@ int main()
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 { @@ -16,9 +16,8 @@ namespace rpd {
class App {
public:
/*!
* Creates an rpd app and initializes FastCGI.
* Creates a Rapida application instance.
* \brief Default constructor
* \param socket_path UNIX Socket path.
*/
App()
{

1
include/Response.hxx

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

3
include/app.h

@ -35,7 +35,8 @@ int rpd_app_create(rpd_app *app); @@ -35,7 +35,8 @@ int rpd_app_create(rpd_app *app);
/*!
* Handle accepted request.
* \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);

13
include/response.h

@ -93,13 +93,22 @@ typedef struct { @@ -93,13 +93,22 @@ typedef struct {
} rpd_res;
/*!
* \brief Initialize response data from FastCGI request.
* \brief Initialize response data.
*
* \param dest Response instance.
* \param req FastCGI request.
*/
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.
*

30
include/servers/tcp.h

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

102
servers/tcp.c

@ -0,0 +1,102 @@ @@ -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