3
votes

I've coded websocket server/client with PHP and it worked for me during 2 years. Now it is not working, saying: Error during WebSocket handshake: Incorrect 'Sec-WebSocket-Accept' header value

My clientside code is essentially this:

socket = new WebSocket("ws://<?= EVENT_SERVER_ADDR ?>:"+EVENT_SERVER_PORT+"<?= EVENT_SERVER_WWW_PATH ?>");

PHP serverside code is this:

list ($resource, $host, $connection, $version, $origin, $key, $protocol, $upgrade) = $this->getheaders ($buffer);

$this->log ("Handshaking...");
$reply  = 
    "HTTP/1.1 101 Switching Protocols\r\n" .
    "Upgrade: {$upgrade}\r\n" .
    "Connection: {$connection}\r\n" .
    "Sec-WebSocket-Version: {$version}\r\n" .
    "Sec-WebSocket-Origin: {$origin}\r\n" .
    "Sec-WebSocket-Location: ws://{$host}{$resource}\r\n" .
    "Sec-WebSocket-Accept: " . $this->calcKey ($key) . "\r\n";
if ($protocol)
    $reply .= "Sec-WebSocket-Protocol: $protocol\r\n";
$reply .=   "\r\n";

// Closes the handshake
socket_write ($user->socket, $reply, strlen ($reply));

function calcKey ($key) {
    // Constant string as specified in the ietf-hybi-17 draft
    $key .= "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    $key = sha1 ($key, true);
    // $key = pack ('H*', $key); // should I uncomment this line?
    $key = base64_encode ($key);

    return $key;
}

function getheaders ($buffer) {
    $resource = $host = $connection = $version = $origin = $key = $protocol = $upgrade = null;

    preg_match ('#GET (.*?) HTTP#', $buffer, $match) && $resource = $match[1];
    preg_match ("#Host: (.*?)\r\n#", $buffer, $match) && $host = $match[1];
    preg_match ("#Connection: (.*?)\r\n#", $buffer, $match) && $connection = $match[1];
    preg_match ("#Sec-WebSocket-Version: (.*?)\r\n#", $buffer, $match) && $version = $match[1];
    preg_match ("#Origin: (.*?)\r\n#", $buffer, $match) && $origin = $match[1];
    preg_match ("#Sec-WebSocket-Key:\s*(.*?)\r\n#", $buffer, $match) && $key = $match[1];
    preg_match ("#Sec-WebSocket-Protocol:\s*(.*?)\r\n#", $buffer, $match) && $protocol = $match[1];
    preg_match ("#Upgrade: (.*?)\r\n#", $buffer, $match) && $upgrade = $match[1];

    return array ($resource, $host, $connection, $version, $origin, $key, $protocol, $upgrade);
}

It's funny that those guys just changed the standard without keeping old code functioning and without saying anything online (I really tried to google it very hard). Does anyone know what's my problem?

1
Hi, it is failing with different browsers? Obvious question, but you didn't specify it. - pietro909
@pietro909 it's failing with Chrome 41, Firefox says: "Firefox can't establish a connection to the server at ws://192.168.1.34:38848/operator/modules/campaign/event_server.php" - Fancy John
@pietro909 Safari under MacOS 10.9 says: "[Error] WebSocket connection to 'ws://192.168.1.34:38848/operator/modules/campaign/event_server.php' failed: Invalid UTF-8 sequence in header value (campaign.php, line 0)" - Fancy John
IE 11 seems to work though... - Fancy John

1 Answers

5
votes

So I figured out the problem. And that was the buffer limit.

Apparently, the variable $buffer contained only about 4 KB of data, and because of cookies that were coming from dataTables, the input data was much more. And the Sec-WebSocket-Key header was after all cookies. So the $key was empty every time, giving wrong Sec-WebSocket-Accept.

Advice: debug more deeply.