warmcat / libwebsockets

canonical libwebsockets.org networking library
https://libwebsockets.org
Other
4.81k stars 1.49k forks source link

Cannot access Coinex via WebSocket on Windows #3173

Closed quejp closed 5 months ago

quejp commented 5 months ago

Hello owner.

When I run the test code below, I cannot connect to the WebSocket server. If it is possible to avoid this, could you please tell me how to do so? The operating system is Windows 11. The version of Visual Studio used is toolset 2022 v143. The version of libwebsockets used is v4.3.3. The version of OpenSSL used is v3.3.0.

ERROR LOG [2024/06/30 19:29:33:0018] N: lws_create_context: LWS: 4.3.3-unknown, NET CLI SRV H1 H2 WS ConMon IPv6-absent [2024/06/30 19:29:33:0053] N: lws_lc_tag: ++ [wsi|0|pipe] (1) [2024/06/30 19:29:33:0104] N: lws_lc_tag: ++ [vh|0|default||-1] (1) [2024/06/30 19:29:33:0212] N: lws_plat_vhost_tls_client_ctx_init: Imported 58 certs from plat store LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: LWS_CALLBACK_PROTOCOL_INIT: LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: [2024/06/30 19:29:33:0222] N: __lws_lc_tag: ++ [wsicli|0|WS/h1/default/socket.coinex.com] (1) [2024/06/30 19:29:33:0247] W: lws_plat_set_socket_options_ip: not implemented on windows platform LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: 00000000000002AC LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: LWS_CALLBACK_GET_THREAD_ID: LWS_CALLBACK_WSI_CREATE: [2024/06/30 19:29:33:0385] N: lws_gate_accepts: on = 0 LWS_CALLBACK_EVENT_WAIT_CANCELLED: wsi 000001C06F9578C8 user 0000000000000000 in 0000000000000000 len 0 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000001C071E12A10 in 000001C0719296B0 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000001C071E12A10 in 000001C0719296B0 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000001C071E12A10 in 000001C0719296B0 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000001C071E12A10 in 000001C0719296B0 len 1 [2024/06/30 19:29:33:0512] N: lws_gate_accepts: on = 0 LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: in 00000076F6FAE400 len 3804 LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: [2024/06/30 19:29:33:0783] W: [wsicli|0|WS/h1/default/socket.coinex.com]: lws_client_ws_upgrade: got bad HTTP response '502' LWS_CALLBACK_CLIENT_CONNECTION_ERROR: HS: ws upgrade response not 101 [2024/06/30 19:29:33:0783] N: lws_gate_accepts: on = 0 LWS_CALLBACK_WSI_DESTROY: [2024/06/30 19:29:33:0783] N: lws_lc_untag: -- [wsicli|0|WS/h1/default/socket.coinex.com] (0) 56.091ms [2024/06/30 19:29:33:0783] N: lws_lc_untag: -- [wsi|0|pipe] (0) 72.944ms Unknown callback event: 28 [2024/06/30 19:29:33:0788] N: __lws_lc_untag: -- [vh|0|default||-1] (0) 68.341ms

TEST CODE


#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include <libwebsockets.h>

// LIBWEBSOCKETS
#pragma comment(lib, "websockets.lib")
// OPEN SSL
#pragma comment(lib, "libcrypto.lib")

#define STD_OFF                             0
#define STD_ON                              1

#define DEBUG_DETAIL_INFO_MODE              STD_ON
#define DEBUG_INFO_MODE                     STD_ON

static int CoinexCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len);

typedef struct _WEBSOCKET_THREAD
{
    struct lws *pWsi;
    bool bConnected;
    bool bReConnect;
    uint32_t ulReConnectCount;
    uint8_t ucURI[2048];
    struct lws_context_creation_info stLwsInfo;
    struct lws_client_connect_info stLwsConInfo;
    struct lws_context *pContext;
    const uint8_t *pProtocol;
    const uint8_t *pPath;
    bool bTxOngoing;
} WEBSOCKET_CONTEXT;

struct lws_protocols g_WsProtocols[] =
{
    {
        "COINEX protocol",
        CoinexCallback,
        0,
        0,
        1,
        NULL,
        0
    },

    LWS_PROTOCOL_LIST_TERM
};

static int CoinexCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len)
{
    WEBSOCKET_CONTEXT *pWS;
    int32_t IsFinal;

    switch (reason)
    {
    case LWS_CALLBACK_ESTABLISHED:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_ESTABLISHED: %s established\n", __func__);
#endif

        break;

    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_CONNECTION_ERROR: %s\n", in ? (char*)in : "(null)");
#endif

        break;

    case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH: \n");
#endif

        break;

    case LWS_CALLBACK_CLIENT_ESTABLISHED:

#if (DEBUG_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_ESTABLISHED: \n");
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));
        pWS->bConnected = true;

        break;

    case LWS_CALLBACK_CLIENT_RECEIVE:

        IsFinal = lws_is_final_fragment(wsi);

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_RECEIVE: %s len %lld IsFinal %d\n", (const char*)in, len, IsFinal);
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));

        break;

    case LWS_CALLBACK_CLIENT_RECEIVE_PONG:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_RECEIVE_PONG: %s\n", (const char*)in);
