0

I have the following code, this was previously working and now all of a sudden I am getting an error;

The error I am getting is;

Failed to connect to server Server responed with: Server did not accept to upgrade connection to websocket.HTTP/1.1 200 OK Date: Sun, 22 Aug 2021 01:07:27 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive Last-Modified: Fri, 05 Mar 2021 07:33:32 GMT X-By: @XRPLF X-Upgrade: WebSocket X-Conn: upgrade CF-Cache-Status: DYNAMIC Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=ahbUvdpOxo1wZb%2B54qo5pEWE0KGc%2BTpWu2vgw47WhbCgjbfPwdQOGLCAZlivJyijhHs4PTt4nYVIW3ak%2BwAtlz6qhz36saYBmLZ3%2FyKJc8ZB6OJA0%2FNVp14%3D"}],"group":"cf-nel","max_age":604800} NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800} Server: cloudflare CF-RAY: 682834517edc2ce3-LHR alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400 6980

I am not too sure what is causing it below is the code;

<?php 
    
    include('/websocket_client.php'); 

    $server = 'xrpl.ws';
        
    $command = json_encode(array(
        'id' => 2,
        'command' => "server_info"
        
    ));
    
    
    if( $sp = websocket_open($server, 443,'',$errstr, 10, true) ) {
      websocket_write($sp,$command);
      $result = websocket_read($sp,$errstr);
    }else {
      echo "Failed to connect to server\n";
      echo "Server responed with: $errstr\n";
    }

    $result_data = json_decode($result, true);
        
    echo '<pre>';
    echo $result_data; 
    echo '</pre>';
?>

Below is the Websocket_Client.php page I am sorry for the length ; But I thought it might be important to include it all.

<?php
/*----------------------------------------------------------------------------*\
  Websocket client - https://github.com/paragi/PHP-websocket-client
  By Paragi 2013, Simon Riget MIT license.
  This is a demonstration of a websocket clinet.
  If you find flaws in it, please let me know at simon.riget (at) gmail
  Websockets use hybi10 frame encoding:
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-------+-+-------------+-------------------------------+
       |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
       |I|S|S|S|  (4)  |A|     (7)     |             (16/63)           |
       |N|V|V|V|       |S|             |   (if payload len==126/127)   |
       | |1|2|3|       |K|             |                               |
       +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
       |     Extended payload length continued, if payload len == 127  |
       + - - - - - - - - - - - - - - - +-------------------------------+
       |                               |Masking-key, if MASK set to 1  |
       +-------------------------------+-------------------------------+
       | Masking-key (continued)       |          Payload Data         |
       +-------------------------------- - - - - - - - - - - - - - - - +
       :                     Payload Data continued ...                :
       + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
       |                     Payload Data continued ...                |
       +---------------------------------------------------------------+
  See: https://tools.ietf.org/rfc/rfc6455.txt
  or:  http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-4.2
\*----------------------------------------------------------------------------*/

