Embedded AI Series – A Simple Header File to Quickly Enable HTTP Capabilities for Rockchip RV1126_RV1109 AI Applications

1. Introduction

In embedded AI application development, we often need to add HTTP network interfaces to devices for remote control, data upload, or information queries.

Traditional methods often require introducing complex dependency libraries, which can be cumbersome.

In practical development, I prefer using a single header file solution, such as cpp-httplib, which is a single-header C++ HTTP library that allows your project to quickly and easily have HTTP server and client capabilities.

In addition to powerful HTTP capabilities, cpp-httplib also provides useful non-HTTP components, such as thread pools and utility classes, which are also very useful in embedded AI development.

2. What is cpp-httplib

cpp-httplib is an open-source C++11 library that consists of a single header file (httplib.h) but provides complete HTTP server and client functionality. Its features include:

  • • Zero dependencies: Only include one header file, no need to install other libraries
  • • Cross-platform: Supports Linux, Windows, macOS, etc.
  • • Simple and easy to use: Intuitive API design, low learning cost
  • • Comprehensive functionality: Supports HTTPS, file uploads, multipart forms, etc.

3. Quick Start

3.1 Download the Header File

First, download the httplib.h header file from the GitHub repository:

wget https://raw.githubusercontent.com/yhirose/cpp-httplib/master/httplib.h

3.2 Example HTTP Server Program

3.2.1 Prepare Example Source Code

Here is a simple HTTP server example:

#include "httplib.h"
#include <iostream>

int main() {
    httplib::Server svr;

    svr.Get("/hello", [](const httplib::Request& req, httplib::Response& res) {
        res.set_content("Hello World!\n\n", "text/plain");
    });

    svr.Post("/predict", [](const httplib::Request& req, httplib::Response& res) {
        std::string result = "Received: " + req.body + "\n\n";
        res.set_content(result, "text/plain");
    });

    std::cout << "Server starting on http://localhost:8080" << std::endl;
    
    // Listening returns false indicates an error
    if (!svr.listen("0.0.0.0", 8080)) {
        std::cerr << "Failed to start server on port 8080. Maybe the port is already in use?" << std::endl;
        return -1;
    }

    return 0;
}

3.2.2 Compile and Run

Save the above code as main.cpp in a Linux environment, placing it in the same directory as httplib.h, compile and run:

# Create a development directory, e.g., httpTest
mkdir httpTest
cd httpTest

# Place the above two files httplib.h and main.cpp in this directory

# Compile to generate the executable file httpServer
g++ -std=c++11 main.cpp -o httpServer -lpthread

# Run httpServer
chmod +x httpServer
./httpServer

You may encounter port occupation issues, as shown in the figure below:

Embedded AI Series - A Simple Header File to Quickly Enable HTTP Capabilities for Rockchip RV1126_RV1109 AI Applications

In this case, you need to modify the source code to use an unoccupied port number, such as 38080.

# Check if 38080 is occupied
netstat -tulpn | grep :38080

After modifying the port number in the source code to 38080, compile and run, you can see the program in a blocking listen state:

Embedded AI Series - A Simple Header File to Quickly Enable HTTP Capabilities for Rockchip RV1126_RV1109 AI Applications

3.2.3 Use Tools to Test the Server Example Program

The above server example program provides two HTTP interfaces: the /hello endpoint with the GET method, and the /predict endpoint with the POST method. We can use existing HTTP client tools to test and verify.

Common tools on Windows include Postman, while in Linux environments, we can use the curl command. Here we will demonstrate using curl.Open another terminal and enter:

# Verify hello interface
curl http://localhost:38080/hello

# Verify predict interface
curl -X POST -d '{"data": "test"}' http://localhost:38080/predict

You can see the returned messages, both interfaces verified OK:

Embedded AI Series - A Simple Header File to Quickly Enable HTTP Capabilities for Rockchip RV1126_RV1109 AI Applications

3.3 Create an HTTP Client

At the same time, you can easily create an HTTP client, which is very simple, so we won’t compile and test it:

#include "httplib.h"
#include <iostream>

