Home Quick start Logging TLS Authentication IPFilter Safe Safe API Safe utilities Dev environment REST API Download Support

CETAN Documentation

This documentation covers the CETAN Web Application Server, logging, TLS, authentication, IP filtering, Safe secrets, Safe utilities, development environment setup, and building C++ REST web services using the CETAN REST API.

Contents

1. Quick start

This guide walks you through installing, configuring, and starting the CETAN Server on Linux. All examples use Ubuntu 24.04.2, but the steps apply to most modern Linux distributions.

1.1 Prerequisites

Before you begin, ensure your system includes:

1.2 Download the server

Download the CETAN Server package: Cetan Web Application Server.

1.3 Unpack the software

tar -xvf cetan-server_1.0.tar.gz

This extracts the server into a directory named CETAN_SERVER.

1.4 Edit the configuration file

The main configuration file is located at: CETAN_SERVER/config/cetan_config.xml. A full reference file (cetan_config_full_sample.xml) is also available in the same directory.

Update the <host> directive:

<host>example.com</host>

Replace example.com with your server’s hostname.

1.5 Set permissions and ownership

To improve security and simplify access control, configure group‑based permissions for the server directory.

Create the cetan group:

sudo addgroup cetan

Assign ownership to root:cetan:

sudo chown -R root:cetan CETAN_SERVER

Apply secure permissions:

sudo chmod -R 2670 CETAN_SERVER

Meaning of 2670:

Add users to the cetan group:

sudo usermod -aG cetan username

Replace username with the actual Linux user.

1.6 Start the CETAN server

First‑time startup:

bin/cetan start

Subsequent startups:

bin/cetan start

Enter the server password created during the first startup.

1.7 Verify the server

Open your server URL in a browser. The CETAN welcome page should appear, confirming the server is running.

1.8 About Safe

Safe is CETAN’s secure storage system for sensitive data, including the server password. A detailed Safe reference document is available in the next section.

1.9 Troubleshooting

Missing GLIBCXX version:

/lib64/libstdc++.so.6: version 'GLIBCXX_X.Y.Z' not found

Install or upgrade GLIBC++ to the required version and retry.

Other errors:

1.10 Running the server in the background

To move a running foreground job to the background:

Ctrl+Z
bg
disown
exit

Congratulations — your CETAN Server is now installed, configured, and running.

2. Logging (SLog)

CETAN uses SLog as its primary logging utility. This section explains how to configure logging for the CETAN Web Application Server. For full details about SLog’s API and advanced features, refer to the official SLog documentation.

Configuration overview

2.1 Logging configuration files

The main logging configuration file is located at:

CETAN_SERVER/config/slog_config.xml

A complete reference configuration is also provided:

CETAN_SERVER/config/slog_config_full_sample.xml

CETAN reads this file at startup. Any changes require restarting the server.

2.2 CETAN’s built‑in logger

CETAN includes a private, reserved logger named CETAN. This logger is used internally by the server and must not be redefined in your SLog configuration.

The default logger entry appears as:

<target name="cetan_file">
	<type>file</type>
	<file_name>cetan.log</file_name>
	<max_number_file>10</max_number_file>
 </target>
<logger name="CETAN">
    <target>cetan_file</target>
    <accept_level>INFO</accept_level>
</logger>

This logger accepts messages at INFO level or lower and outputs them to the console. Because it is a private logger, do not create another logger with the same name.

2.3 Log levels

SLog supports the following log levels, from highest to lowest severity:

For normal operation, set accept_level to ERROR or WARN to reduce overhead. Use AUDIT for basic request logging. Use DEBUG only when diagnosing issues, as it significantly impacts performance.

2.4 Writing logs to a file

To write log output to a file, configure a file target and logger as follow:

<target name="restapi_file">
    <type>file</type>
    <file_name>restapi.log</file_name>
    <max_number_file>10</max_number_file>
</target>
<logger name="RestAPI">
    <target>restapi_file</target>
    <accept_level>INFO</accept_level>
</logger>

Additional options such as file_size_max may be used to control rotation size.

2.5 Applying changes

After modifying slog_config.xml, restart the CETAN server for changes to take effect.

SLog is a flexible logging system. Refer to your SLog configuration file for full element definitions and advanced options.

3. SSL/TLS configuration

CETAN can serve HTTPS traffic using TLS certificates configured in cetan_config.xml. You can use unencrypted or encrypted private keys, and store passphrases in Safe.

