hoaproject / Websocket

The Hoa\Websocket library.
https://hoa-project.net/
423 stars 75 forks source link

How to broadcast events to websocket server with URL wss://xxx.xx #41

Closed shulard closed 9 years ago

shulard commented 9 years ago

Hello,

I'm currently trying to implement slack bot stuff using PHP. I found that there is an awesome Real Time Messaging API which using websockets (https://api.slack.com/rtm).

I tried to implement a method which can follow the given steps :

I found that Hoaproject as an awesome WebSocket client/server but I can't find a way to do that...

A websocket URL given by Slack API (they have a lifetime of 30sec): wss://ms177.slack-msgs.com/websocket/GEjhTU7tHl3C90SwJuKaF71Orb8F9HZgdv-HIwf4HxIMygveAW9xc2X_bZb_LtDXLVAapy7nTJ7qh9Oz0zYnyrr7Gn2kD4635oihxNh3ZTM=.

Here the code I used to do that :

<?php
require_once __DIR__.'/vendor/autoload.php';

$rtmStart = "https://slack.com/api/rtm.start?token=".$argv[1];
$content = json_decode(file_get_contents($rtmStart));

$client = new Hoa\Websocket\Client( new Hoa\Socket\Client($url) );
$client->connect();

$client->send('{
     "type": "message",
     "channel": "XXX",
     "user": "XXX",
     "text": "Hello world",
     "ts": "'.microtime(true).'"
}');

$client->close();

Thanks :)

Hywan commented 9 years ago

Hello :-),

Really good idea and interesting project! Thanks to bet on Hoa!

So, let's talk about the actor. Slack is the server and Hoa\Websocket is the client.

To connect to Slack, you read a wss:// URI from the Real Time Message API. Good. But Hoa\Socket expects a udp:// or tcp:// scheme because wss:// is too high-level :smile:. For WebSocket, we use TCP. So all you have to do is to extract the hostname or IP address from the wss:// URI and prefix it by tcp:// (here, you will have tcp://ms177.slack-msgs.com). The rest of the wss:// URI is the end-point. You need to pass this as an argument of the Hoa\Websocket\Client class (here /websocket/GEjhTU7tHl3C90SwJuKaF71Orb8F9HZgdv-HIwf4HxIMygveAW9xc2X_bZb_LtDXLVAapy7nTJ7qh9Oz0zYnyrr7Gn2kD4635oihxNh3ZTM=).

Oh, and since this is wss://, you will have to enable encryption on the Hoa\Socket\Client object.

To sum up:

$host   = 'ms177.slack-msgs.com';
$socket = new Hoa\Socket\Client('tcp://' . $host);
$socket->enableEncryption(true);

$endpoint  = '/websocket/GEjh…=';
$websocket = new Hoa\Websocket\Client($socket, $endpoint);
…

After that, the client is not responsible to broadcast the message, it is the responsability of the server. So you are safe on this one. However, what you are trying to do is to: Read new messages from Slack and send one message to Slack when your user sent one. So here, basically, you have 2 things to do at the same time: Waiting on the server and waiting on the client (user). The problem with PHP is that we cannot do 2 things at the same time because this is synchronous :wink:. You have several possibilities thought (group of socket connections, threads…), it will depend how your receive/read the user message. Can you help me?

Hywan commented 9 years ago

Maybe we should add a socket factory based on a ws:// or wss:// URI… I never thought it could be an issue but now I realize it can! Thoughts? If yes, please, open a new issue :smile:!

shulard commented 9 years ago

Thanks for your feedback, I think I need to learn a little more about socket here ;)

I've updated my code to cut the URL and give the right part to the right object.

<?php
require_once __DIR__.'/vendor/autoload.php';

$rtmStart = "https://slack.com/api/rtm.start?token=".$argv[1];
$content = json_decode(file_get_contents($rtmStart));
$url = parse_url($content->url);

$socket = new Hoa\Socket\Client(
  'tcp://'.$url['host'].(isset($url['port'])?':'.$url['port']:''), 
  5
);
$socket->enableEncryption(true);
$client = new Hoa\Websocket\Client( $socket, $url['path'] );