int main() {
    // Create HTTP client
    httplib::Client cli("http://localhost:8080");

    // Send GET request
    if (auto res = cli.Get("/hello")) {
        std::cout << "Response status: " << res->status << std::endl;
        std::cout << "Response content: " << res->body << std::endl;
    } else {
        std::cout << "Request failed: " << res.error() << std::endl;
    }

    // Send POST request (for AI inference)
    std::string json_data = "{\"input\": \"test data\"}";
    if (auto res = cli.Post("/predict", json_data, "application/json")) {
        std::cout << "AI inference result: " << res->body << std::endl;
    }

    return 0;
}

4. Practical Functional Components Provided by cpp-httplib

4.1 Thread Pool

cpp-httplib internally implements an efficient thread pool, mainly used for handling concurrent HTTP requests.However, this thread pool is designed to be general enough that we can also use it directly in our own projects. Here is an example code:

#include "httplib.h"
#include <iostream>
#include <chrono>

int main() {
    // Create thread pool, default number of threads is the current hardware concurrency
    httplib::ThreadPool pool;
    
    // Or specify the number of threads
    // httplib::ThreadPool pool(4); // 4 threads
    
    // Submit tasks to the thread pool
    auto future1 = pool.enqueue([]() {
        std::this_thread::sleep_for(std::chrono::seconds(2));
        return "Task 1 completed";
    });
    
    auto future2 = pool.enqueue([]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        return "Task 2 completed";
    });
    
    // Get task results
    std::cout << future2.get() << std::endl; // First completed task
    std::cout << future1.get() << std::endl; // Later completed task
    
    return 0;
}

4.2 File Service

cpp-httplib has built-in static file service functionality:

int main() {
    httplib::Server svr;
    
    // Provide static file service
    svr.set_base_dir("./www");
    
    // Custom file download
    svr.Get("/download/model", [](const httplib::Request& req, httplib::Response& res) {
        // Set file download header
        res.set_header("Content-Disposition", "attachment; filename=model.bin");
        
        // Read and send model file
        std::ifstream file("model.bin", std::ios::binary);
        if (file) {
            std::string content((std::istreambuf_iterator<char>(file)), 
                               std::istreambuf_iterator<char>());
            res.set_content(content, "application/octet-stream");
        }
    });
    
    svr.listen("0.0.0.0", 8080);
    return 0;
}

4.3 HTTPS Support

Enabling HTTPS is also very simple:

int main() {
    // Enable HTTPS
    httplib::SSLServer svr("./server.crt", "./server.key");
    
    svr.Get("/secure", [](const httplib::Request& req, httplib::Response& res) {
        res.set_content("Secure connection", "text/plain");
    });
    
    svr.listen("0.0.0.0", 8443);
    return 0;
}

4.4 String Processing Utilities

cpp-httplib provides a series of string processing functions, example application code:

#include "httplib.h"

void string_utils_example() {
    std::string str = "Hello World";
    
    // String conversion
    std::string lower = httplib::detail::to_lower(str);
    std::string upper = httplib::detail::to_upper(str);
    
    // String trimming
    std::string padded = "   hello   ";
    std::string trimmed = httplib::detail::trim(padded);
    
    // String splitting
    std::string csv = "apple,banana,orange";
    auto parts = httplib::detail::split(csv, ',');
    
    // URL encoding/decoding
    std::string url_encoded = httplib::detail::encode_url("hello world&test");
    std::string url_decoded = httplib::detail::decode_url("hello%20world%26test");
    
    // Base64 encoding/decoding
    std::string base64_encoded = httplib::detail::base64_encode("hello world");
    std::string base64_decoded = httplib::detail::base64_decode("aGVsbG8gd29ybGQ=");
    
    std::cout << "Lower: " << lower << std::endl;
    std::cout << "Trimmed: '" << trimmed << "'" << std::endl;
    std::cout << "Parts count: " << parts.size() << std::endl;
}

4.5 File System Utilities

#include "httplib.h"

void filesystem_examples() {
    // Check if file exists
    bool exists = httplib::detail::exists("test.txt");
    
    // Read file content
    std::string content = httplib::detail::read_file("test.txt");
    
    // Write to file
    httplib::detail::write_file("output.txt", "Hello File");
    
    // Get file size
    size_t size = httplib::detail::get_file_size("test.txt");
    
    // Scan directory (requires enabling filesystem support)
    #ifdef CPPHTTPLIB_USE_STD_FILESYSTEM
    auto files = httplib::detail::list_files(".");
    for (const auto& file : files) {
        std::cout << file << std::endl;
    }
    #endif
}