3.1 Certificate and private key requirements

For production environments, always obtain certificates from a trusted Certificate Authority. Self‑signed certificates should be used only for testing.

3.2 Generating a self‑signed certificate (testing only)

From CETAN_SERVER, run:

bin/cetan gen-cert

Follow the prompts to enter certificate subject information. The command generates two files:

Record the private key passphrase — it will be needed when configuring Safe.

3.3 Placing certificate and key files

CETAN only searches for certificate and key files inside:

CETAN_SERVER/security

Place your cert.pem (or CA‑issued bundle) and key.pem in this directory.

3.4 Enabling HTTPS in cetan_config.xml

Set the protocol to HTTPS:

<protocol>https</protocol>

Specify the certificate bundle:

<ssl_cert_bundle_pem>cert.pem</ssl_cert_bundle_pem>

Only specify the file name — do not include the security/ directory path.

3.5 Configuring the private key

CETAN supports both encrypted and unencrypted private keys. Choose the method that matches your key type.

3.5.1 Method 1 — Encrypted private key (recommended)

Specify the encrypted key file:

<ssl_enc_key_pem>key.pem</ssl_enc_key_pem>

Reference the Safe entry containing the passphrase:

<safe_ssl_key_passphase>cetan_ssl_key_passphase</safe_ssl_key_passphase>

Create the Safe entry:

bin/safe add -n security/cetan.safe -e cetan_ssl_key_passphase

Enter:

You should see: Entry added

3.5.2 Method 2 — Unencrypted private key

Specify the Safe entry containing the raw private key:

<safe_ssl_cert_key>cetan_ssl_key</safe_ssl_cert_key>

Create the Safe entry:

bin/safe add -n security/cetan.safe -e cetan_ssl_key -infile security/key.pem

Enter the Safe password when prompted. You should see Entry added confirming the key was stored.

List Safe entries:

bin/safe list -n security/cetan.safe

3.6 Restarting the server

After updating cetan_config.xml, restart the CETAN server for TLS changes to take effect.

3.7 Verifying HTTPS

Once restarted, access your server using https://. If the certificate and key are configured correctly, CETAN will serve traffic securely over TLS.

4. Authentication

CETAN supports multiple authentication mechanisms for securing web resources. Authentication consists of three components:

These components work together to validate user credentials, API keys, or JWT tokens before granting access to protected URLs.

4.1 How authentication is structured

The example below shows all three declarations working together:

<cetan_server>
  ...
  <idps>
    <idp name="cetan_basic_idp">
      <type>BasicFile</type>
      <lockout_time>100</lockout_time>
      <safe_idp_key>idp_key</safe_idp_key>
      <display_name>basic_idp</display_name>
      <max_failed_attempt>3</max_failed_attempt>
      <file_name>cetan_basic_account.txt</file_name>
    </idp>
  </idps>

  <auths>
    <authn name="cetan_basic_file_authn">
      <type>Basic</type>
      <idp>cetan_basic_idp</idp>
    </authn>
  </auths>

  <protect_resources>
    <uris>/</uris>
    <authn>cetan_basic_file_authn</authn>
  </protect_resources>
  ...
</cetan_server>

protect_resources references an authn entry, which in turn references an IDP. Removing protect_resources disables web authentication entirely.

Multiple IDPs and authentication schemes may be declared, but only one IDP and one authn entry are used per protected resource.

4.2 Identity Providers (IDPs)

CETAN supports the following IDP types:

4.2.1 LDAP IDP

<idp name="cetan_ldap">
  <type>LDAP</type>
  <lockout_time>100</lockout_time>
  <url>ldaps://ldap.cetan.io</url>
  <bind_attribute>uid</bind_attribute>
  <max_failed_attempt>3</max_failed_attempt>
  <base_dn>ou=People,dc=cetan,dc=io</base_dn>
  <admin_dn>cn=admin,dc=cetan,dc=io</admin_dn>
  <safe_admin_key>ldap_admin_key</safe_admin_key>
</idp>

Create the Safe entry:

bin/safe add -n security/cetan.safe -e ldap_admin_key

4.2.2 BasicFile IDP

BasicFile IDP stores user accounts in a text file using the format: uid;password_hmac;salt

