Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
63e61f2
fix: updated build.sh to work with docker
Apr 13, 2026
5faaa32
fix: bypass AI_ADDRCONFIG for numeric-IP gateway URLs
Apr 1, 2026
b5bc480
feat: network flap/no dns fixes
Apr 1, 2026
f38f6fa
ci: update to run connectivity tests
Apr 13, 2026
498a789
chore: linting
Apr 13, 2026
96ee593
chore: add SPDX-License-Identifier to vendored websocketpp header
Apr 13, 2026
b359a31
chore: exclude vendored websocketpp from BlackDuck scan
Apr 13, 2026
43a6ee8
fix: address Copilot review comments on PR #83
Apr 13, 2026
4e39799
Potential fix for pull request finding
brendanobra Apr 13, 2026
3a48bd0
Potential fix for pull request finding
brendanobra Apr 13, 2026
037d8be
Potential fix for pull request finding
brendanobra Apr 13, 2026
f98621c
fix: synchronize connectResult reads under mutex; add copyright heade…
Copilot Apr 13, 2026
1d659cc
fix: address second-round Copilot review comments on PR #83
Apr 13, 2026
a3094c5
chore: lint
Apr 13, 2026
212df73
fix: address third-round Copilot review comments on PR #83
Apr 13, 2026
a1f3800
chore: lint
Apr 13, 2026
82cf882
gateway: handle AlreadyConnected without false disconnect event
Apr 13, 2026
9636800
chore: append BSD-3-Clause license text for vendored WebSocket++ to L…
Apr 14, 2026
4d7c3d7
fix: address fifth-round Copilot and human review comments on PR #83
Apr 14, 2026
6852276
chore: fmt
Apr 14, 2026
65dae23
Potential fix for pull request finding
brendanobra Apr 14, 2026
17860dc
fix: address sixth-round Copilot review comments on PR #83
Apr 14, 2026
95969be
chore: fmt
Apr 14, 2026
acf6654
fix: address seventh-round Copilot review comments on PR #83
Apr 14, 2026
0dbd19a
fix: address eighth-round Copilot review comments on PR #83
Apr 14, 2026
4d41cc0
fix: add Connecting state to Transport so disconnect() aborts in-flig…
Apr 14, 2026
3d62c00
fix: address ninth-round Copilot review comments on PR #83
Apr 14, 2026
4cbe731
fix: prevent IO-thread event loss during AlreadyConnected wrapper window
Apr 14, 2026
87093da
fix: use explicit .load()/.store() on atomic connectionStatus_
Apr 14, 2026
f1ea7a9
fix: remove skipPrevForward race in connect() wrapper
Apr 14, 2026
2b122ae
feat: adding test-soc.sh to run against device over ssh tunnel for te…
Apr 15, 2026
b730dda
chore: remove file that should not be commited
Apr 15, 2026
ed55624
Merge branch 'main' into topic/RDKEMW-16441
brendanobra Apr 30, 2026
249d3fe
Merge pull request #83 from rdkcentral/topic/RDKEMW-16441
brendanobra May 4, 2026
1d68e33
chore: merge conflict resolution
Jun 10, 2026
72eb42d
test: align gateway connect failure tests
Jun 10, 2026
e1accdb
Merge pull request #94 from rdkcentral/auto-sync/develop-to-main
brendanobra Jun 10, 2026
72f22a7
merge(main): integrate develop with conflict resolution for PR #100
Jul 14, 2026
49948dd
test(gateway): stabilize no-server failure paths for async connect
Jul 14, 2026
df222ee
fix(review): address PR #101 transport and gateway feedback
Jul 14, 2026
2f86980
fix(license): remove unresolved MIT placeholder block
Jul 14, 2026
099f94e
fix(review): address latest gateway, test, and docs feedback
Jul 14, 2026
d79c64b
fix(review): prevent in-flight reconnect races and stale headers
Jul 14, 2026
3841e9d
fix(transport): address review feedback on headers and tests
Jul 14, 2026
93e8647
fix(transport): address additional PR review feedback
Jul 14, 2026
6d3b026
fix(test): remove remaining TOCTOU port races
Jul 14, 2026
4168f6f
fix(transport): address callback ordering and lock contention
Jul 14, 2026
cde73b2
fix(test/docs): address new PR review comments
Jul 14, 2026
15adef8
fix(headers): address review feedback on includes and moves
Jul 14, 2026
0b20234
fix(logger): avoid inserting syslog priority lookups
Jul 14, 2026
802d182
fix(transport): harden onOpen and tighten header test
Jul 14, 2026
6e072c3
fix(transport): use single state load in connect guard
Jul 14, 2026
2138256
fix(logger/coverage): avoid inserting lookups and skip conversion ops
Jul 14, 2026
12b456f
fix(logger/transport): tighten casts and header injection safety
Jul 14, 2026
16b8f37
Merge pull request #101 from rdkcentral/pr100-main-integration
brendanobra Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .bdignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# BlackDuck scan exclusions
# Vendored third-party source — attributed in NOTICE and LICENSE files
src/vendor/
5 changes: 5 additions & 0 deletions .github/skills/openspec-coverage/openspec_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,15 @@ def extract_methods_from_code(file_path):
method_pattern = re.compile(
r"^\s*(?:[\w:<>,~]+\s+)+([\w:~]+)\s*\([^)]*\)\s*(const)?\s*[{;]"
)
conversion_operator_pattern = re.compile(r"\boperator\s+[^\s(]+\s*\(")
methods = set()
try:
with open(file_path, encoding="utf-8", errors="ignore") as f:
for line in f:
# Skip conversion operators (e.g., 'explicit operator bool() const'),
# which do not have a standalone method identifier to report.
if conversion_operator_pattern.search(line):
continue
m = method_pattern.match(line)
if m:
methods.add(m.group(1))
Expand Down
1 change: 1 addition & 0 deletions .version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.1.8
22 changes: 0 additions & 22 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -202,25 +202,3 @@
limitations under the License.

