rakucro

Cro::WebSocket::Client doesn't work


Created a websocket server with "cro sub".

Wrote this client:

use v6;
use Cro::WebSocket::Client;

constant WS-PORT = '20000';
constant WS-ADDRESS = 'localhost';
constant WS-PATH = 'chat';
constant WS-URL = 'ws://' ~ WS-ADDRESS ~ ':' ~ WS-PORT ~ '/' ~ WS-PATH;
constant TIMEOUT-TO-CONNECT = 5; # seconds

my $timeout;
my $connection-attempt;

await Promise.anyof(
  $connection-attempt = Cro::WebSocket::Client.connect(WS-URL),
  $timeout = Promise.in(TIMEOUT-TO-CONNECT));

if $timeout.status == Kept
{
  say "* could not connect to server in ', TIMEOUT-TO-CONNECT, ' seconds";
  exit 1;
}

if $connection-attempt.status != Kept
{
  say "* error ", $connection-attempt.cause,
    " when trying to connect to server";
  exit 1;
}

my $connection = $connection-attempt.result;
my $peer = WS-ADDRESS ~ ':' ~ WS-PORT;
say '* connected with ', $peer;

my $counter = 0;

my $message-supplier = Supplier::Preserving.new;
my $has-message-to-send = $message-supplier.Supply;
$message-supplier.emit(1);

react
{
  whenever $has-message-to-send
  {
    $counter++;
    $connection.send($counter);
    say "* ok, sent message ", $counter, " to server"; 
  }

  whenever $connection.messages -> $reply
  {
    say '* received reply=[' ~ $reply ~ '] from server';
    $message-supplier.emit(1);
  }

} # react

I see with tcpdump the response code 101 (switching protocols) from the server, but I don't see the message sent from the client to the server.

So, what am I doing wrong ?

Another question, shoudn't "$connection.send" return a Promise or something ? What if there's an error when sending ?

And another question: it seems the server only understands IPV6 addresses...how to make it understand IPV4 addresses ?

That's it, for now.


UPDATE

As per Takao's advice, changing

$connection.send($counter)

to

$connection.send($counter.Str)

solves the problem (though I tried it on another program, not this one).


Solution

  • Let's resolve this piece by piece.

    Firstly, your code looks correct to me, except for a couple of tiny bits.

    When I reproduced your code, it indeed did not work, so I tried it with cro trace . instead of cro run .. You can find info about that mode in official docs. The alternative way is to just set CRO_TRACE=1 environment variable.

    So during debug I saw this error: [TRACE(anon 1)] Cro::HTTP::ResponseParser QUIT No applicable body serializer could be found for this message As it says, the body you sent could not be serialized. So I looked into what are you sending: $counter. $counter in your code is Int, so we need to make it Str before, doing simple $counter.Str makes your example work.

    Also, note that you are sending a message on every reply, and echo server (default one you created using cro stub) also sends a reply for every incoming message, so your example sends messages endlessly. To prevent that you may consider adding a condition under which you will no longer send things, but well, it is a test example anyway, so up to you.

    As for your other questions:

    Another question, shoudn't "$connection.send" return a Promise or something?

    It should not, I'll write out some cro's architecture details to explain it next. As you may know from reading docs, cro pipeline is basically just a bunch of Cro::Transform-wrapped supplies. Inside of Cro::Websocket::Client::Connection, send method just sends a thing directly into Cro::Source of the whole pipeline, you cannot go wrong with a simple $supplier.emit($message)(the real implementation of this method looks very close to this line). The thing you bumped into occurred further in the pipeline. I am sure it is not a nice user experience to hide exceptions of such cases, so I'll consider making a patch to propagate the exception, so it'd be easier to catch(although you always can use debug mode).

    it seems the server only understands IPV6 addresses...how to make it understand IPV4 addresses ?

    I am not sure about that, please open a new question.