<idp name="cetan_basic_idp">
  <type>BasicFile</type>
  <salt_size>24</salt_size>
  <lockout_time>100</lockout_time>
  <display_name>basic_idp</display_name>
  <hash_algorithm>SHA3-256</hash_algorithm>
  <max_failed_attempt>3</max_failed_attempt>
  <file_name>cetan_basic_account.txt</file_name>
  <safe_idp_key>basic_idp_key</safe_idp_key>
</idp>

Create the IDP key:

bin/safe gen-password -length 32
bin/safe add -n security/cetan.safe -e basic_idp_key

4.2.3 Adding users to BasicFile IDP

Using CETAN CLI:

bin/cetan add-user -idp-name cetan_basic_idp -uid user_uid

Manual method:

  1. Generate salt: bin/safe gen-salt -length 24
  2. Compute HMAC: bin/safe compute-hmac
  3. Retrieve IDP key: bin/safe get -n security/cetan.safe -e basic_idp_key
  4. Add record to file: uid;base64_hmac;base64_salt

4.2.4 KeyFile IDP

<idp name="cetan-api-key">
  <type>KeyFile</type>
  <key_size>64</key_size>
  <display_name>APIKey</display_name>
  <file_name>cetan_api_key.txt</file_name>
  <safe_idp_key>idp_api_key</safe_idp_key>
</idp>

Create the IDP key:

bin/safe gen-password -length 64
bin/safe add -n security/cetan.safe -e idp_api_key

Add API keys:

bin/cetan add-api-key

4.3 Authentication schemes

CETAN supports two authentication types:

4.3.1 Basic Authentication

<authn name="cetan_basic_file_authn">
  <type>Basic</type>
  <idp>cetan_basic_idp</idp>
</authn>

4.3.2 Bearer Authentication

Bearer tokens may be API keys or JWT tokens.

API Key example:

<authn name="cetan_key_bearer">
  <type>Bearer</type>
  <token>apikey</token>
  <idp>cetan-api-key</idp>
</authn>

JWT (HMAC) example:

<authn name="cetan_jwt_HS256_bearer">
  <type>Bearer</type>
  <token>jwt</token>
  <verify>HMAC</verify>
  <safe_key>HS256_secret</safe_key>
</authn>

JWT (RSA/EC/PS/EdDSA) examples:

<authn name="cetan_jwt_RS256_bearer">
  <type>Bearer</type>
  <token>jwt</token>
  <verify>signature</verify>
  <cert_file>jwt_rs256_public_key.pem</cert_file>
</authn>

4.4 Protecting resources

Use protect_resources to secure specific URL paths:

<protect_resources>
  <uris>/</uris>
  <authn>cetan_basic_ldap_authn</authn>
</protect_resources>

4.5 Save and restart

After updating cetan_config.xml, restart the CETAN server for authentication changes to take effect.

5. IPFilter

The CETAN Web Application Server includes a built‑in IP filtering system that allows you to control access based on client IP addresses or ranges. IPFilter operates in either blacklist or whitelist mode and is configured using a dedicated file referenced from cetan_config.xml.

5.1 Enabling IPFilter

To enable IP filtering, add the <ip_filter> directive to your server configuration:

<ip_filter>blacklist.conf</ip_filter>

You may choose any filename. The file must be placed in the CETAN_SERVER/security directory.

5.2 Configuration file structure

The IPFilter configuration file defines the filter mode and the list of IP addresses or ranges to allow or block.

Example configuration file:

mode = 1;  # blacklist mode (required)
1.2.3.4 = 1;  # drop a single IP
4.5.6.7 - 4.5.6.254 = 1;  # drop a range of IPs

If mode is not defined, CETAN defaults to blacklist mode.

5.3 Modes

5.4 IP entries and action codes

Each entry in the configuration file maps an IP address or range to an action code:

1.2.3.4 = 1;

Supported formats:

5.5 Example configurations

5.5.1 Blacklist mode

mode = 1;
203.0.113.5 = 1;
198.51.100.0 - 198.51.100.255 = 1;

5.5.2 Whitelist mode

mode = 2;
192.168.1.10 = 2;
10.0.0.0 - 10.0.0.255 = 2;

5.6 Restart required

After modifying the IPFilter configuration file or updating the <ip_filter> directive, restart the CETAN server for changes to take effect.

IPFilter is evaluated early in the request pipeline. Clients blocked by IPFilter cannot reach authentication or application handlers.

6. Safe (secrets)

