nodepp

Node++ is an asynchronous HTTP(S) engine and framework for low-latency C/C++ web applications / RESTful APIs.

11
1
C++

Node++

Node++ is an asynchronous HTTP(S) engine and backend framework for low-latency C/C++ web applications and RESTful APIs.

It’s more than twice as fast as Spring Boot and five times faster than Node.js.

C++ backend can render pages in microseconds, even with a database, when used with our efficient DAO/ORM class (see live demo).

It can act as:

  • a static web server;
  • a standalone, self-contained, single-process web application;
  • a standalone, multi-process web application, connected and load-balanced via standard POSIX queues;
  • an HTTP servlet/microservice that makes up a building block of the more complex, distributed architecture.

Node++ library (paired with OpenSSL and optionally MySQL) contains everything that is necessary to build a complete, production-grade solution, including session management, users accounts, REST calls and multi-language support.

There is a RESTful API generator to generate almost the whole backend code in a few clicks, requiring only an SQL table definition.

Node++ 3 vs 2

ci

Version 3 gets rid of ci (connection index) carried around for all those years. I kind of forgot about it, even after it had become obvious that Node++ was going to retain a single-threaded model. Migration to 3.x.x requires removing int ci function arguments.

Silgy

Silgy compatibility has been removed. It’s been so long that it no loger makes sense to clutter Node++'s code base with npp_silgy.h.

USERS

Although 62 power 15 gives insane number of possible combinations (exactly 768,909,704,948,766,668,552,634,368 of them), I decided to extend sessid to 20 characters, in case the whole universe and all of its IoT devices started using Node++. I skip providing the updated number of combinations here. Also, CSRF token has been extended to 15 characters.

One Time Password is now possible with NPP_USER_ONE_TIME_PASSWORD_ONLY macro present in npp_app.h.

All of the above requires some database changes:

alter table users add otp char(44);
alter table users add otp_expires datetime;
alter table users_logins modify sessid char(20);
alter table users_logins modify csrft char(15);

Framework

The backend framework means that there are 7 server-side events you can react to:

Event Function called
application start npp_app_init()
HTTP request npp_app_main()
session start npp_app_session_init()
session authentication (login) npp_app_user_login()
session logout npp_app_user_logout()
session stop npp_app_session_done()
application stop npp_app_done()

npp_app.cpp has to contain these definitions (even if empty).

As Node++ is single-threaded, the application code does not need to be thread-safe. Horizontal scaling is done through adding processes, not threads.

The whole user session and sessid cookie management is handled by the engine.

By default sessions are not started. To change this add one line to npp_app.h.

Performance

Node++'s efficiency makes single CPU, 1 GB AWS EC2 t2.micro free instance sufficient to host a fully-fledged web application with a database for thousands of users.

On a typical server it handles 50,000 requests per second.

Low latency gets Node++ applications “Faster than 100% of tested sites” badge from Pingdom.

Or – as they now don’t publish the percentage – it shows the complete page load in 67 ms:

Why is Node++ faster than other engines?

Security

Node++ has built-in (and enabled by default) protection against most popular attacks, including BEAST, SQL-injection, XSS, and password and cookie brute-force. It does not directly expose the filesystem nor allows any scripting. Its random string generator is FIPS-compliant. CSRF protection is as easy as adding 3 lines to the code.

Default SSL settings give this result:

Programming

Basic convention

If resource is a file present in res or resmin (i.e. an image or css), it will be served and npp_app_main() will not be called.

Simplest Hello World

Return static file if present, otherwise “Hello World!”.

void npp_app_main()
{
    OUT("Hello World!");
}

Simple HTML with 2 pages

Application, yet without moving parts.

void npp_app_main()
{
    if ( REQ("") )  // landing page
    {
        OUT_HTML_HEADER;
        OUT("<h1>%s</h1>", NPP_APP_NAME);
        OUT("<h2>Welcome to my web app!</h2>");
        OUT("<p><a href=\"/about\">About</a></p>");
        OUT_HTML_FOOTER;
    }
    else if ( REQ("about") )
    {
        OUT_HTML_HEADER;
        OUT("<h1>%s</h1>", NPP_APP_NAME);
        OUT("<h2>About</h2>");
        OUT("<p>Hello World Sample Node++ Web Application</p>");
        OUT("<p><a href=\"/\">Back to landing page</a></p>");
        OUT_HTML_FOOTER;
    }
    else  // page not found
    {
        RES_STATUS(404);
    }
}

