3

I developed a WebSocket server using PHP and it worked fine with ws://, but in production environment it uses https://, then I must use wss://.

Should I use certificate to start the socket or something like that? I can't parse the headers to complete the handshake.

How can I perform handshake behind a https server?

This is a AWS EC2 machine with Amazon Certificate.

I have tried import .pem file to socket initialization, run ws:// behind my https:// environment, and nothing worked :(

Socket initialization:

$socket = stream_socket_server(
    "tcp://0.0.0.0:" . env("APP_WSS_PORTA"),
    $errno,
    $errstr
);

I have tried also:

use Aws\Acm\AcmClient;

$cert = (new AcmClient(include config_path('aws.php')))->GetCertificate([
    "CertificateArn" => "arn:aws:acm:sa-east-1:EDITED_TO_STACKOVERFLOW"
])["CertificateChain"];

$cert_path = "cert.pem";

file_put_contents(base_path($cert_path), $cert);

$context = stream_context_create(
    ["ssl" => ["local_cert"=> $cert_path]]
);

$socket = stream_socket_server(
    "tcp://0.0.0.0:" . env("APP_WSS_PORTA"),
    $errno,
    $errstr,
    STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
    $context
);

My handshake function:

function wsHandshake($data)
{
    echo "> Handshake " . remoteIp() . PHP_EOL;
    $lines = preg_split("/\r\n/", $data);

    $headers = array();
    foreach ($lines as $line) {
        $line = chop($line);
        if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
            $headers[$matches[1]] = $matches[2];
        }
    }

    var_dump($data); // to debug it :)
    if (!isset($headers['Sec-WebSocket-Version']) || $headers['Sec-WebSocket-Version'] < 6) {
        echo '> Versao do WebSocket nao suportada' . PHP_EOL;
        return false;
    }

    $sec_accept = base64_encode(pack('H*', sha1($headers['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
    $response   = "HTTP/1.1 101 Switching Protocols\r\n";
    $response  .= "Upgrade: websocket\r\n";
    $response  .= "Connection: Upgrade\r\n";
    $response  .= "Sec-WebSocket-Accept: " . $sec_accept . "\r\n";
    $response  .= "\r\n";
    return $response;
}

var_dump with ws://

string(448) "GET / HTTP/1.1
Host: 127.0.0.1:3131
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://localhost
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: PuIYHJZ4x8IyXajFf4WAsw==
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
"

var_dump with wss://

string(517) "\000\000��}hh�հ�h����`�ݘ����O��GQ�E� S�8�@��,��=��c���C8�ǯ�G!6{<\000$�+�/̨̩�,�0�
�       ��\0003\0009\000/\0005\000
\000�\000\000\000�\000\000\000
\000\000
        \000\000\000\000\000\000
                                \000\000\000#\000\000\000\000\000
                                                                 hhttp/1.1\000\000\000\000\000\000\0003\000k\000i\000\000 ��"�c��GLGX�Ƶ��:�"ŵ�)բ
                                                                                                                                                E��)\000\000Al�d��#Q{��t��q>��eb���u�+�d��M�!2�-��tI����z�y�\ĉ�\000\\000-\000\000\000@\000\0"...
  • 1
    1. `wss` starts over HTTPS, which starts with a TLS HELLO packet... so yes, you will need to establish a TLS connection before starting the handshake. ; 2. PHP was designed for short lived connections. It came a long way since, but I highly recommend **not** using it for WebSockets, which might keep a PHP process running for much longer than it was designed to (or drop the connection). – Myst Aug 24 '19 at 08:24
  • I will try to change it to TLS as you recommended, but use PHP to create a WebSocket in my case is not a problem, I check the end of connection and kill the fork created to each client :). Thank you, I will give a feedback after try it. – Wellington Lopes Aug 24 '19 at 13:28
  • 1
    If you consider the performance costs of `fork`(ing) or creating a new thread (per connection), I think you would understand that this isn't an optimal design. Consider that a single process, with a single thread (or a small thread pool) could handle tens of thousands of concurrent WebSocket connections. How many resources will 1K connections consume with your `fork` based design? Assuming 2Mb per thread stack, which is less than the full cost of `fork`, you might be consuming more than 2GB in unnecessary overhead before the actual connection costs. – Myst Aug 24 '19 at 19:12
  • So what do you recommend to it? Change websocket language?... I can do it with python easily... but at first moment it must work with php xD – Wellington Lopes Aug 24 '19 at 19:21
  • 1
    Yes, Python should work significantly better, especially when using its `kqueue` or `epoll` objects. I wrote my implementation in C ([facil.io](https://facil.io)) and use it in Ruby ([`iodine`](https://github.com/boazsegev/iodine)), mainly because I love these languages and Ruby didn't have good solutions when I started. However, I wonder if Python doesn't already have a good WebSocket library you could use. – Myst Aug 24 '19 at 19:26
  • Thank you! I try it with python :D – Wellington Lopes Aug 24 '19 at 20:36

0 Answers0