Safe is a robust C++‑based system for securely managing sensitive data—commonly referred to as secrets. It provides both a command‑line interface (CLI) for direct user interaction and a comprehensive C++ API for programmatic integration. Safe enables secure storage, retrieval, and lifecycle management of secrets within applications and systems, eliminating hardcoded credentials and insecure storage practices.

6.1 Core concepts

6.2 Typical usage

6.3 Safe C++ API (SDK)

The Safe C++ API provides programmatic access to encrypted Safe files. Applications can create safes, add entries, retrieve values, and manage passwords without exposing secrets in plain text. All functionality is provided through the ctn::Safe class.

6.3.1 Class overview

namespace ctn {
  class Safe {
  public:
    Safe();
    Safe(const string& name, const string& key);

    int create_safe(string& error);
    bool key_verified(string& error);

    int get_entry(Entry& e, string& error);
    int add_entry(const Entry& e, string& error);
    int remove_entry(const string& entry_name, string& error);
    int list_entries(vector<string>& names, string& error);

    int add_entry_from_file(const string& name,
                            const string& file,
                            string& error);

    int change_key(const string& name,
                   const string& current_key,
                   const string& new_key,
                   string& error);
  };
}

All methods returning int follow a standard convention: 0 = success, -1 = error. Error details are returned through the error reference parameter.

6.3.2 Constructors

Default constructor
Safe safe;

Creates an empty Safe instance. This form is used exclusively for change_key() operations.

Constructor with file + key
Safe safe("cetan", "password");

6.3.3 Core API methods

Create Safe
int create_safe(string& error);
Add entry
int add_entry(const Entry& e, string& error);
Add entry from file
int add_entry_from_file(const string& name,
                        const string& file,
                        string& error);
List entries
int list_entries(vector<string>& names, string& error);
Get entry
int get_entry(Entry& e, string& error);
Remove entry
int remove_entry(const string& entry_name, string& error);
Verify key
bool key_verified(string& error);
Change Safe password
int change_key(const string& name,
               const string& current_key,
               const string& new_key,
               string& error);

6.3.4 Examples

Create a Safe
#include "Safe.h"
#include <string>
#include <iostream>

using namespace ctn;
using namespace std;

int main() {
  string error;
  Safe safe("cetan", "password");

  if (safe.create_safe(error) == -1)
    cout << "Could not create safe: " << error << "\n";
  else
    cout << "A new safe created\n";
}
Add entry
#include "Safe.h"
#include "Entry.h"

Entry e("entry_name", "entry value");
safe.add_entry(e, error);
List entries
vector<string> names;
safe.list_entries(names, error);
Retrieve entry
Entry e("entry_name");
safe.get_entry(e, error);
cout << e.value();
Remove entry
safe.remove_entry("entry_name", error);
Change Safe password
Safe safe;
safe.change_key("cetan.safe", "oldpass", "newpass", error);

6.4 Safe Command Line Interface (CLI)

6.4.1 Introduction

Safe Utilities is a standalone command‑line tool for secure management of secrets. It supports both interactive and non‑interactive modes, making it suitable for scripting, automation, and secure deployment workflows.

6.4.2 Installation

6.4.3 Core concepts

6.4.4 General usage

./safe COMMAND [OPTIONS] <ARG>...

6.4.5 Commands

Create Safe
./safe create --name safe-name --pass safe-password
Add entry
./safe add --name safe-name --pass safe-password --entry entry-name --value entry-value
Add entry from file
./safe add --name safe-name --pass safe-password --entry entry-name --infile filename
List entries
./safe list --name safe-name --pass safe-password
Get entry
./safe get --name safe-name --pass safe-password --entry entry-name
./safe get ... --out hex|HEX|base64
./safe get ... --outfile filename
Delete entry
./safe delete --name safe-name --pass safe-password --entry entry-name
Change Safe password
./safe change-password --name safe-name --pass safe-password --newpass new-password
Verify Safe password
./safe verify-password --name safe-name --pass safe-password
Generate salt
./safe gen-salt --length salt-length --out hex|HEX|base64
Generate password
./safe gen-password --length password-length
Compute HMAC
./safe compute-hmac --msg message --b64-salt salt --hash-algo algo --key password --out outform

Supported hash algorithms include SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-384, and SHA3-512.

7. SLog Logging Framework

