WebSocket, "Invalid frame header"
# help
d
When trying to migrate the example at https://github.com/toitlang/toit/discussions/1198 from the pkg-websocket to http.web_socket, I revised
main
initially to look like:
Copy code
import http
import http.request show RequestIncoming
import http.web_socket
import net

/**
Example that demonstrates a web-socket server.
The server updates a counter for button presses.
*/

network := net.open
addr_str := network.address.stringify
server := http.Server

main:
  print "Open a browser on: $network.address:8080"
  sessions := {:}
  server.listen network 8080:: | request/RequestIncoming response/http.ResponseWriter |
    print "enter handler"
    websocket := server.web_socket request response
    if websocket:
      print "ws is: $websocket"
    else:
      print "request $request.method $request.path $request.body.read"
    if request.path == "/":
      response.write spa
    else:
      print "request $request.method $request.path $request.body.read failed"
    print "exit handler"
When the page loads, I get:
Copy code
WebSocket connection to 'ws://192.168.0.174:8080/' failed: Invalid frame header open-ws-connection @ (index):74
which corresponds to the line:
Copy code
ws = new WebSocket('ws://192.168.0.174:8080');
in the page javascript. Is this a bug in the new websocket code or cockpit error (as Session.upgrade is replaced)?
e
The way it's expected to work is that the websocket connection is on a particular path.
For example ws://192.168.0.174.8080/ws
Then in the handler you recognize the path and don't try to upgrade to websocket protocol unless the path matches.
Currently it always tries to upgrade every request to websocket, which gives 400 No nonce errors that you can see in the browser console.
And currenly when it gets a websocket request on the path "/" it first converts to websocket, but then continues with the
if request.path == "/"
and sends it HTML on the websocket connection, which messes things up.
Copy code
main:
  print "Open a browser on: $network.address:8080"
  sessions := {:}
  server.listen network 8080:: | request/RequestIncoming response/http.ResponseWriter |
    if request.path == "/ws":
      websocket := server.web_socket request response
      if websocket:
        print "ws is: $websocket"
    else if request.path == "/":
      response.write SPA
    else:
      print "request $request.method $request.path $request.body.read failed"
      response.headers.set "Content-Type" "text/plain"
      response.write_headers 404
      response.write "Not found\n"
Then you also need to change the script to have:
ws = new WebSocket('ws://$(addr_str):8080/ws');
with the
/ws
at the end.
Another change you need is
--max-tasks=2
at least when creating the HTTP server.
Copy code
server := http.Server --max-tasks=2
Actually it sometimes works with max-tasks=1 (the default).
Note the
else
in front of the
if request.path == "/"
which makes sure you don't do the whole router when it's a websocket connection.
d
doh. thanks.
Does anything jump out at you as you read the below? Summary: Chrome works after ~30 sec (see -- active -- , below "Click Me" button) Firefox fails immediately. Referencing examples/counter.toit in https://github.com/davidg238/webd , when I run:
jag run counter.toit
and view the page on Chrome, I get:
Copy code
Starting web server at 192.168.0.137:8080
DEBUG: client connected {peer: 192.168.0.174:38064}
DEBUG: incoming request {peer: 192.168.0.174:38064, path: /}
DEBUG: client connected {peer: 192.168.0.174:38078}
<after ~30 seconds>
DEBUG: connection ended {peer: 192.168.0.174:38078, reason: DEADLINE_EXCEEDED}
DEBUG: client connected {peer: 192.168.0.174:38082}
DEBUG: incoming request {peer: 192.168.0.174:38082, path: /ws}
websocket request invoked
DEBUG: connection ended {peer: 192.168.0.174:38064, reason: DEADLINE_EXCEEDED}
DEBUG: client socket detached {peer: 192.168.0.174:38082}
I get the first 3 lines on the console, and the page renders immediately. The websocket shows "pending" in the Chrome developer tools/network tab. Afer about 30 seconds, the websocket status changes to 101 and the button works. There is no apparent request for the favicon.ico
On Firefox, I get:
Copy code
Starting web server at 192.168.0.137:8080
DEBUG: client connected {peer: 192.168.0.174:35648}
DEBUG: incoming request {peer: 192.168.0.174:35648, path: /}
DEBUG: client connected {peer: 192.168.0.174:35664}
DEBUG: incoming request {peer: 192.168.0.174:35664, path: /ws}
websocket request invoked
DEBUG: connection ended {peer: 192.168.0.174:35664}
DEBUG: connection ended {peer: 192.168.0.174:35648, reason: DEADLINE_EXCEEDED}
immediately. In the developer/network tab, In the URL window:
Copy code
status 200, method get, file /  
status 404, method get, file favicon.ico  
status 400, method get, file ws
In the request window:
Copy code
GET http://192.168.0.137:8080/  [HTTP/1.1 200 ok 190ms]
GET http://192.168.0.137:8080/favicon.ico  [HTTP1.1 404 Not Found 0ms]
GET ws://192.168.0.137:8080/ws  [HTTP/1.1 400 No Connection: Upgrade 41mS]
h
That "30 seconds" jumps out. That's the copious timeout that can be reduced. You'll probably also need to up the number of tasks the server uses like Erik said, so add these to the server: --max-tasks=6 --read_timeout=d where d is d := Duration --ms=1000 or something smaller than 30 seconds that works for you.
d
Thanks. That made Chrome usable,
Firefox still fails, will look further
2 Views