Skip to content

Commit

Permalink
feat(websocket): Added linux port for websocket
Browse files Browse the repository at this point in the history
  • Loading branch information
suren-gabrielyan-espressif committed Jul 7, 2023
1 parent ecc465d commit eed36e3
Show file tree
Hide file tree
Showing 18 changed files with 310 additions and 15 deletions.
17 changes: 5 additions & 12 deletions .github/workflows/modem__build-host-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ jobs:
- name: Build and Test
shell: bash
run: |
apt-get update
apt-get update && apt-get install -y gcc-8 g++-8 python3-pip
apt-get install -y rsync
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
Expand All @@ -129,9 +128,9 @@ jobs:
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}
gcov-8 `find . -name "esp_modem*gcda" -printf '%h\n' | head -n 1`/*
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x esp_modem_coverage.xml
mkdir docs_gcovr
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html docs_gcovr
cp -rf docs_gcovr $GITHUB_WORKSPACE
mkdir modem_coverage_report
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html modem_coverage_report
cp -rf modem_coverage_report $GITHUB_WORKSPACE
- name: Code Coverage Summary Report
uses: irongut/[email protected]
with:
Expand All @@ -151,13 +150,7 @@ jobs:
uses: actions/upload-artifact@v3
if: always()
with:
name: docs_gcovr
name: modem_coverage_report
path: |
${{ env.COMP_DIR }}/docs_gcovr
${{ env.COMP_DIR }}/modem_coverage_report
if-no-files-found: error
- name: Deploy code coverage results
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs_gcovr
41 changes: 41 additions & 0 deletions .github/workflows/publish-coverage-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Publish coverage report to Github Pages

on:
workflow_run:
workflows: ["websocket: build/host-tests", "esp-modem: build/host-tests"]
types:
- completed

jobs:
publish_github_pages:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Download Websocket Artifact
uses: dawidd6/action-download-artifact@v2
with:
workflow: websocket__build-host-tests.yml
workflow_conclusion: success
name: websocket_coverage_report
path: websocket_coverage_report_artifact
- name: Download Modem Artifact
uses: dawidd6/action-download-artifact@v2
with:
workflow: modem__build-host-tests.yml
workflow_conclusion: success
name: modem_coverage_report
path: modem_coverage_report_artifact
- name: Merge HTML files
run: |
echo "<html><body>" > index.html
cat modem_coverage_report_artifact/index.html >> index.html
cat websocket_coverage_report_artifact/index.html >> index.html
echo "</body></html>" >> index.html
mkdir coverage_report
mv index.html coverage_report
- name: Deploy generated docs
uses: JamesIves/[email protected]
with:
branch: gh-pages
folder: coverage_report
74 changes: 74 additions & 0 deletions .github/workflows/websocket__build-host-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: "websocket: build/host-tests"

on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
host_test_websocket:
if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push'
name: Host Tests
runs-on: ubuntu-20.04
permissions:
contents: write
container: espressif/idf:latest
env:
COMP_DIR: esp-protocols/components/esp_websocket_client
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
path: esp-protocols
- name: Build and Test
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/tests/host_test
idf.py build
./build/websocket-host.elf
- name: Build with Coverage Enabled
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/tests/host_test
cat sdkconfig.ci.coverage >> sdkconfig.defaults
rm -rf build sdkconfig
idf.py fullclean
idf.py build
./build/websocket-host.elf
- name: Run Coverage
shell: bash
run: |
apt-get update && apt-get install -y gcc-8 g++-8 python3-pip rsync
python -m pip install gcovr
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}
gcov `find . -name "*gcda"`
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x websocket_coverage.xml
mkdir websocket_coverage_report
touch websocket_coverage_report/.nojekyll
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html websocket_coverage_report
cp -rf websocket_coverage_report websocket_coverage.xml $GITHUB_WORKSPACE
- name: Code Coverage Summary Report
uses: irongut/[email protected]
with:
filename: esp-protocols/**/websocket_coverage.xml
badge: true
fail_below_min: false
format: markdown
hide_branch_rate: false
hide_complexity: false
indicators: true
output: both
thresholds: '60 80'
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
- name: Upload artifacts
uses: actions/upload-artifact@v3
if: always()
with:
name: websocket_coverage_report
path: |
${{ env.COMP_DIR }}/websocket_coverage_report
if-no-files-found: error
4 changes: 2 additions & 2 deletions .github/workflows/websocket__build-target-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "test" } ]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" } ]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
matrix:
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
idf_target: ["esp32"]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "test" } ]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" } ]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
Expand Down
11 changes: 10 additions & 1 deletion components/esp_websocket_client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
idf_build_get_property(target IDF_TARGET)