SLog is a lightweight, high‑performance logging framework for modern C++ applications. It provides configurable log targets, structured formatting, log‑level filtering, and efficient variadic logging APIs. The framework is designed to be simple to integrate, fast under load, and flexible enough for both small utilities and large server systems.

7.1 Introduction

SLog offers a clean C++ interface for writing log messages at different severity levels. Developers bind an SLog instance to a named logger defined in slog_config.xml, then write log entries using typed logging methods such as info(), warn(), or debug(). The framework includes timestamp utilities, log‑level filtering, file rotation, and a minimal API designed for clarity and performance.

7.2 SLog API

7.2.1 Constants

static constexpr const char* DEFAULT_LOGGER   = "CONSOLE";
static constexpr const char* HTTP_TIME_FORMAT = "%a, %d %b %Y %T %Z";
static constexpr const char* SLOG_TIME_FORMAT = "%m-%d-%Y %H:%M:%S %Z";

7.2.2 Log Levels

enum Level { OUT, FATAL, ERROR, WARN, AUDIT, INFO, TRACE, DEBUG };

7.2.3 Constructors and Object Semantics

SLog();
SLog(const string& logger_name, long key = 0);
SLog(SLog&& orig);
SLog& operator=(SLog&& orig);

SLog(const SLog& orig) = delete;
SLog& operator=(const SLog& orig) = delete;

SLog is move‑only to prevent accidental duplication of logger state. The optional key parameter allows creation of private loggers that are accessible only to components that know the key.

7.2.4 Time Utilities

// Default formats
static string time_gmt();
static string time_local();

// HTTP-style formats
static string http_gmt_time();
static string http_time_local();

// Explicit default formats
static string time_gmt_format();
static string time_local_format();

// Custom formats
static string time_gmt_format(const string& format);
static string time_local_format(const string& format);

These functions generate timestamps in GMT or local time using either default or custom formats.

7.2.5 Log-Level Checking

bool logable(Level L) const;

This function returns true if the logger accepts the specified level. It is useful for avoiding expensive string construction when a message would be filtered out.

7.2.6 Generic Logging Function

template<typename... Params>
void log(Level L, const Params&... params) const;

All parameters are concatenated into a single unformatted message.

7.2.7 Typed Logging Methods

template<typename... Params> void out(const string& tag, const Params&... params) const;
template<typename... Params> void fatal(const string& tag, const Params&... params) const;
template<typename... Params> void error(const string& tag, const Params&... params) const;
template<typename... Params> void warn(const string& tag, const Params&... params) const;
template<typename... Params> void audit(const string& tag, const Params&... params) const;
template<typename... Params> void info(const string& tag, const Params&... params) const;
template<typename... Params> void trace(const string& tag, const Params&... params) const;
template<typename... Params> void debug(const string& tag, const Params&... params) const;

Each method prepends a timestamp, level, and tag, then writes the formatted entry to the configured target.

Example log format:

02-11-2025 04:27:58.263312 GMT D HTTP 333952 receive header for request: 20

7.2.8 Example

#include "SLog.h"

SLog slog("RestAPI");

slog.info("startup", "Service initialized");
slog.warn("validation", "Missing field: ", field_name);
slog.error("db", "Database connection failed: ", err_msg);

if (slog.logable(SLog::DEBUG)) {
    slog.debug("payload", "Request body: ", json_body);
}

7.3 SLog Usage

This section explains how to configure SLog, define loggers and targets, and write log messages in C++ applications.

7.3.1 Configuration File

SLog loads its configuration from config/slog_config.xml or from the application's working directory.

<slog_config>
  <log_dir>logs</log_dir>
  <time_zone>local</time_zone>
  <locale>C</locale>
  <file_size_max>50M</file_size_max>

  <target name="app_file">
    <type>file</type>
    <file_name>application.log</file_name>
    <max_number_file>10</max_number_file>
  </target>

  <logger name="AppLogger">
    <target>app_file</target>
    <accept_level>INFO</accept_level>
  </logger>
</slog_config>

7.3.2 Global Settings

7.3.3 Targets

Targets define where log messages are written. SLog supports console and file targets.

<target name="restapi_file">
  <type>file</type>
  <file_name>restapi.log</file_name>
  <accept_level>INFO</accept_level>
  <max_number_file>10</max_number_file>
</target>

7.3.4 Loggers

A logger binds a name to a target and defines the minimum log level it accepts.