===========================================================================

MIT License

Copyright (c) YEAR COPYRIGHT HOLDER

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ if (value) {

- If the header is present, its value is returned.
- If the header is not present, `std::nullopt` is returned.
- Header-name lookup is case-sensitive; use the same capitalization as received from the server.

#### Thread Safety
Header operations are thread-safe. Access to response headers is protected by a mutex internally.
Expand Down
48 changes: 45 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,39 @@

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
IMAGE="firebolt-cpp-transport-ci:local"

# Pre-scan for --docker before arg parsing so forwarded args are preserved
use_docker=false
_forward_args=()
for _arg in "$@"; do
[[ "$_arg" == "--docker" ]] && { use_docker=true; continue; }
_forward_args+=("$_arg")
done

if $use_docker; then
for _bdir in build build-dev; do
_cache="$SCRIPT_DIR/$_bdir/CMakeCache.txt"
if [[ -f "$_cache" ]]; then
_cached=$(grep '^CMAKE_HOME_DIRECTORY' "$_cache" 2>/dev/null | cut -d= -f2 || true)
# In Docker, the workspace is mounted at /workspace. Only wipe if the
# cache was configured for a different environment (e.g. a native host build).
if [[ -n "$_cached" && "$_cached" != "/workspace" ]]; then
echo "Wiping stale $_bdir (configured at $_cached, expected /workspace)..."
rm -rf "$SCRIPT_DIR/$_bdir"
fi
fi
done
if ! docker image inspect "$IMAGE" &>/dev/null; then
echo "Building CI Docker image (one-time)..."
docker build -t "$IMAGE" -f "$SCRIPT_DIR/.github/Dockerfile" "$SCRIPT_DIR"
fi
exec docker run --rm --user "$(id -u):$(id -g)" \
-v "$SCRIPT_DIR:/workspace" -w /workspace \
"$IMAGE" ./build.sh "${_forward_args[@]}"
fi

bdir="build"
do_install=false
params=
Expand Down Expand Up @@ -50,12 +83,21 @@ while [[ ! -z $1 ]]; do
esac; shift
done

[[ ! -z $SYSROOT_PATH ]] || { echo "SYSROOT_PATH not set" >/dev/stderr; exit 1; }
[[ -e $SYSROOT_PATH ]] || { echo "SYSROOT_PATH not exist ($SYSROOT_PATH)" >/dev/stderr; exit 1; }
SYSROOT_PATH="${SYSROOT_PATH:-/}"
[[ -e $SYSROOT_PATH ]] || { echo "SYSROOT_PATH does not exist ($SYSROOT_PATH)" >/dev/stderr; exit 1; }

$cleanFirst && rm -rf $bdir

if [[ ! -e "$bdir" || -n "$@" ]]; then
_cache="$SCRIPT_DIR/$bdir/CMakeCache.txt"
if [[ -f "$_cache" ]]; then
_cached=$(grep '^CMAKE_HOME_DIRECTORY' "$_cache" 2>/dev/null | cut -d= -f2 || true)
if [[ -n "$_cached" && "$_cached" != "$SCRIPT_DIR" ]]; then
echo "Wiping stale $bdir (configured at $_cached, now at $SCRIPT_DIR)..."
rm -rf "$SCRIPT_DIR/$bdir"
fi
fi

if [[ ! -f "$bdir/CMakeCache.txt" || $# -gt 0 ]]; then
params+=" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
command -v ccache >/dev/null 2>&1 && params+=" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
cmake -B $bdir \
Expand Down
4 changes: 2 additions & 2 deletions fmt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ fi
RUN="docker run --rm --user $(id -u):$(id -g) -v $SCRIPT_DIR:/workspace $IMAGE bash -c"

if [[ "${1:-}" == "--fix" ]]; then
$RUN "find /workspace/src /workspace/include /workspace/test -type f \( -name '*.cpp' -o -name '*.h' \) | xargs clang-format -i"
$RUN "find /workspace/src /workspace/include /workspace/test -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -0 clang-format -i"
echo "Done. Files reformatted."
else
$RUN "find /workspace/src /workspace/include /workspace/test -type f \( -name '*.cpp' -o -name '*.h' \) | xargs clang-format --dry-run --Werror"
$RUN "find /workspace/src /workspace/include /workspace/test -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -0 clang-format --dry-run --Werror"
echo "Formatting OK."
fi
39 changes: 39 additions & 0 deletions include/firebolt/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,45 @@ struct Config

/** Watchdog polling cycle in milliseconds. Default: 500. */
unsigned watchdogCycle_ms = 500;

/**
* @brief Retry policy for the initial gateway connection.
*
* On embedded devices the Firebolt client may start before the gateway
* daemon is ready (poor systemd ordering) or before the loopback interface
* has an IPv4 address assigned. Setting reconnect_max_attempts > 0 causes
* connect() to retry up to that many times, waiting reconnect_delay_ms
* between each attempt, before returning an error to the caller.
Comment thread
brendanobra marked this conversation as resolved.
*
* Each attempt is bounded by connect_attempt_timeout_ms: if the underlying
* transport (DNS + TCP + WebSocket handshake) does not complete within that
* window, the attempt is aborted and counted as a failure. connect()
* therefore blocks for at most:
* (reconnect_max_attempts + 1) * connect_attempt_timeout_ms
* + reconnect_max_attempts * reconnect_delay_ms
*
* Capped internally at 100 retries regardless of this value.
*
* Default: 0 (single attempt, no retry; connect() returns the last
* transport error, e.g. Error::NotConnected, Error::Timedout, or
* Error::General, or Error::NotConnected on per-attempt timeout).
*/
unsigned reconnect_max_attempts = 0;

/** Milliseconds to wait between reconnect attempts. Default: 1000. */
unsigned reconnect_delay_ms = 1000;

/**
* @brief Per-attempt timeout for the initial connection handshake (ms).
*
* If DNS resolution + TCP connect + WebSocket handshake do not complete
* within this window, the in-flight attempt is aborted and connect()
* either retries (if reconnect_max_attempts > 0) or returns
* Error::NotConnected to the caller.
*
* Default: 10000 (10 seconds).
*/
unsigned connect_attempt_timeout_ms = 10000;
};

} // namespace Firebolt
4 changes: 4 additions & 0 deletions include/firebolt/gateway.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <functional>
#include <future>
#include <nlohmann/json.hpp>
#include <optional>
#include <string>

namespace Firebolt::Transport
Expand All @@ -36,6 +37,9 @@ class IGateway
public:
virtual ~IGateway();

// NOTE: onConnectionChange is invoked once with the final result on the
// connect() calling thread. All subsequent callbacks (disconnect, watchdog
// reconnect) fire on the websocketpp IO thread. Callers must be thread-safe.
Comment thread
brendanobra marked this conversation as resolved.
virtual Firebolt::Error connect(const Firebolt::Config& config, ConnectionChangeCallback onConnectionChange) = 0;
virtual Firebolt::Error disconnect() = 0;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "openspec-with-copliot-experience",
"name": "openspec-with-copilot-experience",
"private": true,
"scripts": {
"dev": "slidev --open",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,10 @@ https://github.com/rdkcentral/firebolt-cpp-transport/tree/feature/openspec
- Current slides are Auto Generated from OpenSpec and Copilot chat conversations.
- Multiple Reusable Skills generated for creating slides based on the effort
```sh
$ /init-slidev-presentation "openspec-with-copliot-experience" --content "slides.md"
$ /add-slide "openspec-with-copliot-experience" --content "slides.md" --slideTitle "Slide Title"
$ /add-diagram "openspec-with-copliot-experience" --content "slides.md" --diagramCode "mermaid code here"
$ /export-slidev-presentation "openspec-with-copliot-experience" --format pdf
$ /init-slidev-presentation "openspec-with-copilot-experience" --content "slides.md"
$ /add-slide "openspec-with-copilot-experience" --content "slides.md" --slideTitle "Slide Title"
$ /add-diagram "openspec-with-copilot-experience" --content "slides.md" --diagramCode "mermaid code here"
$ /export-slidev-presentation "openspec-with-copilot-experience" --format pdf
```

---
Expand Down
73 changes: 0 additions & 73 deletions presentations/transport-slides/slides.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,76 +132,3 @@ layout: center
# Thank You!

[GitHub](https://github.com/rdkcentral/firebolt-cpp-transport)

---

# Connection Protocol

The Firebolt Transport Layer uses WebSocket as its underlying transport, with JSON-RPC layered on top for structured messaging and event notifications.

**Message Flow:**

```
┌─────────────┐ WebSocket ┌─────────────┐
│ Client │<------------------->│ Gateway │
└─────────────┘ JSON-RPC └─────────────┘
```

- Client connects to a configurable WebSocket URL (default: ws://127.0.0.1:9998)
- Connection state managed via callbacks (e.g., ConnectionChangeCallback)
- Supports reconnect, disconnect, and error handling
- All requests, responses, and events are JSON-RPC messages
- Legacy support via `legacyRPCv1` flag

---

# Integration Points

- Standalone library for JSON-RPC over WebSocket
- Agnostic to client usage and WebSocket implementation
- API schemas and business logic reside in separate repos (e.g., firebolt-cpp-client)
- IGateway and IHelper interfaces are designed for external use
- Initialization/configuration flows for Gateway are an open requirement

**Conclusion:**
The transport library is decoupled from client and schema logic, providing a generic, extensible foundation for JSON-RPC over WebSocket. Integration patterns and initialization flows should be defined to support external consumers and SDKs.
---

# Firebolt C++ Transport

A modern, efficient transport layer for Firebolt.

---

# Project Overview

- C++ implementation
- Modular design
- JSON-RPC support
- Logging and diagnostics

---

# Architecture

- Gateway
- Helpers
- Transport core
- Utilities

---

# Key Features

- High performance
- Extensible
- Easy integration
- Comprehensive unit tests

---
layout: center
---

# Thank You!

[GitHub](https://github.com/rdkcentral/firebolt-cpp-transport)
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ target_link_libraries(${TARGET}

target_include_directories(${TARGET}
PRIVATE
# vendor/ must come before the system websocketpp headers so that
# our patched endpoint.hpp (numeric-IP resolver fix) takes priority.
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/vendor>
Comment thread
brendanobra marked this conversation as resolved.
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
PUBLIC
Expand Down
Loading
Loading