#endif

        break;

    case LWS_CALLBACK_CLIENT_WRITEABLE:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_WRITEABLE: wsi %p user %p in %p len %lld\n", wsi, user, in, len);
#endif

        pWS = (WEBSOCKET_CONTEXT*)lws_context_user(lws_get_context(wsi));

        break;

    case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: \n");
#endif

        break;

    case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: \n");
#endif

        break;

    case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: in %p len %lld\n", in, len);
#endif

        break;

    case LWS_CALLBACK_PROTOCOL_INIT:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_PROTOCOL_INIT: \n");
#endif

        break;

    case LWS_CALLBACK_WSI_CREATE:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_WSI_CREATE: \n");
#endif

        break;

    case LWS_CALLBACK_WSI_DESTROY:

#if (DEBUG_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_WSI_DESTROY: \n");
#endif
        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));
        pWS->bReConnect = true;

        break;

    case LWS_CALLBACK_GET_THREAD_ID:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_GET_THREAD_ID: \n");
#endif

        break;

    case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: \n");
#endif

        break;

    case LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user %p in %p len %lld\n", user, in, len);
#endif

        break;

    case LWS_CALLBACK_EVENT_WAIT_CANCELLED:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_EVENT_WAIT_CANCELLED: wsi %p user %p in %p len %lld\n", wsi, user, in, len);
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));

        break;

    case LWS_CALLBACK_VHOST_CERT_AGING:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_VHOST_CERT_AGING: %p\n", in);
#endif

        break;

    case LWS_CALLBACK_CLIENT_CLOSED:

#if (DEBUG_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_CLOSED: \n");
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));
        pWS->bConnected = false;

        break;

    case LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: \n");
#endif

        break;

    case LWS_CALLBACK_CONNECTING:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: %p\n", in);
#endif

        break;

    default:

        printf("Unknown callback event: %d\n", reason);
        break;
    }

    return 0;
}

int main(void)
{
    WEBSOCKET_CONTEXT ws;
    int32_t iResult;

    memset(&ws, 0, sizeof(ws));

    sprintf(&ws.ucURI[0], "wss://socket.coinex.com/v2/spot");

    lws_parse_uri(&ws.ucURI[0], &ws.pProtocol, &ws.stLwsConInfo.address, &ws.stLwsConInfo.port, &ws.pPath);

    ws.stLwsInfo.port = CONTEXT_PORT_NO_LISTEN;
    ws.stLwsInfo.protocols = &g_WsProtocols[0];
    ws.stLwsInfo.timeout_secs = 10;
    ws.stLwsInfo.connect_timeout_secs = 30;
    ws.stLwsInfo.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
    ws.stLwsInfo.gid = -1;
    ws.stLwsInfo.uid = -1;
    ws.stLwsInfo.user = (void *)&ws;
    ws.pContext = lws_create_context(&ws.stLwsInfo);
    if (ws.pContext == NULL)
    {
        goto ERROR_HANDLER;
    }

    ws.stLwsConInfo.context = ws.pContext;
    ws.stLwsConInfo.path = ws.pPath;
    ws.stLwsConInfo.host = ws.stLwsConInfo.address;
    ws.stLwsConInfo.origin = ws.stLwsConInfo.address;
    if (strcmp(ws.pProtocol, "http") == 0 || strcmp(ws.pProtocol, "ws") == 0)
    {
        ws.stLwsConInfo.ssl_connection = 0;
    }
    else if (strcmp(ws.pProtocol, "https") == 0 || strcmp(ws.pProtocol, "wss") == 0)
    {
        ws.stLwsConInfo.ssl_connection = LCCSCF_USE_SSL;
    }
    ws.stLwsConInfo.protocol = g_WsProtocols[0].name;
    ws.stLwsConInfo.pwsi = &ws.pWsi;

    lws_client_connect_via_info(&ws.stLwsConInfo);

    while (1)
    {
        iResult = lws_service(ws.pContext, 0);
        if (iResult < 0 || ws.pContext == NULL)
        {
            break;
        }
        if (ws.bReConnect == true)
        {
            ws.bReConnect = false;
            // Avoid infinite reconnect loop
            //lws_client_connect_via_info(&ws.stLwsConInfo);
            goto ERROR_HANDLER;
        }
    }

ERROR_HANDLER:
    if (ws.pContext != NULL)
    {
        lws_context_destroy(ws.pContext);
        ws.pContext = NULL;
    }

    return 0;
}
lws-team commented 5 months ago

If you set the cmake options LWS_TLS_LOG_PLAINTEXT_RX=1 and LWS_TLS_LOG_PLAINTEXT_TX=1, you can see exactly what you're sending and receiving.

