Cross-platform, single .h file HTTP server (Windows, Linux, Mac OS X)

Overview

EWS - Single .h File C Embeddable Web Server

Latest Version: 1.1.4 released September 9, 2021
Supported platforms: Linux, Mac OS X, Windows
License: BSD 2-clause

Embeddable Web Server is a web server in a single header file and has no external dependencies in the tradition of the STB libraries. It can serve static files and handle responses with C/C++ code (the server compiles in both). Here's how to use it:

  1. #include "EmbeddableWebServer.h" (and #define EWS_HEADER_ONLY if you included it somewhere else)
  2. Call acceptConnectionsUntilStoppedFromEverywhereIPv4(NULL), which will initialize a new server and block Note: If you want to take connections from a specific inteface/localhost you can use acceptConnectionsUntilStopped
  3. Fill out createResponseForRequest. Use the responseAlloc* functions to return a response or take over the connection yourself and return NULL. The easiest way to serve static files is responseAllocServeFileFromRequestPath. The easiest way to serve HTML is responseAllocHTML. The easiest way to serve JSON is responseAllocJSON. The server will free() your response once it's been sent. When compiled with Objective-C, an autoreleasepool will be created for you.
  4. Use the connectionDebugStringCreate to aid in debugging.


See the EWSDemo.cpp file for more examples like chunked transfer, HTML forms, and JSON responses.

If you want to control server setup/teardown use serverInit, serverStop, and serverDeInit and pass that same Server in acceptConnectionsUntilStopped.

Quick Example

#include "EmbeddableWebServer.h"
#pragma comment(lib, "ws2_32")

int main(int argc, char* argv[]) {
	return acceptConnectionsUntilStoppedFromEverywhereIPv4(NULL, 8080);
}

struct Response* createResponseForRequest(const struct Request* request, struct Connection* connection) {
	if (0 == strcmp(request->pathDecoded, "/welcome")) {
		return responseAllocHTML("<html><body><marquee><h1>Welcome to my home page</h1></marquee></body></html>");
	}
	if (0 == strcmp(request->pathDecoded, "/status/json")) {
		static const char* statuses[] = { ":-)", ":-(", ":-|" };
		int status = rand() % (sizeof(statuses) / sizeof(*statuses));
		/* There is also a family of responseAllocJSON functions */
		return responseAllocWithFormat(200, "OK", "application/json", "{ \"status\" : \"%s\" }", statuses[status]);
	}
	if (0 == strcmp(request->pathDecoded, "/100_random_numbers")) {
		struct Response* response = responseAllocHTML("<html><body><h1>100 Random Numbers</h1><ol>");
		for (int i = 1; i <= 100; i++) {
			heapStringAppendFormat(&response->body, "<li>%d</li>\n", rand());
		}
		heapStringAppendString(&response->body, "</ol></body></html>");
		return response;
	}
	/* Serve files from the current directory */
	if (request->pathDecoded == strstr(request->pathDecoded, "/files")) {
		return responseAllocServeFileFromRequestPath("/files", request->path, request->pathDecoded, ".");
	}
	return responseAlloc404NotFoundHTML("What?!");
}

Features and use cases

  • Serve a debug page/dashboard for your application
  • Expose variables for debugging your 3D graphics application
  • Handle HTML GET + POST form data
  • Serve up websites for embedded touch display panels
  • Mix dynamic request handlers with static content
  • Seamless emoji support: Handles UTF-8 and international files, even on Windows (run the demo)
  • Someone is using it for robots

Warning

This server is suitable for controlled applications which will not be accessed over the general Internet. If you are determined to use this on Internet I advise you to use a proxy server in front (like haproxy, squid, or nginx). However I found and fixed only 2 crashes with alf-fuzz...

Implementation

The server is implemented in a thread-per-connection model. This way you can do slow, hacky things in a request and not stall other requests. On the other hand this uses ~40KB + request body + response body of memory per connection. All strings are assumed to be UTF-8. On Windows, UTF-8 file paths are converted to their wide-character (wchar_t) equivalent so you can serve files with Chinese characters and so on.

The server assumes all strings are UTF-8. When accessing the file system on Windows, EWS will convert to/from the wchar_t representation and use the appropriate APIs.

pthreads wrapper for Windows

Since EWS uses threads we need to have a way to launch threads on all platforms. pthreads are supported on most of the operating systems this targets. Hence, EWS targets pthreads directly. EWS includes a very light wrapper for pthreads that supports thread creation, mutexes, and condition variables.