$client->connect();

The socket connection can't be established... I always received the error :

Fatal error: Uncaught Hoa\Socket\Client::_open(): (0) Client cannot join tcp://ms177.slack-msgs.com

Do you think it can be possible that Slack client is not compatible with the RFC "Rfc6455" ? I did not find any details in the documentation to help me resolve that issue... The connection works with JavaScript (I tried here: https://www.websocket.org/echo.html).

I think the factory can be a nice thing for that case. I think I'm going to make a PR for that, it'll help me go deeper in the implementation.

Hywan commented 9 years ago

If port is absent, use 80. What happen with this?

Good news for the PR! I already started one but I will be very glad to help you instead, it's better for you!

shulard commented 9 years ago

I've used 80 for the port I've got another error :

Fatal error: Uncaught Hoa\Socket\Client::_open(): (1) Client returns an error (number 60): Operation timed out while trying to join tcp://ms177.slack-msgs.com:80

I can't figure if Slack refuse the connection or if it's a network problem...

Hywan commented 9 years ago

What about 8080 (because of HTTPS)?hoaproject/Websocket wrote:I've used 80 for the port I've got another error :

Fatal error: Uncaught Hoa\Socket\Client::_open(): (1) Client returns an error (number 60): Operation timed out while trying to join tcp://ms177.slack-msgs.com:80

I can't figure if Slack refuse the connection or if it's a network problem...

—Reply to this email directly or view it on GitHub.

Sent Using Firefox OS

shulard commented 9 years ago

Same error with 8080...

I also tried with 443, which is given here as the default port for TLS communication... I receive an empty response...

When I passed 443, I received first an error about undefined host :

Fatal error: Uncaught Hoa\Websocket\Client::doHandshake(): (0) Host name is null. Please, use the Hoa\Websocket\Client::setHost() method.

If I use the setHost method, I received another error because of the empty response :

Fatal error: Uncaught Hoa\Http\Response\Response::parse(): (0) HTTP status is not well-formed: .

Slack documentation is not really verbose on the edge case around websockets... I've asked details but no answer for the moment...

Hywan commented 9 years ago

Yes 443... Ok so... Let me try today pr tonight something. /cc @CircleCode can help too.hoaproject/Websocket wrote:Same error with 8080...

I also tried with 443, which is given here as the default port for TLS communication... I receive an empty response...

—Reply to this email directly or view it on GitHub.

Sent Using Firefox OS

Hywan commented 9 years ago

Sorry, I am late. I have some other stuff to do but you are definitively on my to do list. Give me few more days please :-).

shulard commented 9 years ago

Don't worry, take your time ;)

shulard commented 9 years ago

I opened an issue at slack too to ask them about the problem... They tell me that there is nothing wrong from their part...

They try Hoa to connect to another websocket : echo.websocket.org and they get the same errors :

I hope this can help investigate...

Hywan commented 9 years ago

Thank you. It's really strange. Gonna test really really soon (today or tomorrow :-)).

Hywan commented 9 years ago

First:

$socket = new Hoa\Socket\Client('tcp://echo.websocket.org:80');
$socket->connect(); // works

Second:

$socket = new Socket\Client('tcp://echo.websocket.org:443');
$socket->connect(); // works

So it's good. However, echo.websocket.org does something really strange. I cannot ping the server. So when I try to connect with Hoa\Websocket\Client, I have a 404. Let's focus on one problem: Slack. I will try to take a look at it today (during lunch break probably).

Hywan commented 9 years ago

Ok it works.

use Hoa\Socket;
use Hoa\Websocket;
use Hoa\Core;

// Crappy but we need to read the temp. URL ;-).
$token = $argv[1];
$url = json_decode(
    file_get_contents(
        'https://slack.com/api/rtm.start?token=' . $token
    )
)->url;
var_dump($url);

// Parse the URL.
$parts = parse_url($url);
print_r($parts);

// Because this is wss://, it is secured.
// And because the first request is made over HTTP, here it is over HTTPS, so port 443 (and in TCP, as usual).
$socket = new Socket\Client('tcp://' . $parts['host'] . ':443');