502 means the remote server didn't like something, such as requiring auth headers or somesuch (it's up to them to decide what they like or not). You might get some extra information from the returned 502 body.

quejp commented 5 months ago

Thank you for your advice. I will try it.

quejp commented 5 months ago

@lws-team I changed the cmake build options and ran it. I tried to analyze it in my own way, but since I don't have much knowledge of HTTP headers or the WebSocket protocol, I couldn't figure out what fundamentally was causing the server to reject it. From this message exchange, do you have any ideas as to what the cause might be?

Tx Message from My Program to coinex server -- lws_ssl_capable_write -- GET /v2/spot HTTP/1.1 Pragma: no-cache Cache-Control: no-cache Host: socket.coinex.com Origin: https://socket.coinex.com Upgrade: websocket Connection: Upgrade..Sec-WebSocket-Key: KSO+hOFs1q5SkEnx8bvp6w== Sec-WebSocket-Protocol: COINEX protocol Sec-WebSocket-Version: 13....

Rx Message from coinex server to My Program -- lws_ssl_capable_read -- HTTP/1.1 502 Bad Gateway Date:Mon, 01 Jul 2024 03:43:15 GMT Content-Type: text/plain; charset=UTF-8 Content-Length: 15..Connection: keep-alive X-Frame-Options: SAMEORIGIN Referrer-Policy: same-origin..Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Expires: Thu,01 Jan 1970 00:00:01 GMT Server: cloudflare CF-RAY: 89c3640b6f3bafca-NRT error code: 502

