sirn-se / websocket-php

[php-lib] WebSocket client and server in PHP
Other
117 stars 18 forks source link

How to handle redirection? #48

Closed long-blade closed 6 months ago

long-blade commented 6 months ago

When I try to connect to a websocket using Postman everything is working fine but when I use this library I get "Invalid status code 301." Have in mind that this is an external websocket provider that is working with internal redirecting when you use customer headers

Is there any way to resolve this? or it need to be added as a feature?

image

image

sirn-se commented 6 months ago

Hi @long-blade

As of v2.2 there is a follow-redirect middleware that might help you. https://github.com/sirn-se/websocket-php/blob/v2.2-main/docs/Middleware/FollowRedirect.md

Note that redirects are unspecified by WebSocket protocol, so there are no expected behavior specified anywhere.

long-blade commented 6 months ago

Hey @sirn-se

for some reason is left-handing wen I start the script

image

can you see anything wrong?

long-blade commented 6 months ago

image

long-blade commented 6 months ago

@sirn-se does redirection take into consideration the possibility of response header cookies?

sirn-se commented 6 months ago

@long-blade

No, it only handles headers defined as part of websocket protocol. Any application specific logic has to be handled by implementing code.

The log partial you posted look fine to me (ignore the "Error" row). You can see it disconnecting the initial connection (as it gets a redirect) and then initializing a new one. Whatever goes wrong happen after your log post.

Only odd thing in your code snippet is that you immediately wait for server to transmit messages. Most cases I 've seen the client is expected to send an initial message to the server (identification, configuration, etc) before the server starts delivering things. But you need to check what the service provider expects.

long-blade commented 6 months ago

Hey @sirn-se,

Yes you are right most of the time in order to start the streaming process you initialise the connection with a message, however in this case the server accepts the connection and then expects a special message (heartbeat) every 30 seconds, also the subscription is done by specific message which didn't implement yet.

So in other words, this server accepts connection with 101 response and then expects you to send messages

In postman when I click connect it connects. And then I send messages.

Also in response headers I see 3 set of cookie headers

Should I implement a custom redirect middleware that adds the response headers to the next requests?

long-blade commented 6 months ago

So, The while loop in the pull method in the HttpHandler.php is forever looping with the buffer variable to be NULL.

image

So I don't understand why the buffer is returning null, under what circumstances this is happening?

In the old package, i was able to get the set-cookies headers but I don't see them now.

Just to clarify when I use postman I just click connect and it does connect without any payload, just specific headers which I have included in my script.

sirn-se commented 6 months ago

With the redirect solved, I'm not sure what issue you're experiencing.

What you normally do is something like;

$client
    // add middlewares here
    ->onText(function (Client $client, Connection $connection, Message $message) {
        // This is called when a message is received from server
    });
// Initial message to server - configuration, authorization, similar
$client->text("Hello");
// Start listening to messages received by server, will call the onText method
$client->start();

With your code snippet, you will never send a message to server unless the server have sent a message first.

Cookies doesn't make much sense in a websocket service, but if you do need them then I would recommend extending the FollowRedirect middleware or possibly use the Callback middleware.

long-blade commented 6 months ago

yes I used the code above with the redirect middleware, and it is stuck on a never-ending loop, in the pull method, because the buffer is null.

Even if i send a message first i get the same problem.

long-blade commented 6 months ago

image

sirn-se commented 6 months ago

That's weird. It means that fgets return false, which indicates there's nothing to read on the stream.

You could try to remove all the addHeader() add setPersistent() calls. The headers you've added are part of the websocket protocol, and are added by the client.

From what I can see in your masked screenshots, the redirects from server appear somewhat inconsistent.

Do you have a link to documentation of the API you're trying to connect to? So I can investigate a bit on my own.

long-blade commented 6 months ago

yes ofc https://docs.xtremepush.com/docs/external-api-overview

sirn-se commented 6 months ago

I can't find any information on how to use websockets with that api.

long-blade commented 6 months ago

yes, I can't either...

Facts:

  1. This is a third-party pusher that most of the time is used in mobile applications
  2. Postman has no problem connecting to it. (how does he do it)
  3. to establish a connection with the www.xtremepush.com/websock you need to provide some headers that will guide the stream server to be served (something like an internal routing system)

Problems:

  1. Why we can't connect to the external API using this lib, what does Postman do differently?

If you like me to give you some extra internal data (headers) we can do this in private (you will also need a VPN because is country-based only). I will be very happy to work with you to solve this issue

long-blade commented 6 months ago

Also, do you think is wise to add this line of code? For example, in my case the URL has a path, shouldend we include this in the new $host_uri? Should I open an MR for it or is wrong?

Client.php connect() method

image

sirn-se commented 6 months ago

No, $host_uri is for socket initialization and must only contain scheme:host:port.

Path and other parts of URI are to be used on HTTP handshake request.

long-blade commented 6 months ago

let me know what do you think of the prev comment

sirn-se commented 6 months ago

So I tried connecting to the service. And I do get a proper response, although a 400 Bad Request, but it does read from the stream.

try {
    $client = new \WebSocket\Client('ws://external-api.xtremepush.com/websock/');
    $client
        ->addMiddleware(new \WebSocket\Middleware\CloseHandler())
        ->addMiddleware(new \WebSocket\Middleware\PingResponder())
        ->addMiddleware(new \WebSocket\Middleware\FollowRedirect())
        ->onText(function ($client, $connection, $message) {
            echo "> Received '{$message->getContent()}' [opcode: {$message->getOpcode()}]\n";
        })
        ->onConnect(function ($client, $connection, $message) {
            echo "> Connect - $message\n";
        })
        ->onDisconnect(function ($client, $connection) {
            echo "> Disconnect\n";
        })
        ;

    $client->setLogger(new \WebSocket\Test\EchoLog());
    $client->start();

} catch (\Throwable $e) {
    echo "# ERROR: {$e->getMessage()} [{$e->getCode()}]\n";
}

So I wonder if it might be a SSL issue on the computer you're running on? For some reason Postman connects over http, but the redirect we get is https.

If you run the code above, do you get "Invalid status code 400" as well?

long-blade commented 6 months ago

No, as I said before you need to include custom headers in order for the request to be properly routed! Also is country-related so we will need a VPN from a specific country!

The custom headers are Origin, Host and Sec-WebSocket-Protocol!

Yes, the above code gets me 400 as it is not a correctly formed request! The server expects specific headers and the request to be a specific country related!

I can give you that info but i will need you to do this in private for obvious reasons.

sirn-se commented 6 months ago

My point is that this code takes you further than before. You don't get stuck with an empty stream as before. The server response is received properly (error or not).

So if you start with this, and add the custom headers one by one, you should be able to narrow the issue down. If you add something and end up in the null read loop again, we will know what caused it.

long-blade commented 6 months ago

Ok, I tried that but nothing to be changed, I am getting 404 until all headers are put on! The headers are not the problem, I think the problem is in the request final form.

When I use the correct host header then the request is 301 and forever looping.

** The same headers are used in Postman @sirn-se

sirn-se commented 6 months ago

I did find an issue, where a redirect from ws to https ended up using tcp instead of ssl. This may relate to the problem you're experiencing.

Fix available in v2.2.2.

long-blade commented 6 months ago

@sirn-se Perfect !!!! works very well! Thank you for all your help!

sirn-se commented 6 months ago

Solved, closing.