Example of launching a server thread from your app

#include "EmbeddableWebServer.h"
#ifdef WIN32
#pragma comment(lib, "ws2_32") // link against Winsock on Windows
#endif

static int counter = 0;
static struct Server server;
static THREAD_RETURN_TYPE STDCALL_ON_WIN32 acceptConnectionsThread(void* unusedParam) {
    serverInit(&server);
    const uint16_t portInHostOrder = 8080;
    acceptConnectionsUntilStoppedFromEverywhereIPv4(&server, portInHostOrder);
    return (THREAD_RETURN_TYPE) 0;
}

int main() {
    pthread_t threadHandle;
    pthread_create(&threadHandle, NULL, &acceptConnectionsThread, NULL);
    while (1) {
        counter++;
    }
    // rest of the program
    return 0;
}

struct Response* createResponseForRequest(const struct Request* request, struct Connection* connection) {
    return responseAllocHTMLWithFormat("The counter is currently %d\n", counter);
}

Comparison to other really light embeddable web servers

  • yocto HTTP server - yocto has more features (WebSockets, handling deferred requests, custom headers, and can build PNG images on the fly - pretty cool) and lets you spit out the response in pieces. If you want anything custom EWS makes you take over the whole request yourself. yocto also has better MIME type detection. EWS is smaller and handles each connection on a separate thread so one slow response doesn't block the others.
  • Mongoose - mongoose is professionally supported and developed, dual-licensed under GPL and a commercial license you pay for. Mongoose has a huge amount of features. It works with or without an operating system.
  • Baraccuda - Baraccuda from Real-Time logic is a proprietary web server targetting embedded systems. I think they run with and without an OS and include lots of features like Mongoose does.

Change log

1.1.4

1.1.3

1.1.2

  • Fix build error when building with plain C rather than C++

1.1.1

  • When compiled with Objective-C (OBJC is defined), an autoreleasepool is created around the createResponseForRequest function invocation

1.1.0

  • From Daniel Barry: Check for NULL pointer after calling getifaddrs()
  • From Martin Pulec: Added macro to disable snprintf compat
  • From Martin Pulec: Improved IPv6 support
  • Stop leaking string when a directory is requested but viewing directories is disabled
  • Several functions like responseAllocHTMLWithStatus() are ignoring the status code parameter and always using 200. This is breaking functions like responseAlloc404NotFoundHTML(). The browser always gets 200 OK rather than the actual response code.
  • Fixed warnings and updated comments in various places

1.0

  • Initial release

Things I would change

If rewriting this from scratch I would change:

  • Right now all responses are allocated on the heap. They have to be built up into one single response. Instead I would allow for responses to be sent incrementally. So instead of responseAlloc you would have headerSend(connection, "Content-Type", "...") and bodySend("..."). It's a little more tricky to make that work and the onus of ordering goes on the user. Another advantage is that the user can deal with errors when sending pages.
  • Why did I not prefix all functions with something like EWS? EWSServerStart, EWSResponseAlloc. I should have done that. serverStop? Come on!