ALL LOG [2024/07/01 12:43:15:0363] N: lws_create_context: LWS: 4.3.3-unknown, NET CLI SRV H1 H2 WS ConMon IPv6-absent [2024/07/01 12:43:15:0394] N: lws_lc_tag: ++ [wsi|0|pipe] (1) [2024/07/01 12:43:15:0470] N: lws_lc_tag: ++ [vh|0|default||-1] (1) [2024/07/01 12:43:15:0583] N: lws_plat_vhost_tls_client_ctx_init: Imported 58 certs from plat store LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: LWS_CALLBACK_PROTOCOL_INIT: LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: [2024/07/01 12:43:15:0588] N: __lws_lc_tag: ++ [wsicli|0|WS/h1/default/socket.coinex.com] (1) [2024/07/01 12:43:15:0776] W: lws_plat_set_socket_options_ip: not implemented on windows platform LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: 00000000000002A4 LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: LWS_CALLBACK_GET_THREAD_ID: LWS_CALLBACK_WSI_CREATE: [2024/07/01 12:43:15:0919] N: lws_gate_accepts: on = 0 LWS_CALLBACK_EVENT_WAIT_CANCELLED: wsi 00000244EBB98E58 user 0000000000000000 in 0000000000000000 len 0 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 00000244EDE673F0 in 00000244EDE1E1A0 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 00000244EDE673F0 in 00000244EDE1E1A0 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 00000244EDE673F0 in 00000244EDE1E1A0 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 00000244EDE673F0 in 00000244EDE1E1A0 len 1 [2024/07/01 12:43:15:1031] N: lws_gate_accepts: on = 0 LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: in 000000B4EFAFE368 len 3804 [2024/07/01 12:43:15:1031] N: lws_ssl_capable_write: len 282 [2024/07/01 12:43:15:1031] N: [2024/07/01 12:43:15:1031] N: 0000: 47 45 54 20 2F 76 32 2F 73 70 6F 74 20 48 54 54 GET /v2/spot HTT [2024/07/01 12:43:15:1031] N: 0010: 50 2F 31 2E 31 0D 0A 50 72 61 67 6D 61 3A 20 6E P/1.1..Pragma: n [2024/07/01 12:43:15:1031] N: 0020: 6F 2D 63 61 63 68 65 0D 0A 43 61 63 68 65 2D 43 o-cache..Cache-C [2024/07/01 12:43:15:1031] N: 0030: 6F 6E 74 72 6F 6C 3A 20 6E 6F 2D 63 61 63 68 65 ontrol: no-cache [2024/07/01 12:43:15:1031] N: 0040: 0D 0A 48 6F 73 74 3A 20 73 6F 63 6B 65 74 2E 63 ..Host: socket.c [2024/07/01 12:43:15:1031] N: 0050: 6F 69 6E 65 78 2E 63 6F 6D 0D 0A 4F 72 69 67 69 oinex.com..Origi [2024/07/01 12:43:15:1031] N: 0060: 6E 3A 20 68 74 74 70 73 3A 2F 2F 73 6F 63 6B 65 n: https://socke [2024/07/01 12:43:15:1036] N: 0070: 74 2E 63 6F 69 6E 65 78 2E 63 6F 6D 0D 0A 55 70 t.coinex.com..Up [2024/07/01 12:43:15:1036] N: 0080: 67 72 61 64 65 3A 20 77 65 62 73 6F 63 6B 65 74 grade: websocket [2024/07/01 12:43:15:1036] N: 0090: 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 55 70 ..Connection: Up [2024/07/01 12:43:15:1036] N: 00A0: 67 72 61 64 65 0D 0A 53 65 63 2D 57 65 62 53 6F grade..Sec-WebSo [2024/07/01 12:43:15:1036] N: 00B0: 63 6B 65 74 2D 4B 65 79 3A 20 4B 53 4F 2B 68 4F cket-Key: KSO+hO [2024/07/01 12:43:15:1036] N: 00C0: 46 73 31 71 35 53 6B 45 6E 78 38 62 76 70 36 77 Fs1q5SkEnx8bvp6w [2024/07/01 12:43:15:1036] N: 00D0: 3D 3D 0D 0A 53 65 63 2D 57 65 62 53 6F 63 6B 65 ==..Sec-WebSocke [2024/07/01 12:43:15:1036] N: 00E0: 74 2D 50 72 6F 74 6F 63 6F 6C 3A 20 43 4F 49 4E t-Protocol: COIN [2024/07/01 12:43:15:1036] N: 00F0: 45 58 20 70 72 6F 74 6F 63 6F 6C 0D 0A 53 65 63 EX protocol..Sec [2024/07/01 12:43:15:1036] N: 0100: 2D 57 65 62 53 6F 63 6B 65 74 2D 56 65 72 73 69 -WebSocket-Versi [2024/07/01 12:43:15:1036] N: 0110: 6F 6E 3A 20 31 33 0D 0A 0D 0A on: 13.... [2024/07/01 12:43:15:1036] N: [2024/07/01 12:43:15:1306] N: lws_ssl_capable_read: len 413 [2024/07/01 12:43:15:1306] N: [2024/07/01 12:43:15:1306] N: 0000: 48 54 54 50 2F 31 2E 31 20 35 30 32 20 42 61 64 HTTP/1.1 502 Bad [2024/07/01 12:43:15:1306] N: 0010: 20 47 61 74 65 77 61 79 0D 0A 44 61 74 65 3A 20 Gateway..Date: [2024/07/01 12:43:15:1306] N: 0020: 4D 6F 6E 2C 20 30 31 20 4A 75 6C 20 32 30 32 34 Mon, 01 Jul 2024 [2024/07/01 12:43:15:1306] N: 0030: 20 30 33 3A 34 33 3A 31 35 20 47 4D 54 0D 0A 43 03:43:15 GMT..C [2024/07/01 12:43:15:1306] N: 0040: 6F 6E 74 65 6E 74 2D 54 79 70 65 3A 20 74 65 78 ontent-Type: tex [2024/07/01 12:43:15:1306] N: 0050: 74 2F 70 6C 61 69 6E 3B 20 63 68 61 72 73 65 74 t/plain; charset [2024/07/01 12:43:15:1306] N: 0060: 3D 55 54 46 2D 38 0D 0A 43 6F 6E 74 65 6E 74 2D =UTF-8..Content- [2024/07/01 12:43:15:1306] N: 0070: 4C 65 6E 67 74 68 3A 20 31 35 0D 0A 43 6F 6E 6E Length: 15..Conn [2024/07/01 12:43:15:1306] N: 0080: 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61 6C 69 ection: keep-ali [2024/07/01 12:43:15:1311] N: 0090: 76 65 0D 0A 58 2D 46 72 61 6D 65 2D 4F 70 74 69 ve..X-Frame-Opti [2024/07/01 12:43:15:1311] N: 00A0: 6F 6E 73 3A 20 53 41 4D 45 4F 52 49 47 49 4E 0D ons: SAMEORIGIN. [2024/07/01 12:43:15:1311] N: 00B0: 0A 52 65 66 65 72 72 65 72 2D 50 6F 6C 69 63 79 .Referrer-Policy [2024/07/01 12:43:15:1311] N: 00C0: 3A 20 73 61 6D 65 2D 6F 72 69 67 69 6E 0D 0A 43 : same-origin..C [2024/07/01 12:43:15:1311] N: 00D0: 61 63 68 65 2D 43 6F 6E 74 72 6F 6C 3A 20 70 72 ache-Control: pr [2024/07/01 12:43:15:1311] N: 00E0: 69 76 61 74 65 2C 20 6D 61 78 2D 61 67 65 3D 30 ivate, max-age=0 [2024/07/01 12:43:15:1311] N: 00F0: 2C 20 6E 6F 2D 73 74 6F 72 65 2C 20 6E 6F 2D 63 , no-store, no-c [2024/07/01 12:43:15:1311] N: 0100: 61 63 68 65 2C 20 6D 75 73 74 2D 72 65 76 61 6C ache, must-reval [2024/07/01 12:43:15:1311] N: 0110: 69 64 61 74 65 2C 20 70 6F 73 74 2D 63 68 65 63 idate, post-chec [2024/07/01 12:43:15:1311] N: 0120: 6B 3D 30 2C 20 70 72 65 2D 63 68 65 63 6B 3D 30 k=0, pre-check=0 [2024/07/01 12:43:15:1311] N: 0130: 0D 0A 45 78 70 69 72 65 73 3A 20 54 68 75 2C 20 ..Expires: Thu, [2024/07/01 12:43:15:1317] N: 0140: 30 31 20 4A 61 6E 20 31 39 37 30 20 30 30 3A 30 01 Jan 1970 00:0 [2024/07/01 12:43:15:1317] N: 0150: 30 3A 30 31 20 47 4D 54 0D 0A 53 65 72 76 65 72 0:01 GMT..Server [2024/07/01 12:43:15:1317] N: 0160: 3A 20 63 6C 6F 75 64 66 6C 61 72 65 0D 0A 43 46 : cloudflare..CF [2024/07/01 12:43:15:1317] N: 0170: 2D 52 41 59 3A 20 38 39 63 33 36 34 30 62 36 66 -RAY: 89c3640b6f [2024/07/01 12:43:15:1317] N: 0180: 33 62 61 66 63 61 2D 4E 52 54 0D 0A 0D 0A 65 72 3bafca-NRT....er [2024/07/01 12:43:15:1317] N: 0190: 72 6F 72 20 63 6F 64 65 3A 20 35 30 32 ror code: 502 [2024/07/01 12:43:15:1317] N: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: [2024/07/01 12:43:15:1317] W: [wsicli|0|WS/h1/default/socket.coinex.com]: lws_client_ws_upgrade: got bad HTTP response '502' LWS_CALLBACK_CLIENT_CONNECTION_ERROR: HS: ws upgrade response not 101 [2024/07/01 12:43:15:1322] N: lws_gate_accepts: on = 0 LWS_CALLBACK_WSI_DESTROY: [2024/07/01 12:43:15:1322] N: lws_lc_untag: -- [wsicli|0|WS/h1/default/socket.coinex.com] (0) 73.399ms [2024/07/01 12:43:15:1322] N: lws_lc_untag: -- [wsi|0|pipe] (0) 92.777ms Unknown callback event: 28 [2024/07/01 12:43:15:1327] N: __lws_lc_untag: -- [vh|0|default||-1] (0) 85.634ms

