blob: 62b6f55103215851174ee1c2172ed02fbd41f558 [file] [log] [blame] [view] [raw]
---
title: facil.io - a mini-framework for C web applications
sidebar: 0.7.x/_sidebar.md
toc: false
---
# {{{title}}}
A Web application in C? It's as easy as:
```c
#include "http.h" /* the HTTP facil.io extension */
// We'll use this callback in `http_listen`, to handles HTTP requests
void on_request(http_s *request);
// These will contain pre-allocated values that we will use often
FIOBJ HTTP_HEADER_X_DATA;
// Listen to HTTP requests and start facil.io
int main(int argc, char const **argv) {
// allocating values we use often
HTTP_HEADER_X_DATA = fiobj_str_new("X-Data", 6);
// listen on port 3000 and any available network binding (NULL == 0.0.0.0)
http_listen("3000", NULL, .on_request = on_request, .log = 1);
// start the server
fio_start(.threads = 1);
// deallocating the common values
fiobj_free(HTTP_HEADER_X_DATA);
// these can be used, but we're ignoring them here.
(void)argc; (void)argv;
}
// Easy HTTP handling
void on_request(http_s *request) {
http_set_cookie(request, .name = "my_cookie", .name_len = 9, .value = "data",
.value_len = 4);
http_set_header(request, HTTP_HEADER_CONTENT_TYPE,
http_mimetype_find("txt", 3));
http_set_header(request, HTTP_HEADER_X_DATA, fiobj_str_new("my data", 7));
http_send_body(request, "Hello World!\r\n", 14);
}
```
## facil.io - more than a powerful HTTP/Websockets server library.
[facil.io](http://facil.io) is an evented Network library written in C. It provides high performance TCP/IP network services by using an evented design that was tested to provide an easy solution to [the C10K problem](http://www.kegel.com/c10k.html).
[facil.io](http://facil.io) includes a mini-framework for Web Applications, with a fast HTTP / WebSocket server, integrated Pub/Sub, optional Redis connectivity, easy JSON handling, [Mustache](http://mustache.github.io) template rendering and more nifty tidbits.
[facil.io](http://facil.io) powers the [HTTP/Websockets Ruby Iodine server](https://github.com/boazsegev/iodine) and it can easily power your application as well.
[facil.io](http://facil.io) should work on Linux / BSD / macOS (and possibly CYGWIN) and is continuously tested on both Linux and macOS.
[facil.io](http://facil.io) is a source code library, making it easy to incorporate into any project. The API was designed for simplicity and extendability, which means writing new extensions and custom network protocols is easy.
[facil.io](http://facil.io)'s core library is a two-file library (`fio.h` and `fio.c`), making it easy to incorporate networking solutions into any project.
I used this library (including the HTTP server) on Linux, Mac OS X and FreeBSD (I had to edit the `makefile` for each environment).
## An easy chat-room example
Here's a simple WebSocket chat-room example:
```c
#include "fio_cli.h"
#include "http.h"
/* Chat-room channel name */
static fio_str_info_s CHAT_CHANNEL = {.len = 8, .data = "chatroom"};
/* *****************************************************************************
WebSocket callbacks
***************************************************************************** */
/* We'll subscribe to the channel's chat channel when a new connection opens */
static void on_open_websocket(ws_s *ws) {
/* subscription - easy as pie */
websocket_subscribe(ws, .channel = CHAT_CHANNEL, .force_text = 1);
/* notify everyone about new (named) visitors */
FIOBJ nickname = (FIOBJ)websocket_udata_get(ws);
fio_str_info_s n = fiobj_obj2cstr(nickname);
FIOBJ msg = fiobj_str_new(n.data, n.len);
fiobj_str_write(msg, " joined the chat.", 17);
pubsub_publish(.channel = CHAT_CHANNEL, .message = fiobj_obj2cstr(msg));
/* cleanup */
fiobj_free(msg);
}
/* Free the nickname, if any. */
static void on_close_websocket(intptr_t uuid, void *udata) {
FIOBJ nickname = (FIOBJ)udata;
fio_str_info_s n = fiobj_obj2cstr(nickname);
/* send notification */
FIOBJ msg = fiobj_str_new(n.data, n.len);
fiobj_str_write(msg, " left the chat.", 15);
fio_publish(.channel = CHAT_CHANNEL, .message = fiobj_obj2cstr(msg));
/* cleanup */
fiobj_free(msg);
fiobj_free(nickname);
(void)uuid;
}
/* Received a message from a client, format message for chat . */
static void handle_websocket_messages(ws_s *ws, fio_str_info_s data,
uint8_t is_text) {
FIOBJ nickname = (FIOBJ)websocket_udata_get(ws);
fio_str_info_s n = fiobj_obj2cstr(nickname);
/* allocates a dynamic string. knowing the buffer size is faster */
FIOBJ msg = fiobj_str_buf(n.len + 2 + data.len);
fiobj_str_write(msg, n.data, n.len);
fiobj_str_write(msg, ": ", 2);
fiobj_str_write(msg, data.data, data.len);
fio_publish(.channel = CHAT_CHANNEL, .message = fiobj_obj2cstr(msg));
fiobj_free(msg);
(void)(ws);
(void)(is_text);
}
/* *****************************************************************************
HTTP Handling (Upgrading to WebSocket)
***************************************************************************** */
/* Answers simple HTTP requests */
static void answer_http_request(http_s *h) {
http_set_header2(h, (fio_str_info_s){.data = "Server", .len = 6},
(fio_str_info_s){.data = "facil.example", .len = 13});
http_set_header(h, HTTP_HEADER_CONTENT_TYPE, http_mimetype_find("txt", 3));
/* this both sends the response and frees the http handler. */
http_send_body(h, "This is a simple Websocket chatroom example.", 44);
}
/* tests that the target protocol is "websockets" and upgrades the connection */
static void answer_http_upgrade(http_s *h, char *target, size_t len) {
/* test for target protocol name */
if (len != 9 || memcmp(target, "websocket", 9)) {
http_send_error(h, 400);
return;
}
FIOBJ n;
fio_str_info_s path = fiobj_obj2cstr(h->path);
if (path.len > 1) {
n = fiobj_str_new(path.data + 1, path.len - 1);
} else {
n = fiobj_str_new("Guest", 5);
}
// Websocket upgrade will use our existing response.
if (http_upgrade2ws(
h, .on_open = on_open_websocket, .on_close = on_close_websocket,
.on_message = handle_websocket_messages, .udata = (void *)n))
fiobj_free(n);
}
/* *****************************************************************************
The main function
***************************************************************************** */
int main(int argc, char const *argv[]) {
fio_cli_start(
argc, argv, 0, 0, "WebSocket chat room example using facil.io",
FIO_CLI_INT("-t number of threads"),
FIO_CLI_INT("-w number of workers"),
FIO_CLI_INT("-p port number to listen on (0 == unix socket)"),
FIO_CLI_STRING("-b address binding"),
FIO_CLI_STRING("-www a public folder from which to serve files"),
FIO_CLI_BOOL("-v logs requests to STDERR"));
fio_cli_set_default("-p", "3000");
if (http_listen(fio_cli_get("-p"), fio_cli_get("-b"),
.on_request = answer_http_request,
.on_upgrade = answer_http_upgrade,
.log = fio_cli_get_bool("-v"),
.public_folder = fio_cli_get("-www")) == -1) {
perror("Couldn't listen for HTTP / WebSocket connections");
exit(1);
}
fio_start(.threads = fio_cli_get_i("-t"), .workers = fio_cli_get_i("-w"));
fio_cli_end();
}
```
## Further reading
The code in this project is heavily commented and the header files could (and probably should) be used as the actual documentation.
However, experience shows that a quick reference guide is immensely helpful and that Doxygen documentation is ... well ... less helpful and harder to navigate (I'll leave it at that for now).
I hope you find the documentation on this website helpful.