<logger name="RestAPI">
  <target>restapi_file</target>
  <accept_level>INFO</accept_level>
</logger>

7.3.5 Writing Logs in C++

#include "SLog.h"

SLog log("RestAPI");

log.out("startup", "Service initialized");
log.info("startup", "Listening on port ", port);
log.warn("validation", "Missing field: ", field_name);
log.error("db", "Database connection failed: ", err_msg);
log.debug("trace", "Processing request ID=", id);

7.3.6 Checking Log Level

if (log.logable(SLog::DEBUG)) {
    log.debug("payload", "Request body: ", expensive_to_string(obj));
}

7.3.7 Time Formatting Utilities

string now       = SLog::time_local();
string http_time = SLog::http_gmt_time();
string custom    = SLog::time_local_format("%Y-%m-%d %H:%M:%S");

7.3.8 Recommended Practices

8. Develop Web Applications Using the CETAN REST API

The CETAN REST API enables developers to build high‑performance C++ web applications that run natively on the CETAN Web Application Server. It provides an object‑oriented framework for defining services, routing HTTP requests, generating responses, and integrating with CETAN’s logging, security, and configuration systems.

This section explains how to set up a complete development environment, then walks through building a fully functional REST application named rest-api-101 that exposes a simple “Hello, World!” endpoint.

8.1 Development Environment Setup

CETAN REST development typically uses a Linux server as the build host and a Windows workstation for editing source code. The example environment below uses Ubuntu 24.04.2 LTS with g++ (C++23) and Apache NetBeans on Windows with remote development.

8.1.1 Requirements

8.1.2 Directory Layout

A typical project structure:

project/
  include/
  src/
  build/
  CMakeLists.txt
  cetan-rest-sdk/

7.1.3 Configure Samba Share on Linux

8.1.4 Install and Configure Apache NetBeans

If NetBeans reports an unpack200 error, copy the unpack200 binary from an older JDK into the new JDK’s bin directory.

8.1.5 Configure Remote Build Host

8.1.6 Validate the Environment

8.1.7 Linking Against CETAN REST

Include the CETAN REST headers:

#include "Rest.h"
#include "WebService.h"
#include "Resource.h"

Ensure your runtime search path includes:

$ORIGIN/../lib

8.2 Develop the rest-api-101 Web Application

This tutorial walks through creating a minimal REST application that exposes a single endpoint: /greeting/hello-world. The application returns a simple HTML “Hello, World!” page.

8.2.1 Create the Greeting WebService

// Greeting.h
#ifndef GREETING_H
#define GREETING_H

#include "Response.h"
#include "WebService.h"
#include <vector>

using namespace std;
using namespace ctn;

class Greeting : public WebService {
public:
    Greeting();
    Response hello_world();
    virtual ~Greeting();
private:
    vector<Resource*> register_resources() const override final;
};

#endif /* GREETING_H */
// Greeting.cpp
#include "Greeting.h"

Greeting::Greeting() : WebService("/greeting") {}

Response Greeting::hello_world() {
    Response resp(200);
    resp.body("<html><head><title>Hello World from CETAN REST</title></head>"
              "<body><center><font size=\\"25\\">Hello, World!</font>"
              "</center></body></html>");
    return resp;
}

vector<Resource*> Greeting::register_resources() const {
    vector<Resource*> resources;
    resources.emplace_back(new Resource("GET", "/hello-world", &Greeting::hello_world));
    return resources;
}

Greeting::~Greeting() {}

The full path for this endpoint becomes: /greeting/hello-world under the application’s root URL.

8.2.2 Create the RestAPI101 Application Class

// RestAPI101.h
#ifndef RESTAPI101_H
#define RESTAPI101_H

#include "REST.h"
#include "WebService.h"
#include <set>

using namespace std;
using namespace ctn;
class RestAPI101 : public REST {
public:
    RestAPI101();
    virtual ~RestAPI101();
private:
    REST* create_cetan_app() const override final;
    set<WebService*> register_services() const override final;
};

#endif /* RESTAPI101_H */
// RestAPI101.cpp
#include "REST.h"
#include "Greeting.h"
#include "RestAPI101.h"
RestAPI101::RestAPI101() {}

REST* RestAPI101::create_cetan_app() const {
    return new RestAPI101();
}

set<WebService*> RestAPI101::register_services() const {
    set<WebService*> services;
    services.emplace(new Greeting());
    return services;
}