/*============================================================================*\
  Open websocket connection
  resource websocket_open(string $host [,int $port [,$additional_headers [,string &error_string ,[, int $timeout]]]]
  host
    A host URL. It can be a domain name like www.example.com or an IP address,
    with port number. Local host example: 127.0.0.1:8080
  port
  headers (optional)
    additional HTTP headers to attach to the request.
    For example to parse a session cookie: "Cookie: SID=" . session_id()
  error_string (optional)
    A referenced variable to store error messages, i any
  timeout (optional)
    The maximum time in seconds, a read operation will wait for an answer from
    the server. Default value is 10 seconds.
  ssl (optional)  
  persistant (optional)
  path (optional)
  Context (optional)
  Open a websocket connection by initiating a HTTP GET, with an upgrade request
  to websocket.
  If the server accepts, it sends a 101 response header, containing
  "Sec-WebSocket-Accept"
\*============================================================================*/
function websocket_open($host='',$port=80,$headers='',&$error_string='',$timeout=10,$ssl=false, $persistant = false, $path = '/', $context = null){

  // Generate a key (to convince server that the update is not random)
  // The key is for the server to prove it i websocket aware. (We know it is)
  $key=base64_encode(openssl_random_pseudo_bytes(16));

  $header = "GET " . $path . " HTTP/1.1\r\n"
    ."Host: $host\r\n"
    ."pragma: no-cache\r\n"
    ."Upgrade: WebSocket\r\n"
    ."Connection: Upgrade\r\n"
    ."Sec-WebSocket-Key: $key\r\n"
    ."Sec-WebSocket-Version: 13\r\n";

  // Add extra headers
  if(!empty($headers)) foreach($headers as $h) $header.=$h."\r\n";

  // Add end of header marker
  $header.="\r\n";

  // Connect to server
  $host = $host ? $host : "127.0.0.1";
  $port = $port <1 ? ( $ssl ? 443 : 80 ): $port;
  $address = ($ssl ? 'ssl://' : '') . $host . ':' . $port;
  
  $flags = STREAM_CLIENT_CONNECT | ( $persistant ? STREAM_CLIENT_PERSISTENT : 0 );
  $ctx = $context ?? stream_context_create();
  $sp = stream_socket_client($address, $errno, $errstr, $timeout, $flags, $ctx);
  
  if(!$sp){
    $error_string = "Unable to connect to websocket server: $errstr ($errno)";
    return false;
  }

  // Set timeouts
  stream_set_timeout($sp,$timeout);

  if (!$persistant or ftell($sp) === 0) {

    //Request upgrade to websocket
    $rc = fwrite($sp,$header);
    if(!$rc){
      $error_string
        = "Unable to send upgrade header to websocket server: $errstr ($errno)";
      return false;
    }

    // Read response into an assotiative array of headers. Fails if upgrade failes.
    $reaponse_header=fread($sp, 1024);

    // status code 101 indicates that the WebSocket handshake has completed.
    if (stripos($reaponse_header, ' 101 ') === false
      || stripos($reaponse_header, 'Sec-WebSocket-Accept: ') === false) {
      $error_string = "Server did not accept to upgrade connection to websocket."
        .$reaponse_header. E_USER_ERROR;
      return false;
    }
    // The key we send is returned, concatenate with "258EAFA5-E914-47DA-95CA-
    // C5AB0DC85B11" and then base64-encoded. one can verify if one feels the need...

  }
  return $sp;
}

/*============================================================================*\
  Write to websocket
  int websocket_write(resource $handle, string $data ,[boolean $final])
  Write a chunk of data through the websocket, using hybi10 frame encoding
  handle
    the resource handle returned by websocket_open, if successful
  data
    Data to transport to server
  final (optional)
    indicate if this block is the final data block of this request. Default true
  binary (optional)
    indicate if this block is sent in binary or text mode.  Default true/binary
\*============================================================================*/
function websocket_write($sp,$data,$final=true,$binary=true){
  // Assemble header: FINal 0x80 | Mode (0x02 binary, 0x01 text)

  if ($binary)
      $header=chr(($final?0x80:0) | 0x02); // 0x02 binary mode
  else
      $header=chr(($final?0x80:0) | 0x01); // 0x01 text mode

  // Mask 0x80 | payload length (0-125)
  if(strlen($data)<126) $header.=chr(0x80 | strlen($data));
  elseif (strlen($data)<0xFFFF) $header.=chr(0x80 | 126) . pack("n",strlen($data));
  else $header.=chr(0x80 | 127) . pack("N",0) . pack("N",strlen($data));

  // Add mask
  $mask=pack("N",rand(1,0x7FFFFFFF));
  $header.=$mask;

  // Mask application data.
  for($i = 0; $i < strlen($data); $i++)
    $data[$i]=chr(ord($data[$i]) ^ ord($mask[$i % 4]));

  return fwrite($sp,$header.$data);
}

/*============================================================================*\
  Read from websocket
  string websocket_read(resource $handle [,string &error_string])
  read a chunk of data from the server, using hybi10 frame encoding
  handle
    the resource handle returned by websocket_open, if successful
  error_string (optional)
    A referenced variable to store error messages, i any
  Read
  Note:
    - This implementation waits for the final chunk of data, before returning.
    - Reading data while handling/ignoring other kind of packages
 \*============================================================================*/