if(NOT CONFIG_WS_TRANSPORT AND NOT CMAKE_BUILD_EARLY_EXPANSION)
message(STATUS "Websocket transport is disabled so the esp_websocket_client component will not be built")
# note: the component is still included in the build so it can become visible again in config
Expand All @@ -6,7 +8,14 @@ if(NOT CONFIG_WS_TRANSPORT AND NOT CMAKE_BUILD_EARLY_EXPANSION)
return()
endif()

idf_component_register(SRCS "esp_websocket_client.c"
if(${IDF_TARGET} STREQUAL "linux")
idf_component_register(SRCS "esp_websocket_client.c"
INCLUDE_DIRS "include"
REQUIRES esp-tls tcp_transport http_parser esp_event nvs_flash esp_stubs
PRIV_REQUIRES esp_timer)
else()
idf_component_register(SRCS "esp_websocket_client.c"
INCLUDE_DIRS "include"
REQUIRES lwip esp-tls tcp_transport http_parser
PRIV_REQUIRES esp_timer esp_event)
endif()
7 changes: 7 additions & 0 deletions components/esp_websocket_client/esp_websocket_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
#include "esp_timer.h"
#include "esp_tls_crypto.h"

#if defined(CONFIG_IDF_TARGET_LINUX)
#include <arpa/inet.h>
#include <errno.h>
#define BIT0 0x00000001
#define BIT1 0x00000002
#endif

static const char *TAG = "websocket_client";

#define WEBSOCKET_TCP_DEFAULT_PORT (80)
Expand Down
15 changes: 15 additions & 0 deletions components/esp_websocket_client/tests/host_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.5)

set(COMPONENTS esp_websocket_client main)
set(common_component_dir ../../../../common_components)
set(EXTRA_COMPONENT_DIRS
../../..
"${common_component_dir}/linux_compat/esp_timer"
"${common_component_dir}/linux_compat"
"${common_component_dir}/linux_compat/freertos")

list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

project(websocket-host)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS
"."
REQUIRES esp_websocket_client protocol_examples_common)

if(CONFIG_GCOV_ENABLED)
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
target_link_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)

idf_component_get_property(esp_websocket_client esp_websocket_client COMPONENT_LIB)

target_compile_options(${esp_websocket_client} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
target_link_options(${esp_websocket_client} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
menu "Host-test config"

config GCOV_ENABLED
bool "Coverage analyzer"
default n
help
Enables coverage analyzing for host tests.

config WEBSOCKET_URI
string "Websocket endpoint URI"
default "ws://echo.websocket.events"
help
URL of websocket endpoint this example connects to and sends echo

endmenu
122 changes: 122 additions & 0 deletions components/esp_websocket_client/tests/host_test/main/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "esp_log.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_websocket_client.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"

static const char *TAG = "websocket";

static void log_error_if_nonzero(const char *message, int error_code)
{
if (error_code != 0) {
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
}
}

static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
}
break;
case WEBSOCKET_EVENT_DATA:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
ESP_LOGI(TAG, "Received opcode=%d", data->op_code);
if (data->op_code == 0x08 && data->data_len == 2) {
ESP_LOGW(TAG, "Received closed message with code=%d", 256 * data->data_ptr[0] + data->data_ptr[1]);
} else {
ESP_LOGW(TAG, "Received=%.*s", data->data_len, (char *)data->data_ptr);
}

// If received data contains json structure it succeed to parse
ESP_LOGW(TAG, "Total payload length=%d, data_len=%d, current payload offset=%d\r\n", data->payload_len, data->data_len, data->payload_offset);

break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
}
break;
}
}


static void websocket_app_start(void)
{
esp_websocket_client_config_t websocket_cfg = {};

websocket_cfg.uri = CONFIG_WEBSOCKET_URI;

ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri);

esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client);

esp_websocket_client_start(client);
char data[32];
int i = 0;
while (i < 1) {
if (esp_websocket_client_is_connected(client)) {
int len = sprintf(data, "hello %04d", i++);
ESP_LOGI(TAG, "Sending %s", data);
esp_websocket_client_send_text(client, data, len, portMAX_DELAY);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}

esp_websocket_client_destroy(client);
}

int main(void)
{

ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("websocket_client", ESP_LOG_DEBUG);
esp_log_level_set("transport_ws", ESP_LOG_DEBUG);
esp_log_level_set("trans_tcp", ESP_LOG_DEBUG);

ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());

/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());

websocket_app_start();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_GCOV_ENABLED=y
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="linux"
CONFIG_IDF_TARGET_LINUX=y
CONFIG_ESP_EVENT_POST_FROM_ISR=n
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n

0 comments on commit eed36e3

Please sign in to comment.