RestAPI101::~RestAPI101() {}

8.2.3 Build Configuration

8.2.4 Deploy to CETAN

<applications>
  <app name="cetan-rest-101">
    <root_url>/rest-101</root_url>
    <app_file>cetan-rest-101.ctn</app_file>
  </app>
</applications>

After restarting the CETAN Web Application Server, the endpoint becomes:

https://your-server.com/rest-101/greeting/hello-world

You should now see the “Hello, World!” HTML page in your browser. This completes the rest-api-101 tutorial.

9. REST C++ API (SDK)

The CETAN REST SDK provides a set of C++ classes for building RESTful services, handling requests, generating responses, managing uploads, and defining routes. This section documents each class in the REST framework.

9.1 REST Application Base Class

The REST class is the foundation of every CETAN REST application. It manages application initialization, service registration, and endpoint discovery. Developers derive from this class to define their application and register WebService components.

Constructors & Lifecycle

REST();
virtual ~REST();

The constructor initializes the REST application. The destructor cleans up registered services and internal state.

Members

const string tag = "REST";

A default tag used for logging or identification.

Endpoint Discovery

const string& json_endpoints() const;
const vector<string>& app_endpoints() const;

Initialization Hook

protected:
  virtual void init();

Override init() in your derived class to configure services, routes, and application‑specific initialization logic.

Service Registration (Required)

private:
  virtual set<WebService*> register_services() const = 0;

Every REST application must implement register_services() to return the set of WebService instances that belong to the application. Each service defines its own routes and handlers.

9.2 WebService Base Class

WebService represents a REST resource bound to a specific base path. Developers derive from this class to implement REST endpoints using Resource objects. Each service is responsible for registering its own routes.

Constructors & Semantics

WebService(const string& path);
WebService(WebService&& orig);
WebService& operator=(WebService&& orig);

WebService(const WebService& orig) = delete;
WebService& operator=(const WebService& orig) = delete;

virtual ~WebService();

A WebService is move‑only. The path parameter defines the base URL under which all service endpoints are registered.

Accessors

Request get_request() const;
const string& app_name() const;
const string& app_root_url() const;
const string& document_root() const;

const REST* get_app() const;
const string& get_path() const;
const vector<string>& get_endpoints() const;

Resource Registration (Required)

private:
  virtual vector<Resource*> register_resources() const = 0;

Derived classes must implement register_resources() to return the list of Resource objects that define the service’s HTTP endpoints.

12.3 Response Class

Response represents an HTTP response, including status code, headers, cookies, and body content.

Header Enumeration

enum Header {
  Cache_Control, Connection, Date, Pragma, Trailer, Transfer_Encoding, Upgrade, Via, Warning,
  Accept, Accept_Charset, Accept_Encoding, Accept_Language, Authorization, Expect, From, Host,
  If_Match, If_Modified_Since, If_None_Match, If_Range, If_Unmodified_Since, Max_Forwards,
  Proxy_Authorization, Range, Referer, TE, User_Agent, Accept_Ranges, Age, ETag, Location,
  Proxy_Authenticate, Retry_After, Server, Vary, WWW_Authenticate, Allow, Content_Encoding,
  Content_Language, Content_Length, Content_Location, Content_MD5, Content_Range, Content_Type,
  Expires, Last_Modified, Cookie, Set_Cookie
};

Constructors

Response();
Response(int code);
Response(Response&& orig);
Response& operator=(Response&& orig);

Response(const Response& orig) = delete;
Response& operator=(const Response& orig) = delete;

virtual ~Response();

Status & Body

bool has_content() const;
Response& status(int code);
int status() const;

void body(const string_view& body);
void body(const char* buf, unsigned size);
const string& body() const;

Headers & Cookies

string_view header() const;

Response& header(const char* buf, unsigned size);
Response& header(const map<string,string>& header);
Response& header(Header header, const string& value);
Response& header(const string& name, const string& value);
Response& header(const string_view& n, const string_view& v);
Response& header(const map<string_view,string_view>& headers);

Response& set_cookie(const string& name, const string& value);

9.4 ResourceBuilder Class

ResourceBuilder registers HTTP routes and binds them to member function handlers.

Route Registration

template<typename MemPtr>
auto PUT(const string& path, MemPtr mptr);

template<typename MemPtr>
auto POST(const string& path, MemPtr mptr);