4.6 Network Utilities

#include "httplib.h"

void network_utils_example() {
    // Get local IP address
    std::string host = httplib::detail::get_host_name();
    
    // Check if port is occupied
    bool port_used = httplib::detail::is_port_available(8080);
    
    std::cout << "Host: " << host << std::endl;
    std::cout << "Port 8080 available: " << (port_used ? "No" : "Yes") << std::endl;
}

4.7 Date and Time Utilities

#include "httplib.h"

void datetime_examples() {
    // Get current time string (HTTP date format)
    std::string http_date = httplib::detail::get_header_value(
        nullptr, httplib::detail::make_http_date_header()
    );
    
    // Get timestamp
    auto now = std::chrono::system_clock::now();
    std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
    
    std::cout << "HTTP Date: " << http_date << std::endl;
    std::cout << "Timestamp: " << timestamp << std::endl;
}

5. Applications in Embedded AI Projects

5.1 Scenario 1: Model as a Service

Expose the trained AI model through HTTP interfaces for easy access by other systems:

// AI model service example
class AIModel {
public:
    std::string predict(const std::string& input) {
        // Implement model inference logic here
        return "Prediction result";
    }
};

int main() {
    AIModel model;
    httplib::Server svr;

    // Model inference interface
    svr.Post("/model/predict", [&model](const httplib::Request& req, httplib::Response& res) {
        try {
            std::string result = model.predict(req.body);
            res.set_content(result, "application/json");
        } catch (const std::exception& e) {
            res.status = 500;
            res.set_content(e.what(), "text/plain");
        }
    });

    // Model information interface
    svr.Get("/model/info", [&model](const httplib::Request& req, httplib::Response& res) {
        nlohmann::json info = {
            {"model_name", "resnet18"},
            {"version", "1.0"},
            {"input_shape", "224x224x3"}
        };
        res.set_content(info.dump(), "application/json");
    });

    svr.listen("0.0.0.0", 8080);
    return 0;
}

5.2 Scenario 2: Device Status Monitoring

Add status monitoring interfaces for embedded devices:

// Device status service
class DeviceMonitor {
private:
    float cpu_temperature = 45.6f;
    int memory_usage = 65;

public:
    nlohmann::json get_status() {
        return {
            {"cpu_temperature", cpu_temperature},
            {"memory_usage", memory_usage},
            {"model_inference_count", 1234},
            {"status", "normal"}
        };
    }
};

int main() {
    DeviceMonitor monitor;
    httplib::Server svr;

    // Device status query
    svr.Get("/status", [&monitor](auto& req, auto& res) {
        auto status = monitor.get_status();
        res.set_content(status.dump(), "application/json");
    });

    // Health check
    svr.Get("/health", [](auto& req, auto& res) {
        res.set_content("OK", "text/plain");
    });

    svr.listen("0.0.0.0", 8080);
    return 0;
}

6. Performance Considerations

In embedded environments, performance is key. cpp-httplib performs excellently in this regard:

  • • Low memory usage: Single-header design, small size after compilation
  • • Concurrent processing: Supports multi-threaded request handling
  • • Resource-friendly: Buffer size and thread count can be adjusted as needed
// Performance optimization configuration example
int main() {
    httplib::Server svr;
    
    // Set read timeout
    svr.set_read_timeout(5, 0); // 5 seconds
    
    // Set write timeout  
    svr.set_write_timeout(5, 0); // 5 seconds
    
    // Limit request body size (to prevent memory overflow)
    svr.set_payload_max_length(1024 * 1024); // 1MB
    
    // Register routes
    svr.Get("/ai/predict", [](auto& req, auto& res) {
        // AI inference processing
    });
    
    svr.listen("0.0.0.0", 8080);
    return 0;
}

7. Conclusion

cpp-httplib provides an extremely convenient HTTP solution for C++ developers, especially for embedded AI developers. Its single-header design makes integration very simple, and its powerful features can meet most network communication needs.Main advantages include:

  • • Zero dependencies, single header file
  • • Quick integration, can be up and running in minutes
  • • Complete HTTP server and client functionality
  • • Suitable for resource-constrained embedded environments
  • • Active open-source community support

Leave a Comment