// We pass the socket client and the endpoint (path) to the websocket client.
$websocket = new Websocket\Client(
    $socket,
    $parts['path']
);

// We must specify a host for the first HTTP request.
// Since we are running it in CLI, we don't have a host, so we provide one.
$websocket->setHost('hoa-project.net');

// Just print the response from Slack.
$websocket->on('message', function (Core\Event\Bucket $bucket) {
    $data = $bucket->getData();
    echo 'Received message: ', $data['message'], "\n";

    return;
});

// Here we go.
$websocket->run();

This script works perfectly well. Just call it this way:

$ php Client.php <slack-api-token>
# enjoy

I always did it this way, but you're right, we must provide a socket factory because… I lied a little bit. I edited the Hoa\Websocket\Client::doHandshake method to run $connection->enableEncryption(true, $connection::ENCRYPTION_TLS); after connecting the client.

Ok. Let me open an issue and develop a PR. You will get it in few days normally.

Hywan commented 9 years ago

Also, an example of responses I got from Slack:

Received message: {"type":"hello"}
Received message: {"reply_to":82,"type":"message","channel":"C026B05BQ","user":"U074D6GUR","text":"<https://fr.wikipedia.org/wiki/Gulo_gulo> pour tout ceux qui pensaient que ça n'était qu'un mythe ;-).","ts":"1437564314.000007"}
…
Hywan commented 9 years ago

@shulard Go check #44 :-].

shulard commented 9 years ago

@Hywan thanks for your detailed answer ! It works with the Hoa\Websocket\Client::doHandshake update ;)

I'll follow the #44 to view how it will be implemented, maybe I can help ?

Hywan commented 9 years ago

@shulard If you want to make a PR, I would be glad :-). If you think you are able to do so, feel free! Just tell me because I will start in few hours.

shulard commented 9 years ago

Ok so let me try, I'm starting in the afternoon ! Maybe I'll request reviews but I'll be happy to help ;)

Hywan commented 9 years ago

@shulard I guess you will start on Hoa\Socket?

shulard commented 9 years ago

Yep ! I'll submit PR for review

Hywan commented 9 years ago

Excellent :-]!

Hywan commented 9 years ago

I am closing this issue since the issue has been answered.

pprasun commented 7 years ago

Hi Hywan,

I have used the exact code you have used

use Hoa\Socket; use Hoa\Websocket; use Hoa\Core;

// Crappy but we need to read the temp. URL ;-). $token = $argv[1]; $url = json_decode( file_get_contents( 'https://slack.com/api/rtm.start?token=' . $token ) )->url; var_dump($url);

// Parse the URL. $parts = parse_url($url); print_r($parts);

// Because this is wss://, it is secured. // And because the first request is made over HTTP, here it is over HTTPS, so port 443 (and in TCP, as usual). $socket = new Socket\Client('tcp://' . $parts['host'] . ':443');

// We pass the socket client and the endpoint (path) to the websocket client. $websocket = new Websocket\Client( $socket, $parts['path'] );

// We must specify a host for the first HTTP request. // Since we are running it in CLI, we don't have a host, so we provide one. $websocket->setHost('hoa-project.net');

// Just print the response from Slack. $websocket->on('message', function (Core\Event\Bucket $bucket) { $data = $bucket->getData(); echo 'Received message: ', $data['message'], "\n";

return;

});

// Here we go. $websocket->run();

But i am receiving the error PHP Fatal error: Uncaught Hoa\Http\Response\Response::parse(): (0) HTTP status is not well-formed: .

shulard commented 7 years ago

Hello, since this issue is now closed, I encourage you to open a new one because it seems that the problem is not directly related to the one solved here.

Also, we have now introduced a new way to interact with Websocket URLs by handling ws and wss transport natively.

You can write directly something like :

$websocket = new Websocket\Client('wss://yourslackurlhere');
$websocket->setHost('hoa-project.net');

All the specific wss security features are now handled directly by the wss transport implementation (you can take a look at (official documentation)[https://hoa-project.net/En/Literature/Hack/Websocket.html] or #47 PR for more details).

If you still have the problem, maybe you can provide the Hoa\Http\Response\Response body to help debugging...