template<typename MemPtr>
auto DEL(const string& path, MemPtr mptr);

template<typename MemPtr>
auto GET(const string& path, MemPtr mptr);

template<typename MemPtr>
auto add(const string& method, const string& path, MemPtr mptr);

Each method returns a RouteBuilder for additional configuration.

9.5 Request Class

Request represents an incoming HTTP request, including headers, method, URI, cookies, parameters, and uploaded files.

Constructors

Request(void*);
Request(Request&& orig);
Request& operator=(Request&& orig);
Request(const Request& orig);
Request& operator=(const Request& orig);

virtual ~Request();

General Properties

Header header() const;
bool keepalive() const;
bool has_content() const;
bool authenticated() const;

string_view method() const;
string_view get_uri() const;
string_view get_path() const;
string_view request_line() const;

Content & Types

string_view mime_type() const;
string_view accept_type() const;
string_view content_type() const;
unsigned long content_length() const;

Query & Parameters

string_view get_query_string() const;
unordered_map<string_view,string_view> get_params() const;
string_view get_param(const string& name) const;

Headers & Cookies

string_view get_header(const string& name) const;
list<string_view> get_cookies() const;
string_view get_cookie(const string& name) const;

Uploads

list<FileUpload> get_upload_files() const;

9.6 Param Class Template

Param represents a named parameter with an optional value.

Constructors

Param();
Param(string&& name);
Param(const char* name);
Param(const string& name);
Param(const string_view& name);

Param(Param&& orig);
Param(const Param& orig);
Param& operator=(Param&& orig);
Param& operator=(const Param& orig);

Name & Value

void name(string&& s);
void name(const char* s);
void name(const string& s);
void name(const string_view& s);

string param() const;
bool has_value() const;
const string& name();
char value() const;

9.7 Header Class

Header provides access to HTTP header fields and cookies.

Constructors

Header();
Header(Header&& orig);
Header(const Header& orig);
Header& operator=(Header&& orig);
Header& operator=(const Header& orig);

virtual ~Header();

Accessors

string_view view() const;
const header_t& get() const;

list<string_view> get_cookies() const;
string_view get(const string& name) const;
string_view get_cookie(const string& name) const;

9.8 FileUpload Class

FileUpload represents a single uploaded file in a multipart request.

Constructors

FileUpload();
FileUpload(const string& filename);
FileUpload(HTTPFile* file);
FileUpload(FileUpload&& orig);
FileUpload(const FileUpload& orig);
FileUpload& operator=(FileUpload&& orig);
FileUpload& operator=(const FileUpload& orig);

virtual ~FileUpload();

Properties & Operations

bool has_content() const;
ssize_t file_size() const;
string_view filename() const;
string_view content_type() const;
const string& name_id();

int move_file(const WebService*, const string& new_dir) const;
int save_as(const WebService*, const string& dir, const string& name);

9.9 Stream Class

Stream represents an in‑memory content buffer.

Constructors

Stream();
Stream(Stream&& orig);
Stream(const Stream& orig);
Stream& operator=(Stream&& orig);
Stream& operator=(const Stream& orig);

virtual ~Stream();

Accessors

unsigned int length() const;
const string_view& view() const;
const string_view& content_type() const;
ssize_t save_as(const string& filename) const;

9.10 Uri Class

Uri stores a parsed URI string for a request or resource.

Constructors

Uri();
Uri(Uri&& orig);
Uri(const Uri& orig);
Uri& operator=(Uri&& orig);
Uri& operator=(const Uri& orig);

virtual ~Uri();

Accessor

const string_view& get() const;

9.11 RouteBuilder Class

The RouteBuilder class is a lightweight helper used internally by ResourceBuilder to construct Resource objects. It binds an HTTP method, a path, and a member function pointer, and then produces a fully constructed Resource instance when build() is invoked.

Constructor

RouteBuilder(string method, string path, MemPtr ptr)
  : method(method), path(path), memptr(ptr) {}

Creates a route builder for the given HTTP method, URL path, and handler member function pointer memptr.

Build Functions

template<typename... Args>
Resource* build(Args&&... args) {
  return new Resource(method, path, memptr, forward<Args>(args)...);
}

template<typename... Args>
Resource* build(const Args&... args) {
  return new Resource(method, path, memptr, args...);
}

Both overloads return a dynamically allocated Resource object configured with the method, path, and handler pointer captured by the RouteBuilder.