function websocket_read($sp,&$error_string=NULL){
  $data="";

  do{
    // Read header
    $header=fread($sp,2);
    if(!$header){
      $error_string = "Reading header from websocket failed.";
      return false;
    }

    $opcode = ord($header[0]) & 0x0F;
    $final = ord($header[0]) & 0x80;
    $masked = ord($header[1]) & 0x80;
    $payload_len = ord($header[1]) & 0x7F;

    // Get payload length extensions
    $ext_len = 0;
    if($payload_len >= 0x7E){
      $ext_len = 2;
      if($payload_len == 0x7F) $ext_len = 8;
      $header=fread($sp,$ext_len);
      if(!$header){
        $error_string = "Reading header extension from websocket failed.";
        return false;
      }

      // Set extented paylod length
      $payload_len= 0;
      for($i=0;$i<$ext_len;$i++)
        $payload_len += ord($header[$i]) << ($ext_len-$i-1)*8;
    }

    // Get Mask key
    if($masked){
      $mask=fread($sp,4);
      if(!$mask){
        $error_string = "Reading header mask from websocket failed.";
        return false;
      }
    }

    // Get payload
    $frame_data='';
    while($payload_len>0){
      $frame= fread($sp,$payload_len);
      if(!$frame){
        $error_string = "Reading from websocket failed.";
        return false;
      }
      $payload_len -= strlen($frame);
      $frame_data.=$frame;
    }

    // Handle ping requests (sort of) send pong and continue to read
    if($opcode == 9){
      // Assamble header: FINal 0x80 | Opcode 0x0A + Mask on 0x80 with zero payload
      fwrite($sp,chr(0x8A) . chr(0x80) . pack("N", rand(1,0x7FFFFFFF)));
      continue;

    // Close
    } elseif($opcode == 8){
      fclose($sp);

    // 0 = continuation frame, 1 = text frame, 2 = binary frame
    }elseif($opcode < 3){
      // Unmask data
      $data_len=strlen($frame_data);
      if($masked)
        for ($i = 0; $i < $data_len; $i++)
          $data.= $frame_data[$i] ^ $mask[$i % 4];
      else
        $data.= $frame_data;

    }else
      continue;

  }while(!$final);

  return $data;
}
?>

I thought initially I had been blacklisted or something for the amount of requests I was making and the fact that it in the message I saw something about CloudFlare but I used a VPN to navigate to xrpl.ws via the IP of the host and I was able to access this without problems. I have not made any changes to the PHP ini file either so I really am stuck to what is causing this. Thanks for any help and sorry for the length of the examples. Thanks again.

I did see this previous answer which mentioned about the way the key is generated but I looked into it and I believe its using a good generation method. So I really am at a loss.

Lewis
  • 170
  • 7
  • 1
    Does it work with domain `xrplcluster.com`? – Jacob Mulquin Aug 22 '21 at 01:32
  • @mulquin I'm just going to try – Lewis Aug 22 '21 at 01:32
  • @mulquin same problem with `xrplcluster.com` – Lewis Aug 22 '21 at 01:33
  • 1
    I'm not sure to be honest. I tried a few online websocket testers and they can all connect to it just fine. Have you tried a different PHP client? – Jacob Mulquin Aug 22 '21 at 01:56
  • @mulquin ah ok thanks for sharing that with me, yeah I think you are right with it being the PHP client but so strange how it was working flawlessy for days without no problem then all of a sudden – Lewis Aug 22 '21 at 02:17
  • 1
    Probably ."Origin: https://xrpl.ws\r\n" is requiered now by server ? – Heinz Aug 22 '21 at 10:42
  • @Heinz What does that mean? Is there a way I can edit the current WS client to pass that? – Lewis Aug 22 '21 at 13:55
  • 1
    Just add this line `"Origin: https://xrpl.ws\r\n"` to $header in your php client and see what happens. You might have to experiment ... – Heinz Aug 22 '21 at 14:29
  • 1
    Connecting to 'wss://xrplcluster.com" then sending {"command": "server_info"} yield this {"result":{"info":{"build_version":"1.7.2","complete_ledgers":"32570-65836746","hostid":"ADD","io_latency_ms":1,"jq_trans_overflow":"51255","last_close":{"converge_time_s":3.004,"proposers":40},"load_factor":1,"peer_disconnects":"155","peer_disconnects_resources":"3","peers":d_ledger":{"age":4,"base_fee_xrp":1e-05,"hash":"D6A2CFDAB7A8120 .cut here because to long – Heinz Aug 22 '21 at 15:18
  • @Heinz what do you mean man? did you manage to get this working? I tried adding the origin to the header but had the same result. – Lewis Aug 22 '21 at 15:20
  • 1
    Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/236292/discussion-between-heinz-and-lewis). – Heinz Aug 22 '21 at 15:24
  • @Heinz is there any limit in your websocket client that would limit the response to set amount of characters, the responses im receiving back from the server are being chopped off – Lewis Aug 22 '21 at 18:08
  • @Heinz don't suppose you had a chance to extend the amount of data in the response it can handle? – Lewis Oct 22 '21 at 03:34
  • 1
    The limit is now set to 50, and no cut. See `websocketxrpl.php` – Heinz Oct 22 '21 at 07:08
  • @Heinz Thanks mate. – Lewis Oct 22 '21 at 21:50

1 Answers1

0

The error message is pretty clear:

Server did not accept to upgrade connection to websocket.

You'd need Ratchet, because there likely is no web-socket support available on this server.

Or it may send out unexpected HTTP headers.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216