cl_http
The cl_http package
README
HTTP Client (cl_http)
SMACC2 client library for making HTTP/HTTPS requests using Boost.Beast.
Architecture
/home/brettpac/workspaces/isaac_ros-dev/src/SMACC2/smacc2_client_library/cl_http
The cl_http follows a pure component-based architecture with three specialized components:
ClHttp (Client - Orchestrator)
├── CpHttpConnectionManager - io_context and thread management
├── CpHttpSessionManager - SSL context and session creation
└── CpHttpRequestExecutor - Request execution and response handling
Components
CpHttpConnectionManager
Responsibility: Manages the asynchronous I/O infrastructure
Owns boost::asio::io_context
Manages worker thread lifecycle
Provides strand for thread-safe operations
Lifetime: State machine scoped (created with ClHttp, destroyed with state machine)
CpHttpSessionManager
Responsibility: Manages HTTP/HTTPS session configuration
Parses server URLs (protocol, host, port)
Manages SSL context for HTTPS connections
Creates session objects (http_session or ssl_http_session)
Lifetime: State machine scoped
CpHttpRequestExecutor
Responsibility: Coordinates HTTP request execution
Accepts request parameters (method, path, body, headers)
Acquires strand from CpHttpConnectionManager
Acquires session from CpHttpSessionManager
Executes requests and emits response signals
Lifetime: State machine scoped
Signal Flow
User Code (Behavior)
↓
CpHttpRequestExecutor::executeRequest()
↓
Session created (http_session/ssl_http_session)
↓
Request executed asynchronously
↓
Response received
↓
CpHttpRequestExecutor::onResponseReceived_ (SmaccSignal)
↓ (backward compatibility)
ClHttp::onResponseReceived_ (forwarded)
↓
Behavior callback
Usage
Quick Start: Using Base Behaviors
The easiest way to make HTTP requests is using the provided base behaviors:
#include <cl_http/client_behaviors/cb_http_get_request.hpp>
class StHttpRequest : public smacc2::SmaccState<StHttpRequest, SmExample>
{
public:
using SmaccState::SmaccState;
void runtimeConfigure() override
{
auto behavior = this->configure<cl_http::CbHttpGetRequest>();
}
};
Custom Behaviors: Component-Based Approach
For custom HTTP behavior, inherit from base class and override response handling:
#include <cl_http/client_behaviors/cb_http_request.hpp>
class CbCustomRequest : public cl_http::CbHttpRequestBase
{
public:
CbCustomRequest() : CbHttpRequestBase(CpHttpRequestExecutor::HttpMethod::GET) {}
void onResponseReceived(const CpHttpRequestExecutor::TResponse& response) override
{
if (response.result() == boost::beast::http::status::ok)
{
RCLCPP_INFO(getLogger(), "Success: %s", response.body().c_str());
}
}
};
Advanced: Direct Component Access
For full control, access the executor component directly:
class CbAdvancedHttp : public smacc2::SmaccClientBehavior
{
public:
void runtimeConfigure() override
{
this->requiresComponent(requestExecutor_);
this->getStateMachine()->createSignalConnection(
requestExecutor_->onResponseReceived_,
&CbAdvancedHttp::onResponse,
this
);
}
void onEntry() override
{
std::unordered_map<std::string, std::string> headers = {
{"Accept", "application/json"},
{"Authorization", "Bearer token123"}
};
requestExecutor_->executeRequest(
CpHttpRequestExecutor::HttpMethod::POST,
"/api/data",
R"({"key": "value"})", // JSON body
headers
);
}
void onResponse(const CpHttpRequestExecutor::TResponse& response)
{
RCLCPP_INFO(
getLogger(),
"HTTP %d: %s",
response.result_int(),
response.body().c_str()
);
}
private:
CpHttpRequestExecutor* requestExecutor_;
};
State Machine Configuration
Orthogonal Setup
namespace sm_example
{
namespace clients
{
class ClHttpOrthogonal : public smacc2::Orthogonal<ClHttpOrthogonal>
{
public:
void onInitialize() override
{
auto httpClient = this->createClient<cl_http::ClHttp>(
"https://example.com", // Server URL
1500 // Timeout (ms)
);
}
};
}
}
Server URL Formats
The client automatically detects HTTP vs HTTPS based on the URL:
// HTTPS (SSL enabled)
this->createClient<cl_http::ClHttp>("https://api.example.com");
// HTTP (no SSL)
this->createClient<cl_http::ClHttp>("http://api.example.com");
// Custom port
this->createClient<cl_http::ClHttp>("https://api.example.com:8443");
Supported HTTP Methods
GET: Retrieve data
POST: Submit data
PUT: Update/replace data
// Using enum
CpHttpRequestExecutor::HttpMethod::GET
CpHttpRequestExecutor::HttpMethod::POST
CpHttpRequestExecutor::HttpMethod::PUT
Response Handling
Responses use Boost.Beast’s http::response<http::string_body> type:
void onResponse(const CpHttpRequestExecutor::TResponse& response)
{
// Status code
auto status = response.result(); // enum
int statusCode = response.result_int(); // int (200, 404, etc.)
// Headers
auto contentType = response[boost::beast::http::field::content_type];
// Body
std::string body = response.body();
// Check success
if (status == boost::beast::http::status::ok)
{
RCLCPP_INFO(getLogger(), "Success!");
}
}
Pure Component-Based Architecture
The cl_http package uses a pure component-based architecture with no legacy API remnants. All functionality is accessed through components:
Direct component access is the only supported pattern
Base behaviors (
CbHttpRequestBase,CbHttpGetRequest,CbHttpPostRequest) use components internally
Examples
Example 1: Simple GET Request
class StFetchData : public smacc2::SmaccState<StFetchData, SmExample>
{
public:
using SmaccState::SmaccState;
void runtimeConfigure() override
{
this->configure<cl_http::CbHttpGetRequest>();
}
// Transitions
using Transition = smacc2::transition<EvCbSuccess<CbHttpGetRequest>, StNextState>;
};
Example 2: POST with Custom Response Handling
class CbApiSubmit : public cl_http::CbHttpPostRequest
{
public:
void onEntry() override
{
// Set up POST data
requestExecutor_->executeRequest(
CpHttpRequestExecutor::HttpMethod::POST,
"/api/submit",
R"({"username": "robot", "action": "navigate"})",
{{"Content-Type", "application/json"}}
);
}
void onResponseReceived(const CpHttpRequestExecutor::TResponse& response) override
{
if (response.result() == boost::beast::http::status::created)
{
RCLCPP_INFO(getLogger(), "Resource created successfully");
this->postSuccessEvent();
}
else
{
RCLCPP_ERROR(getLogger(), "API error: %d", response.result_int());
this->postFailureEvent();
}
}
};
Example 3: Multiple Sequential Requests
class CbMultiRequest : public smacc2::SmaccAsyncClientBehavior
{
public:
void onEntry() override
{
this->requiresComponent(requestExecutor_);
// First request
requestExecutor_->executeRequest(
CpHttpRequestExecutor::HttpMethod::GET,
"/api/status"
);
}
void onResponse(const CpHttpRequestExecutor::TResponse& response)
{
if (requestCount_ == 0)
{
// Handle first response, make second request
requestCount_++;
requestExecutor_->executeRequest(
CpHttpRequestExecutor::HttpMethod::POST,
"/api/update",
response.body() // Use first response as second request body
);
}
else
{
// All requests complete
this->postSuccessEvent();
}
}
private:
int requestCount_ = 0;
CpHttpRequestExecutor* requestExecutor_;
};
Dependencies
Boost.Beast: HTTP/HTTPS client implementation
Boost.Asio: Asynchronous I/O
OpenSSL: HTTPS/TLS support
SMACC2: State machine framework
Testing
Run the reference state machine:
ros2 launch sm_atomic_http sm_atomic_http.py
This will make GET requests to https://example.com and demonstrate the HTTP client functionality.
Architecture Benefits
Single Responsibility: Each component has a focused purpose
Testability: Components can be tested independently
Reusability: Components can be composed differently for different use cases
Thread Safety: Proper strand usage prevents race conditions
Lifetime Management: State machine scoped components ensure proper cleanup
SMACC2 Best Practices: Follows recommended patterns for client libraries
Troubleshooting
SSL Errors
If you encounter SSL certificate errors:
SSL handshake failed: certificate verify failed
This is expected for self-signed certificates. The client currently validates certificates. For testing with self-signed certs, you may need to configure OpenSSL context (advanced usage).
Connection Timeouts
The default timeout is 1500ms. Adjust when creating the client:
this->createClient<cl_http::ClHttp>("https://slow-api.com", 5000); // 5 second timeout
Port Issues
Ensure the correct port is specified:
HTTP: default port 80
HTTPS: default port 443
Custom: specify in URL (
https://api.example.com:8443)
License
Copyright 2023-2024 RobosoftAI Inc.
Licensed under the Apache License, Version 2.0.