lws-team commented 5 months ago

The remote server can set whatever rules it likes for liking what you send. Usually, some kind of auth, eg with an "api key" in what you send is going to be required. A 30s google shows https://docs.coinex.com/api/v2/authorization

quejp commented 5 months ago

I found the cause. In conclusion, it seems that the following header information was the cause. Their server does not recognize the Sec-WebSocket-Protocol item, and it seems to be rejected if this information is included. "Sec-WebSocket-Protocol: COINEX protocol"

Therefore, I modified the code below and the connection was successful. ws.stLwsConInfo.protocol = NULL;// g_WsProtocols[0].name;

For anyone having the same problem, I will provide the steps I took to debug it.

Can connect using Node.js WebSocket. Therefore, I decided to monitor the http header when connecting with Node.js.

Node.js websocket http header request headers: { 'Sec-WebSocket-Version': 13, 'Sec-WebSocket-Key': 'g4q5OTKmU+ql6pg5semqKQ==', Connection: 'Upgrade', Upgrade: 'websocket', 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits' },

DEBUG test code on node.js

const WebSocket = require('ws');

const socketUrl = 'wss://socket.coinex.com/v2/spot';

// Dump http header on Node.js websocket
function logHttpRequest(options, callback) {
    const originalRequest = (options.protocol === 'https:' ? require('https') : require('http')).request;

    const req = originalRequest(options, (res) => {
        console.log('Status Code:', res.statusCode);
        console.log('Headers:', res.headers);

        res.on('data', (chunk) => {
            console.log('Response Body:', chunk.toString());
        });

        res.on('end', () => {
            console.log('No more data in response.');
        });

        if (callback) {
            callback(res);
        }
    });

    req.on('error', (e) => {
        console.error(`Request error: ${e.message}`);
    });

    return req;
}

const ws = new WebSocket(socketUrl, {
    createConnection: (options) => {
        console.log('Request Options:', options);
        return logHttpRequest(options);
    }
});

ws.on('open', () => {
    console.log('Connected to the WebSocket server');

});

ws.on('message', (data) => {
    const message = JSON.parse(data);
    console.log('Received message:', message);
});

ws.on('close', () => {
    console.log('Disconnected from the WebSocket server');
});

ws.on('error', (error) => {
    console.error('WebSocket error:', error);
});

FIXED code my program side with libwebsockets

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include <libwebsockets.h>

// LIBWEBSOCKETS
#pragma comment(lib, "websockets.lib")
// OPEN SSL
#pragma comment(lib, "libcrypto.lib")

#define STD_OFF                             0
#define STD_ON                              1

#define DEBUG_DETAIL_INFO_MODE              STD_ON
#define DEBUG_INFO_MODE                     STD_ON

static int CoinexCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len);

typedef struct _WEBSOCKET_THREAD
{
    struct lws *pWsi;
    bool bConnected;
    bool bReConnect;
    uint32_t ulReConnectCount;
    uint8_t ucURI[2048];
    struct lws_context_creation_info stLwsInfo;
    struct lws_client_connect_info stLwsConInfo;
    struct lws_context *pContext;
    const uint8_t *pProtocol;
    const uint8_t *pPath;
    bool bTxOngoing;
} WEBSOCKET_CONTEXT;

struct lws_protocols g_WsProtocols[] =
{
    {
        "COINEX protocol",
        CoinexCallback,
        0,
        0,
        1,
        NULL,
        0
    },

    LWS_PROTOCOL_LIST_TERM
};

static int CoinexCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len)
{
    WEBSOCKET_CONTEXT *pWS;
    int32_t IsFinal;

    switch (reason)
    {
    case LWS_CALLBACK_ESTABLISHED:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_ESTABLISHED: %s established\n", __func__);
#endif

        break;

    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_CONNECTION_ERROR: %s\n", in ? (char*)in : "(null)");
#endif

        break;

    case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH: \n");
#endif

        break;

    case LWS_CALLBACK_CLIENT_ESTABLISHED:

#if (DEBUG_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_ESTABLISHED: \n");
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));
        pWS->bConnected = true;

        break;

    case LWS_CALLBACK_CLIENT_RECEIVE:

        IsFinal = lws_is_final_fragment(wsi);

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_RECEIVE: %s len %lld IsFinal %d\n", (const char*)in, len, IsFinal);
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));

        break;

    case LWS_CALLBACK_CLIENT_RECEIVE_PONG:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_RECEIVE_PONG: %s\n", (const char*)in);
#endif

        break;

    case LWS_CALLBACK_CLIENT_WRITEABLE:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_WRITEABLE: wsi %p user %p in %p len %lld\n", wsi, user, in, len);
#endif

        pWS = (WEBSOCKET_CONTEXT*)lws_context_user(lws_get_context(wsi));

        break;

    case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: \n");
#endif

        break;

    case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: \n");
#endif

        break;

    case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: in %p len %lld\n", in, len);
#endif

        //unsigned char** p = (unsigned char**)in;
        //unsigned char* end = (*p) + len;
        //const char* user_agent = "permessage-deflate; client_max_window_bits";

        //if (lws_add_http_header_by_name(wsi, (unsigned char*)"Sec-WebSocket-Extensions:", (unsigned char*)user_agent, strlen(user_agent), p, end)) {
        //    return -1;
        //}

        break;

    case LWS_CALLBACK_PROTOCOL_INIT:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_PROTOCOL_INIT: \n");
#endif

        break;

    case LWS_CALLBACK_WSI_CREATE:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_WSI_CREATE: \n");
#endif

        break;

    case LWS_CALLBACK_WSI_DESTROY:

#if (DEBUG_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_WSI_DESTROY: \n");
#endif
        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));
        pWS->bReConnect = true;

        break;

    case LWS_CALLBACK_GET_THREAD_ID:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_GET_THREAD_ID: \n");
#endif

        break;

    case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: \n");
#endif

        break;

    case LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user %p in %p len %lld\n", user, in, len);
#endif

        break;

    case LWS_CALLBACK_EVENT_WAIT_CANCELLED:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_EVENT_WAIT_CANCELLED: wsi %p user %p in %p len %lld\n", wsi, user, in, len);
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));

        break;

    case LWS_CALLBACK_VHOST_CERT_AGING:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_VHOST_CERT_AGING: %p\n", in);
#endif

        break;

    case LWS_CALLBACK_CLIENT_CLOSED:

#if (DEBUG_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_CLOSED: \n");
#endif

        pWS = (WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi));
        pWS->bConnected = false;

        break;

    case LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: \n");
#endif

        break;

    case LWS_CALLBACK_CONNECTING:

#if (DEBUG_DETAIL_INFO_MODE == STD_ON)
        printf("LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: %p\n", in);
#endif

        break;

    default:

        printf("Unknown callback event: %d\n", reason);
        break;
    }

    return 0;
}

int main(void)
{
    WEBSOCKET_CONTEXT ws;
    int32_t iResult;

    memset(&ws, 0, sizeof(ws));

    sprintf(&ws.ucURI[0], "wss://socket.coinex.com/v2/spot");

    lws_parse_uri(&ws.ucURI[0], &ws.pProtocol, &ws.stLwsConInfo.address, &ws.stLwsConInfo.port, &ws.pPath);

    ws.stLwsInfo.port = CONTEXT_PORT_NO_LISTEN;
    ws.stLwsInfo.protocols = &g_WsProtocols[0];
    ws.stLwsInfo.timeout_secs = 10;
    ws.stLwsInfo.connect_timeout_secs = 30;
    ws.stLwsInfo.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
    ws.stLwsInfo.gid = -1;
    ws.stLwsInfo.uid = -1;
    ws.stLwsInfo.user = (void *)&ws;
    ws.pContext = lws_create_context(&ws.stLwsInfo);
    if (ws.pContext == NULL)
    {
        goto ERROR_HANDLER;
    }

    ws.stLwsConInfo.context = ws.pContext;
    ws.stLwsConInfo.path = ws.pPath;
    ws.stLwsConInfo.host = ws.stLwsConInfo.address;
    ws.stLwsConInfo.origin = "chrome";// ws.stLwsConInfo.address;
    if (strcmp(ws.pProtocol, "http") == 0 || strcmp(ws.pProtocol, "ws") == 0)
    {
        ws.stLwsConInfo.ssl_connection = 0;
    }
    else if (strcmp(ws.pProtocol, "https") == 0 || strcmp(ws.pProtocol, "wss") == 0)
    {
        ws.stLwsConInfo.ssl_connection = LCCSCF_USE_SSL;
    }
    ws.stLwsConInfo.protocol = NULL;// g_WsProtocols[0].name;
    ws.stLwsConInfo.pwsi = &ws.pWsi;

    lws_client_connect_via_info(&ws.stLwsConInfo);

    while (1)
    {
        iResult = lws_service(ws.pContext, 0);
        if (iResult < 0 || ws.pContext == NULL)
        {
            break;
        }
        if (ws.bReConnect == true)
        {
            ws.bReConnect = false;
            // Avoid infinite reconnect loop
            //lws_client_connect_via_info(&ws.stLwsConInfo);
            goto ERROR_HANDLER;
        }
    }

ERROR_HANDLER:
    if (ws.pContext != NULL)
    {
        lws_context_destroy(ws.pContext);
        ws.pContext = NULL;
    }

    return 0;
}

SUCCESS on libwebsockets [2024/07/01 13:40:47:4729] N: lws_create_context: LWS: 4.3.3-unknown, NET CLI SRV H1 H2 WS ConMon IPv6-absent [2024/07/01 13:40:47:4760] N: lws_lc_tag: ++ [wsi|0|pipe] (1) [2024/07/01 13:40:47:4852] N: lws_lc_tag: ++ [vh|0|default||-1] (1) [2024/07/01 13:40:47:4981] N: lws_plat_vhost_tls_client_ctx_init: Imported 58 certs from plat store LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: LWS_CALLBACK_PROTOCOL_INIT: [2024/07/01 13:40:47:4986] N: __lws_lc_tag: ++ [wsicli|0|WS/h1/default/socket.coinex.com] (1) [2024/07/01 13:40:47:5022] W: lws_plat_set_socket_options_ip: not implemented on windows platform LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL: 00000000000002CC LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: LWS_CALLBACK_GET_THREAD_ID: LWS_CALLBACK_WSI_CREATE: [2024/07/01 13:40:47:5166] N: lws_gate_accepts: on = 0 LWS_CALLBACK_EVENT_WAIT_CANCELLED: wsi 000002B30FF76E18 user 0000000000000000 in 0000000000000000 len 0 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000002B3122BEE40 in 000002B311DD9F00 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000002B3122BEE40 in 000002B311DD9F00 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000002B3122BEE40 in 000002B311DD9F00 len 1 LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: user 000002B3122BEE40 in 000002B311DD9F00 len 1 [2024/07/01 13:40:47:5283] N: lws_gate_accepts: on = 0 LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: in 000000E02FCFE418 len 3856 [2024/07/01 13:40:47:5283] N: lws_ssl_capable_write: len 230 [2024/07/01 13:40:47:5283] N: [2024/07/01 13:40:47:5283] N: 0000: 47 45 54 20 2F 76 32 2F 73 70 6F 74 20 48 54 54 GET /v2/spot HTT [2024/07/01 13:40:47:5283] N: 0010: 50 2F 31 2E 31 0D 0A 50 72 61 67 6D 61 3A 20 6E P/1.1..Pragma: n [2024/07/01 13:40:47:5283] N: 0020: 6F 2D 63 61 63 68 65 0D 0A 43 61 63 68 65 2D 43 o-cache..Cache-C [2024/07/01 13:40:47:5283] N: 0030: 6F 6E 74 72 6F 6C 3A 20 6E 6F 2D 63 61 63 68 65 ontrol: no-cache [2024/07/01 13:40:47:5288] N: 0040: 0D 0A 48 6F 73 74 3A 20 73 6F 63 6B 65 74 2E 63 ..Host: socket.c [2024/07/01 13:40:47:5288] N: 0050: 6F 69 6E 65 78 2E 63 6F 6D 0D 0A 4F 72 69 67 69 oinex.com..Origi [2024/07/01 13:40:47:5288] N: 0060: 6E 3A 20 68 74 74 70 73 3A 2F 2F 63 68 72 6F 6D n: https://chrom [2024/07/01 13:40:47:5288] N: 0070: 65 0D 0A 55 70 67 72 61 64 65 3A 20 77 65 62 73 e..Upgrade: webs [2024/07/01 13:40:47:5288] N: 0080: 6F 63 6B 65 74 0D 0A 43 6F 6E 6E 65 63 74 69 6F ocket..Connectio [2024/07/01 13:40:47:5288] N: 0090: 6E 3A 20 55 70 67 72 61 64 65 0D 0A 53 65 63 2D n: Upgrade..Sec- [2024/07/01 13:40:47:5288] N: 00A0: 57 65 62 53 6F 63 6B 65 74 2D 4B 65 79 3A 20 4B WebSocket-Key: K [2024/07/01 13:40:47:5288] N: 00B0: 53 4F 2B 68 4F 46 73 31 71 35 53 6B 45 6E 78 38 SO+hOFs1q5SkEnx8 [2024/07/01 13:40:47:5288] N: 00C0: 62 76 70 36 77 3D 3D 0D 0A 53 65 63 2D 57 65 62 bvp6w==..Sec-Web [2024/07/01 13:40:47:5288] N: 00D0: 53 6F 63 6B 65 74 2D 56 65 72 73 69 6F 6E 3A 20 Socket-Version: [2024/07/01 13:40:47:5288] N: 00E0: 31 33 0D 0A 0D 0A 13.... [2024/07/01 13:40:47:5288] N: [2024/07/01 13:40:47:5636] N: lws_ssl_capable_read: len 261 [2024/07/01 13:40:47:5636] N: [2024/07/01 13:40:47:5636] N: 0000: 48 54 54 50 2F 31 2E 31 20 31 30 31 20 53 77 69 HTTP/1.1 101 Swi [2024/07/01 13:40:47:5641] N: 0010: 74 63 68 69 6E 67 20 50 72 6F 74 6F 63 6F 6C 73 tching Protocols [2024/07/01 13:40:47:5641] N: 0020: 0D 0A 44 61 74 65 3A 20 4D 6F 6E 2C 20 30 31 20 ..Date: Mon, 01 [2024/07/01 13:40:47:5641] N: 0030: 4A 75 6C 20 32 30 32 34 20 30 34 3A 34 30 3A 34 Jul 2024 04:40:4 [2024/07/01 13:40:47:5641] N: 0040: 38 20 47 4D 54 0D 0A 43 6F 6E 74 65 6E 74 2D 4C 8 GMT..Content-L [2024/07/01 13:40:47:5641] N: 0050: 65 6E 67 74 68 3A 20 30 0D 0A 43 6F 6E 6E 65 63 ength: 0..Connec [2024/07/01 13:40:47:5641] N: 0060: 74 69 6F 6E 3A 20 75 70 67 72 61 64 65 0D 0A 53 tion: upgrade..S [2024/07/01 13:40:47:5641] N: 0070: 65 63 2D 57 65 62 53 6F 63 6B 65 74 2D 41 63 63 ec-WebSocket-Acc [2024/07/01 13:40:47:5641] N: 0080: 65 70 74 3A 20 47 2F 63 45 74 34 48 74 73 59 45 ept: G/cEt4HtsYE [2024/07/01 13:40:47:5641] N: 0090: 6E 50 30 4D 6E 53 56 6B 4B 52 6B 34 35 39 67 4D nP0MnSVkKRk459gM [2024/07/01 13:40:47:5641] N: 00A0: 3D 0D 0A 55 70 67 72 61 64 65 3A 20 77 65 62 73 =..Upgrade: webs [2024/07/01 13:40:47:5641] N: 00B0: 6F 63 6B 65 74 0D 0A 43 46 2D 43 61 63 68 65 2D ocket..CF-Cache- [2024/07/01 13:40:47:5641] N: 00C0: 53 74 61 74 75 73 3A 20 44 59 4E 41 4D 49 43 0D Status: DYNAMIC. [2024/07/01 13:40:47:5647] N: 00D0: 0A 53 65 72 76 65 72 3A 20 63 6C 6F 75 64 66 6C .Server: cloudfl [2024/07/01 13:40:47:5647] N: 00E0: 61 72 65 0D 0A 43 46 2D 52 41 59 3A 20 38 39 63 are..CF-RAY: 89c [2024/07/01 13:40:47:5647] N: 00F0: 33 62 38 35 35 31 65 38 63 61 66 36 34 2D 4E 52 3b8551e8caf64-NR [2024/07/01 13:40:47:5647] N: 0100: 54 0D 0A 0D 0A T.... [2024/07/01 13:40:47:5647] N: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH: rops_issue_keepalive_ws isvalid 1 LWS_CALLBACK_CLIENT_ESTABLISHED:

quejp commented 5 months ago

@lws-team Thank you for some of the advice. It helped us identify and resolve the issue early on.

The ticket can be closed so I will close it.