Comments
  • Segmentation Fault From NULL Address Pointer

    Segmentation Fault From NULL Address Pointer

    In the following method it is possible to run into a segmentation fault (commit 1451d9b92452a0cfa0be64ec298cba42cd5ed818):

    2279 static void printIPv4Addresses(uint16_t portInHostOrder) {
    2280     struct ifaddrs* addrs = NULL;
    2281     getifaddrs(&addrs);
    2282     struct ifaddrs* p = addrs;
    2283     while (NULL != p) {
    2284         if (p->ifa_addr->sa_family == AF_INET) {
    2285             char hostname[256];
    2286             getnameinfo(p->ifa_addr, sizeof(struct sockaddr_in), hostname, sizeof(hostname), NULL, 0, NI_NUMERICHOST);
    2287             ews_printf("Probably listening on http://%s:%u\n", hostname, portInHostOrder);
    2288         }
    2289         p = p->ifa_next;
    2290     }
    2291     if (NULL != addrs) {
    2292         freeifaddrs(addrs);
    2293     }
    2294 }
    

    After calling getifaddrs() it is possible that p->ifa_addr == NULL as per the documentation http://man7.org/linux/man-pages/man3/getifaddrs.3.html :

    The ifa_addr field points to a structure containing the interface address. (The sa_family subfield should be consulted to determine the format of the address structure.) This field may contain a null pointer.

    As the following code relies on the pointer not being NULL and this method having no impact on the actual functionality of the server itself, I suggest a patch as follows:

    From fddf18dab7cb938f9d3fc6b0751d90866d2de4b3 Mon Sep 17 00:00:00 2001
    From: Dan <[email protected]>
    Date: Sun, 24 Mar 2019 01:08:25 +1300
    Subject: [PATCH] Check for NULL pointer after calling getifaddrs()
    
    ---
     EmbeddableWebServer.h | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/EmbeddableWebServer.h b/EmbeddableWebServer.h
    index 129548b..febf093 100755
    --- a/EmbeddableWebServer.h
    +++ b/EmbeddableWebServer.h
    @@ -2281,7 +2281,7 @@ static void printIPv4Addresses(uint16_t portInHostOrder) {
         getifaddrs(&addrs);
         struct ifaddrs* p = addrs;
         while (NULL != p) {
    -        if (p->ifa_addr->sa_family == AF_INET) {
    +        if (p->ifa_addr != NULL && p->ifa_addr->sa_family == AF_INET) {
                 char hostname[256];
                 getnameinfo(p->ifa_addr, sizeof(struct sockaddr_in), hostname, sizeof(hostname), NULL, 0, NI_NUMERICHOST);
                 ews_printf("Probably listening on http://%s:%u\n", hostname, portInHostOrder);
    -- 
    2.17.1
    

    P.S. Thanks for this awesome project, we are using it as part of a local debug server for a humanoid robotics project: https://www.humanoid.science/

    opened by danielbarry 4
  • Msys2 windows compat

    Msys2 windows compat

    Hi @hellerf, thanks again for the great work.

    I'd like to ask you if you could look at those commits. Basically it adds support for:

    • some msys2 flavors by declaring strcasecmp not as a function but a macro (because the function may or may not be defined). This may be possibly also ifdef-decided guarded macro or function but I think that this one is easier.
    • completely disable pthread-compat stuff if winpthreads are used

    Those two are fundamental for me in order to use your upstream, because otherwise it triggers compilation error in my project. I guess that it should not harm other builds, since it is if-defed.

    If you decide to merge, I'd perhaps have a batch of small fixes to merge.

    Thanks for your time! Martin

    opened by MartinPulec 2
  • An improper locking bug(e.g., deadlock) on the lock server

    An improper locking bug(e.g., deadlock) on the lock server

    Hi, developers, thank you for your checking. I found a possible bug in UltraGrid, which locates in EmbeddableWebServer. It seems the lock server is not released correctly when!server->initialize in the function serverStop?

    https://github.com/CESNET/UltraGrid/blob/e3c926c8ff67586bf74c4cc0763a78c83ede9cf6/src/EmbeddableWebServer.h#L1515-L1526 Can you confirm this?

    The original issue: https://github.com/CESNET/UltraGrid/issues/183

    opened by ycaibb 2
  • responseAllocHTMLWithStatus() ignoring status code

    responseAllocHTMLWithStatus() ignoring status code

    Several functions like responseAllocHTMLWithStatus() are ignoring the status code parameter and always using 200. This is breaking functions like responseAlloc404NotFoundHTML(). The browser always gets 200 OK rather than the actual response code.

    bug 
    opened by braddabug 2
  • Cleanup & warning fixes under MinGW64

    Cleanup & warning fixes under MinGW64

    • removal of whitespaces at the end of a line.
    • indentation (little bit)

    Fix for warnings under MinGW64:

    • the socket (DWORD) was compare to -1, should be vs INVALID_SOCKET
    • a few of the parameters of the pthread-wrapper weren't used, mark them as unused so there ain't no warnings.
    • SIGPIPEHandler was not used, and as it was a static, it spawned a warning about an unused function
    • return type for connectionHandlerThread was a NULL casted, should be a 0
    • conversion from GetLastError to %d caused a warning. Now bring it first to a DWORD always to mitigate the warning.
    opened by svaningelgem 1
  • Error handlings for pthread_mutex_lock

    Error handlings for pthread_mutex_lock

    Hi, developers, I have a suggestion about error handlings for locking. Would it be better to handle the possible errors that return from pthread_mutex_lock.

    Possible situations that return errors.

        EAGAIN The mutex could not be acquired because the maximum number
                  of recursive locks for mutex has been exceeded.
    
           EINVAL The mutex was created with the protocol attribute having
                  the value PTHREAD_PRIO_PROTECT and the calling thread's
                  priority is higher than the mutex's current priority
                  ceiling.
    
           ENOTRECOVERABLE
                  The state protected by the mutex is not recoverable.
    
           EOWNERDEAD
                  The mutex is a robust mutex and the process containing the
                  previous owning thread terminated while holding the mutex
                  lock. The mutex lock shall be acquired by the calling
                  thread and it is up to the new owner to make the state
                  consistent.
    
           EDEADLK
                  The mutex type is PTHREAD_MUTEX_ERRORCHECK and the current
                  thread already owns the mutex.
    
           EOWNERDEAD
                  The mutex is a robust mutex and the previous owning thread
                  terminated while holding the mutex lock. The mutex lock
                  shall be acquired by the calling thread and it is up to
                  the new owner to make the state consistent.
    
           EDEADLK
                  A deadlock condition was detected.
    

    https://github.com/hellerf/EmbeddableWebServer/blob/9d71cee513e2eae98d1676b554385c7e894096db/EmbeddableWebServer.h#L1920-L1922

    For example, this example does not check the value returned by pthread_mutex_lock() for errors. If pthread_mutex_lock() cannot acquire the mutex for any reason, the function may introduce a race condition into the program (CWE-413). The manners of error handlings could be flagging any warnings or returning before accessing the critical region.

    void f(pthread_mutex_t *mutex) {
    pthread_mutex_lock(mutex);
    
    /* access shared resource */
    
    
    pthread_mutex_unlock(mutex);
    }
    
    opened by ycaibb 1
  • CORS triggered when used locally.

    CORS triggered when used locally.

    I think I need to add a "Access-Control-Allow-Origin: *" header to my response. I could not see how to add headers.

    When I tried it here: struct Response* responseAllocWithFile(const char* filename, const char* MIMETypeOrNULL) { struct Response* response = responseAlloc(200, "OK", MIMETypeOrNULL, 0); response->extraHeaders = strdup("Access-Control-Allow-Origin: *"); response->filenameToSend = strdup(filename); return response; }

    I broke an already working (in browser with security disabled) json file response.

    I really do not understand CORS that much and why when I have a server, the origins do not match. Maybe its a windows thing.

    opened by jshanab 1
  • Travis CI Integration part 3 - fuzzing

    Travis CI Integration part 3 - fuzzing

    Previously I used the AFL fuzzer on Linux to catch bugs. It seems like Travis CI can run AFL! I ran AFL for several days. I don't think it's nice to tie up infrastructure for several days fuzzing the EmbeddableWebServer. But it's certainly something to look into. Example: https://github.com/awslabs/s2n/pull/263/files

    enhancement help wanted 
    opened by hellerf 0
  • Travis CI Integration part 2 - run automated tests

    Travis CI Integration part 2 - run automated tests

    To be implemented after Part 1

    x. Run unit tests (these already exist) x. Make examples in the README build as part of the CI x. Create integration tests to do end-to-end response / request testing x. Do stress testing with abench

    enhancement help wanted 
    opened by hellerf 0
Owner
Forrest Heller
Mobile, Embedded, Desktop, some Cloud
Forrest Heller
To have platform independent network interfaces over usb which is working with Linux, Windows, Mac OS ect.

To have platform independent network interfaces over usb which is working with Linux, Windows, Mac OS ect. called RNDIS. This project is a RNDIS demo, which addtionally implements a http server. It runs out of the box on a stm32f411 BlackPill board. My RNDIS library with an empty template for the second interface (which can ba UART, CAN, ETH or like in this demo a tcp/ip stack) can be found under following link: https://github.com/RDMsmartnetworks/STM32_HAL_RNDIS

Nico Korn 17 Dec 24, 2022
Cross-platform, efficient, customizable, and robust asynchronous HTTP/WebSocket server C++14 library with the right balance between performance and ease of use

What Is RESTinio? RESTinio is a header-only C++14 library that gives you an embedded HTTP/Websocket server. It is based on standalone version of ASIO

Stiffstream 924 Jan 6, 2023
A cross-platform network learning demos. Like high-performance http server

Network-Learn A cross-platform network learning demos (toys). And I try not to use 3rd-party libraries. Welcome to try it out and leave your comments.

Ho 229 24 Sep 6, 2022
A collection of C++ HTTP libraries including an easy to use HTTP server.

Proxygen: Facebook's C++ HTTP Libraries This project comprises the core C++ HTTP abstractions used at Facebook. Internally, it is used as the basis fo

Facebook 7.7k Jan 4, 2023
Pushpin is a reverse proxy server written in C++ that makes it easy to implement WebSocket, HTTP streaming, and HTTP long-polling services.

Pushpin is a reverse proxy server written in C++ that makes it easy to implement WebSocket, HTTP streaming, and HTTP long-polling services. The project is unique among realtime push solutions in that it is designed to address the needs of API creators. Pushpin is transparent to clients and integrates easily into an API stack.

Fanout 3.2k Jan 2, 2023
Gromox - Groupware server backend with MAPI/HTTP, RPC/HTTP, IMAP, POP3 and PHP-MAPI support for grommunio

Gromox is the central groupware server component of grommunio. It is capable of serving as a replacement for Microsoft Exchange and compatibles. Conne

grommunio 139 Dec 26, 2022
A project designed for the esp8266 D1 Mini or the esp8266 D1 Mini PRO to provide a wifi http server and dns server.

PS4 Server 9.00 This is a project designed for the esp8266 D1 Mini or the esp8266 D1 Mini PRO to provide a wifi http server and dns server. this is fo

null 14 Nov 28, 2022
A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio.

A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio. Created to be an easy way to make REST resources available from C++ applications.

Ole Christian Eidheim 2.4k Dec 23, 2022
modern c++(c++17), cross-platform, header-only, easy to use http framework

cinatra--一个高效易用的c++ http框架 English | 中文 目录 使用cinatra常见问题汇总(FAQ) cinatra简介 如何使用 快速示例 性能测试 注意事项 roadmap 联系方式 cinatra简介 cinatra是一个高性能易用的http框架,它是用modern

qicosmos 1.4k Dec 30, 2022
A cross-platform HTTP client library with a focus on usability and speed

EasyHttp A cross-platform HTTP client library with a focus on usability and speed. Under its hood, EasyHttp uses POCO C++ Libraries and derives many o

Sony 148 Dec 30, 2022
Tiny cross-platform HTTP / HTTPS client library in C.

naett /nɛt:/ Tiny HTTP client library in C. Wraps native HTTP client functionality on macOS, Windows, Linux, iOS and Android in a single, simple non-b

Erik Agsjö 21 Nov 28, 2022
Small and fast cross-platform networking library, with support for messaging, IPv6, HTTP, SSL and WebSocket.

frnetlib Frnetlib, is a cross-platform, small and fast networking library written in C++. There are no library dependencies (unless you want to use SS

Fred Nicolson 23 Nov 25, 2022
XQUIC Library released by Alibaba is a cross-platform implementation of QUIC and HTTP/3 protocol.

XQUIC 简体中文文档 README-zh-CN Introduction XQUIC Library released by Alibaba is … … a client and server implementation of QUIC and HTTP/3 as specified by

Alibaba 1.4k Dec 29, 2022
Drogon: A C++14/17 based HTTP web application framework running on Linux/macOS/Unix/Windows

English | 简体中文 | 繁體中文 Overview Drogon is a C++14/17-based HTTP application framework. Drogon can be used to easily build various types of web applicat

An Tao 8.5k Jan 5, 2023
Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Felix Queißner 569 Dec 30, 2022
tiny HTTP parser written in C (used in HTTP::Parser::XS et al.)

PicoHTTPParser Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, Shigeo Mitsunari PicoHTTPParser is a tiny, primitive, fast HTTP r

H2O 1.6k Jan 1, 2023
cuehttp is a modern c++ middleware framework for http(http/https)/websocket(ws/wss).

cuehttp 简介 cuehttp是一个使用Modern C++(C++17)编写的跨平台、高性能、易用的HTTP/WebSocket框架。基于中间件模式可以方便、高效、优雅的增加功能。cuehttp基于boost.asio开发,使用picohttpparser进行HTTP协议解析。内部依赖了nl

xcyl 29 Dec 17, 2022
An extensible, cross-platform, single-header C/C++ OpenGL loader library.

Simple OpenGL Loader An extensible, cross-platform, single-header C/C++ OpenGL loader library. Usage For Windows Win32 or Linux X11 applications, the

Tarek Sherif 77 Dec 12, 2022
Windows named pipe server that forwards connections to given TCP server

PipeTcp An asynchronous Windows named pipe server that forwards connections to given TCP server. Pre-built binaries can be found in Releases. Invocati

Jinoh Kang 5 Nov 3, 2022