Using query string value

This is a simple 2-pages application demonstrating QS() macro usage to get a value provided by client in the query string.

QS works with all popular HTTP methods and payload types.

void npp_app_main()
{
    if ( REQ("") )  // landing page
    {
        OUT_HTML_HEADER;    // generic HTML header with opening body tag

        OUT("<h1>%s</h1>", NPP_APP_NAME);

        OUT("<h2>Welcome to my web app!</h2>");

        /* show client type */

        if ( REQ_DSK )
            OUT("<p>You're on desktop, right?</p>");
        else if ( REQ_TAB )
            OUT("<p>You're on tablet, right?</p>");
        else  /* REQ_MOB */
            OUT("<p>You're on the phone, right?</p>");

        /* link to welcome */

        OUT("<p>Click <a href=\"/welcome\">here</a> to try my welcoming bot.</p>");

        OUT_HTML_FOOTER;    // body and html closing tags
    }
    else if ( REQ("welcome") )  // welcoming bot
    {
        OUT_HTML_HEADER;
        OUT("<h1>%s</h1>", NPP_APP_NAME);

        /* display form */

        OUT("<p>Please enter your name:</p>");
        OUT("<form action=\"welcome\"><input name=\"name\" autofocus> <input type=\"submit\" value=\"Run\"></form>");

        /* try to get the query string value */

        QSVAL qs_name;   // query string value

        if ( QS("name", qs_name) )    // if present, bid welcome
            OUT("<p>Welcome %s, my dear friend!</p>", qs_name);

        OUT("<p><a href=\"/\">Back to landing page</a></p>");

        OUT_HTML_FOOTER;
    }
    else  // page not found
    {
        RES_STATUS(404);
    }
}

Complete 4-page application example is included in the package (see npp_app.cpp).

More examples are available here: Node++ examples.

Configuration

Configuration file (bin/npp.conf) contains a handful of settings, starting with logLevel, httpPort, httpsPort, SSL certificate paths, database credentials and so on.

You can also add your own and read them in npp_app_init() with npp_read_param_str() or npp_read_param_int().

Documentation

Step-by-step tutorial how to set up complete system on AWS EC2 Linux instance

Getting Started on Linux

Getting Started on Windows

Switches and constants

Functions and macros

Configuration parameters

How to enable HTTPS

Sessions in Node++

ASYNC (multi-process mode)

Memory models

Multi-language support

USERS Module

Error codes

RESTful calls from Node++

What is npp_watcher

Directory structure

Although not necessary, it’s good to have $NPP_DIR set in the environment, pointing to the project directory. Node++ engine always first looks in $NPP_DIR/<dir> for the particular file, with <dir> being one of the below:

src

(Required only for development)

  • Application sources. It has to contain at least npp_app.h and npp_app.cpp with npp_app_main() inside.
  • Customizable compilation script: m

lib

(Required only for development)

  • Node++ engine & library source
  • users.sql

bin

  • Main compilation script: nppmake
  • Executables, i.e. npp_app, npp_svc, npp_watcher, npp_update
  • Runtime scripts, i.e. nppstart, nppstop
  • Configuration: npp.conf
  • Strings in additional languages: strings.LL-CC
  • Blacklist, i.e. blacklist.txt
  • Whitelist, i.e. whitelist.txt
  • 10,000 most common passwords: passwords.txt

res

Static resources. The whole tree under res is publicly available. All the files are read on startup and served straight from the memory. File list is updated once a minute.

  • images
  • static HTML files
  • text files
  • fonts

resmin

Static resources to be minified. The whole tree under resmin is publicly available. All the files are read on startup, minified and served straight from the memory. File list is updated once a minute.

  • CSS
  • JS

snippets

Static parts of rendered content, i.e. HTML or markdown snippets.

logs

  • Log files