Skip to content

A threaded HTTP library for building REST services in C.

License

Notifications You must be signed in to change notification settings

cluoma/bittyhttp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bittyhttp

A Linux, threaded HTTP library and basic webserver for creating REST services in C.

Building

make lib
make example

Basic Usage

#include "server.h"

int helloworld_handler(bhttp_request *req, bhttp_response *res);

int
main(int argc, char **argv)
{
    bhttp_server *server = bhttp_server_new();
    if (server == NULL) return 1;
    
    bhttp_server_set_ip(server, "0.0.0.0");
    bhttp_server_set_port(server, "8989");
    bhttp_server_set_docroot(server, "./www");
    bhttp_server_set_dfile(server, "index.html");
    
    bhttp_add_simple_handler(server,
                             BHTTP_GET | BHTTP_POST,  // declare supported http methods
                             "/helloworld",           // pattern to match uri path
                             helloworld_handler);     // callback function pointer
    
    bhttp_server_start(server, 0);
    
    return 0;
}

Starting Server

The second argument to bhttp_server_start decides how the server is started. Setting own_thread to 1 will start bittyhttp in a separate thread and return immediately. It is then up to the caller to later call bhttp_server_stop and bhttp_server_free.

While running in a separate thread, it is possible for the caller to add new handlers.

Alternatively, setting own_thread to 0 with take over the calling thread and will never return unless bittyhttp encounters an error.

Handlers

In addition to simply serving files, bittyhttp also has several different handler types that the user can define. Check out examples/examples.c for even more examples.

Handlers are matched in the order they are added. If two handlers would match the same uri path, then the handler added first will get the callback.

Simple Handler

Simple handlers must match the uri path exactly.

int
helloworld_handler(bhttp_request *req, bhttp_response *res)
{
    /* business logic */
    bstr bs;
    bstr_init(&bs);
    bstr_append_printf(&bs, "<html><p>Hello, world! from URL: %s</p><p>%s</p><p>%s</p></html>",
                       bstr_cstring(&req->uri),
                       bstr_cstring(&req->uri_path),
                       bstr_cstring(&req->uri_query));
    bhttp_res_set_body_text(res, bstr_cstring(&bs));
    bstr_free_contents(&bs);
    
    /* add custom headers and response code */
    bhttp_res_add_header(res, "content-type", "text/html");
    res->response_code = BHTTP_200_OK;
    return 0;
}

bhttp_add_simple_handler(&server, BHTTP_GET, "/helloworld", helloworld_handler);

Regex Handler

Regex handlers use Linux's POSIX regex library to match on the uri path. Any matched groups will also be passed to the handler function.

int
helloworld_regex_handler(bhttp_request *req, bhttp_response *res, bvec *args)
{
    /* business logic */
    bstr bs;
    bstr_init(&bs);
    bstr_append_printf(&bs, "<html><p>Hello, Regex world! from URL: %s</p><p>%s</p><p>%s</p>",
                       bstr_cstring(&req->uri),
                       bstr_cstring(&req->uri_path),
                       bstr_cstring(&req->uri_query));
    /* check the request for a specific header */
    bhttp_header *h = bhttp_req_get_header(req, "accept-encoding");
    if (h)
        bstr_append_printf(&bs, "<p><b>accept-encoding</b>: %s</p>", bstr_cstring(&h->value));
    /* add all Regex matched groups to our output */
    for (int i = 0; i < bvec_count(args); i++)
    {
        bstr *arg = bvec_get(args, i);
        bstr_append_printf(&bs, "<p>arg: %d: %s</p>", i, bstr_cstring(arg));
    }
    bstr_append_cstring_nolen(&bs, "</html>");

    bhttp_res_set_body_text(res, bstr_cstring(&bs));
    bstr_free_contents(&bs);
    /* custom headers and response code */
    bhttp_res_add_header(res, "content-type", "text/html");
    res->response_code = BHTTP_200_OK;
    return 0;
}

bhttp_add_regex_handler(&server, BHTTP_GET | BHTTP_HEAD, "^/api/([^/]+)/([^/]+)$", helloworld_regex_handler);

File Handlers

Instead of using bhttp_res_set_body_text, we can use the function bhttp_set_body_file_rel/abs to return a file. This is more efficient than than supplying the binary data ourselves because sendfile can avoid unecessary data copying.

bhttp_res_set_body_file_rel will append the uri path to bittyhttps docroot. bhttp_res_set_body_file_abs will take the given filepath as is, and try to serve it.

If bittyhttp cannot read the file or the file is not found, a 404 message is returned.

int
rel_file_handler(bhttp_request *req, bhttp_response *res)
{
    bhttp_res_set_body_file_rel(res, "/hugo/404.html");
    res->response_code = BHTTP_200_OK;
    return 0;
}

bhttp_add_simple_handler(&server, BHTTP_GET, "/rel_file", rel_file_handler);

Threads

bittyhttp uses a new thread for each request. It is recommended that only threadsafe functions be used inside callback handlers. Additionally, appropriate structures should be used when callback handlers access the same data: mutexes, pools, etc.

Sites Using bittyhttp

  • squid poll - create and share polls for fun. it's squidtastic!

Roadmap

In the future I would like to add the following features to bittyhttp:

  • file/multipart upload support
  • Lua integration for handlers

Use of other code

This project would not be possible without the following resources: