emscripten icon indicating copy to clipboard operation
emscripten copied to clipboard

Can't connect to a websocket from Chrome.

Open denprog opened this issue 1 year ago • 10 comments

Version of emscripten/emsdk: emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.34 (57b21b8fdcbe3ebb523178b79465254668eab408) clang version 17.0.0 (https://github.com/llvm/llvm-project a031f72187ce495b9faa4ccf99b1e901a3872f4b) Target: wasm32-unknown-emscripten Thread model: posix

My emscripten app connects to a websocket in a C++ server. This server is based on boost beast, I've got their simple example:

void do_session(tcp::socket& socket)
{
    try
    {
        // Construct the stream by moving in the socket
        websocket::stream<tcp::socket> ws{std::move(socket)};

        ws.set_option(websocket::stream_base::timeout::suggested(beast::role_type::server));
        // Set a decorator to change the Server of the handshake
        ws.set_option(websocket::stream_base::decorator(
            [](websocket::response_type& res)
            {
                res.set(http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-server-sync");
            }));
        
        // Accept the websocket handshake
        ws.accept();
        //ws.accept(buffer.data());

        for (;;)
        {
            // This buffer will hold the incoming message
            beast::flat_buffer buffer;

            // Read a message
            ws.read(buffer);
        }
    }
    catch (beast::system_error const& se)
    {
        // This indicates that the session was closed
        if (se.code() != websocket::error::closed)
            std::cerr << "Error: " << se.code().message() << std::endl;
    }
    catch (std::exception const& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

int main(int argc, char *argv[])
{
    net::io_context ioc{1};
    tcp::resolver resolver(ioc);
    //tcp::acceptor acceptor{ioc, resolver.resolve("0.0.0.0", "8010")};
    tcp::acceptor acceptor{ioc, {net::ip::make_address("0.0.0.0"), 8010}};

    while (!service_context.exit)
    {
        tcp::socket socket{ioc};
        acceptor.accept(socket);
        logger->Info("Connection accepted");

        std::thread{std::bind(&do_session, std::move(socket))}.detach();
    }
}

In my emscripten app I use boost::asio sockets. Although, I far as I unserstand, it doesn't matter. This is the params of the client app:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_SDL=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_SDL_TTF=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_SDL_IMAGE=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_FREETYPE=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=4")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --preload-file ./fonts")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DISABLE_EXCEPTION_CATCHING")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WEBSOCKET_SUBPROTOCOL='text'")

So, it works nice in Firefox (connects well). Also, it connects using a test extension in Chrome. But it doesn't work when I run the client in Chrome. In Debug I see:

WebSocket connection to 'ws://127.0.0.1:8010/' failed: 

Stack trace points on createPeer(),

ws = new WebSocketConstructor(url, opts);
yutovo_web.worker.js:192 worker.js onmessage() captured an uncaught exception: boost::wrapexcept<boost::system::system_error>: write: Socket not connected [system:53]

Server exception: "End of file". Any suggestions?

denprog avatar Mar 30 '23 11:03 denprog

I've got a simple python server to test:

from simple_websocket_server import WebSocketServer, WebSocket


class SimpleEcho(WebSocket):
    def handle(self):
        # echo message back to client
        self.send_message(self.data)

    def connected(self):
        print(self.address, 'connected')

    def handle_close(self):
        print(self.address, 'closed')


server = WebSocketServer('', 8010, SimpleEcho)
server.serve_forever()

with the same result...

denprog avatar Mar 30 '23 12:03 denprog

What I see in Wireshark: In Firefox, where all is ok:

GET / HTTP/1.1
Host: 127.0.0.1:8010
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0
Accept: */*
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13
Origin: http://localhost:6931
Sec-WebSocket-Protocol: text
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: X3M/D9V4gnEJ/kgn7FndUA==
Connection: keep-alive, Upgrade
Sec-Fetch-Dest: websocket
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: cross-site
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: 8KmtW7aV6HAZkl8K4TlrvYAi5Sw=

..=].-i8{Y..Test

In Chrome, where the connection fails:

GET / HTTP/1.1
Host: 127.0.0.1:8010
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Upgrade: websocket
Origin: http://localhost:6931
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
Sec-WebSocket-Key: ds3grl+Ked2SanDzRl8F8A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: text

HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: oyK9uvxYMBBySj50gB3JzGedn8A=

The server wants to upgrade, but the client closes the connection...

denprog avatar Mar 30 '23 13:03 denprog

Hi,

I encountered the same issue, things working well on Firefox, but Chromium-based browsers are not opening the WebSocket.

Did you find a solution?

gabrik avatar Jul 04 '23 06:07 gabrik

I did it with web socket in JS. I call JS wrappers from C++, do send/receive, and return result into C++.

denprog avatar Jul 04 '23 06:07 denprog

Thank you, unfortunately that's not a solution we can apply so we'll continue to investigate.

gabrik avatar Jul 05 '23 06:07 gabrik

Any solution on this? I have the same issue, and it happens only when i'm using asio based WS server (websocketpp or beast). With another WS servers chrome works fine.

Is there workaround for this?

Honya2000 avatar Nov 27 '23 12:11 Honya2000

I think a secure WebSocket is needed, technically an NGINX configured to expose the WebSocket with a certificate it should work.

gabrik avatar Nov 27 '23 13:11 gabrik

What do you mean needed? For sure i'm using wss connection from chrome. And SSL handshake on the server was accepted. So the issue isn't SSL related.

Honya2000 avatar Nov 27 '23 14:11 Honya2000

Then disregard my comment, the issue I had was because of not using secure WebSocket

gabrik avatar Nov 27 '23 14:11 gabrik

I had what is possibly a similar problem, in that Firefox would connect to a secure web socket, but Chrome/Edge wouldn't. On Windows, the log also had:

WebSocket connection to 'wss://xyz/' failed: 

Which wasn't much help, as the error is missing. However, running Chromium on Linux gave an error:

WebSocket connection to 'wss://xyz/' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

I needed to modify the server (written using Qt), with a call to:

QWebSocketServer::setSupportedSubprotocols({"binary"})

srcejon avatar Apr 27 '24 16:04 srcejon

Thanks it really helped me, i was passing info in the Subprotocols field at the initialization of the WebSocket. I removed that and used this solution :

https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api

it work now on both Firefox and Chrome

cesarombredane avatar May 28 '24 13:05 cesarombredane