Search code examples
nginxamazon-ec2websocketcloudflareratchet

Ratchet is not receiving WebSocket packet from browser?


I'm trying to test simple ping & pong responses with WebSockets by using Ratchet. My WebSocket server is not seeing any responses from my web browser's WebSocket client but packets sent by WebSocket server are seen fine by the browser. I'm trying to find why.

My current guesses are:

  • I'm missing some HTTP header(s)
  • I have to encode packets on browser wsclient.send(encodeFrame("{'action': 'pong'}"));
  • CloudFlare is not recognizing packet in WS stream as valid and thrashes it
  • CloudFlare or nginx in EC2 instance is doing some weird buffering
  • Ratchet is not recognizing packet on lowest IOServer level and thrashes it
    • But I never get any errors or Exceptions from this level

Setup:

  • Linux server @ Amazon EC2
  • DNS @ CloudFlare with free plan and acceleration on + forced HTTPS redirect on
  • HTTP server is nginx
  • No HTTPS on nginx (Cloudflare redirects HTTPS -> EC2 HTTP)
  • WebSocket server (Ratchet) running at 127.0.0.1:65000 on EC2
  • Nginx redirects /api to 127.0.0.1:65000 (Ratchet)

Tested with own WebSocket client at EC2 instance:

  • 127.0.0.1:65000 works fine
  • <Amazon NAT IP of instance>:80 works fine
  • <Amazon public IP of instance>:80 works fine
  • <CloudFlare IP of Amazon public IP>:80 Connects to WebSocket server on Application implementatuin level but doesn't see packet on onMessage method on any level (App, WsServer, HTTPServer)
  • <CloudFlare IP of Amazon public IP>:443 Gives 400 Bad Request because test client is just simple TCP stream

Tested from local machine:

Directly connect to host offered by CloudFlare's cached IP. Dojox.Socket connects to wss://host/api. Connection is again seen on Application implementation level on Ratchet (onOpen is fired). My browser sees the ping packets fine so sending from Ratchet works fine.

But then I try to send pong reply to the ping from browser and onMessage method is not never fired on any level on Ratched. Connection keeps open and if I watch with Fiddler both pings and pongs are constantly sent but WebSocket server never receives those pongs (onMessage).

Following Fiddler's WebSocket stream shows that pongs from browser has "Data masked by key: <some hex>" but pings from Ratched are not masked.

Connection summary:

Page load:

Local machine http://host/ → CloudFlare HTTPS redirect https://host/http://host-with-amazon-public-ip/http://host-with-amazon-NAT-ip/ → HTML + JS page that loads wss WebSocket Connection to /api

WebSocket connection to /api:

CloudFlare HTTPS redirected wss://host/api → http://host-with-amazon-public-ip/apihttp://host-with-amazon-NAT-ip/api → local server nginx redirect /api → 127.0.0.1:65000 → Connection upgrade → WebSocket stream → WebSocket stream for web browser

nginx.conf

server {
  listen 80;

  server_name test.example.com;
  root /home/raspi/test/public;
  autoindex off;
  index index.php;

  access_log /home/raspi/test/http-access.log;
  error_log /home/raspi/test/http-error.log notice;

  location / { 
    index index.php; 
    try_files $uri $uri/ /index.php?$args;
  }

 location /api {
    access_log /home/raspi/test/api-access.log;
    error_log /home/raspi/test/api-error.log;

    expires epoch;

    proxy_ignore_client_abort on;
    proxy_buffering off;
    proxy_request_buffering off;
    proxy_cache off;
    proxy_pass http://127.0.0.1:65000/;

    proxy_http_version 1.1;

    proxy_set_header Host $host;
    proxy_set_header Connection "keep-alive, Upgrade";
    proxy_set_header Upgrade "websocket";
    proxy_set_header Accept-Encoding "gzip, deflate";
    proxy_set_header Sec-WebSocket-Extensions "permessage-deflate";
    proxy_set_header Sec-WebSocket-Protocol "game";
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  location ^~ /js/app/ {
    try_files $uri /;
    expires epoch;
    add_header Cache-Control "no-cache" always;
  }

  location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt)$ {
    try_files $uri /;
    access_log off;
    expires max;
  }

  location = /robots.txt  { access_log off; log_not_found off; }
  location = /favicon.ico { access_log off; log_not_found off; }    
  location ~ /\. { access_log off; log_not_found off; deny all; }

  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$; 
    fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; 
    fastcgi_index index.php; 
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 
    include fastcgi_params; 
  }
}

Solution

  • Web sockets are currently only available at the business and enterprise levels. You mentioned you're on the free plan. That's the issue here.