Skip to content

Instantly share code, notes, and snippets.

@remogatto
Forked from rpl/.gitignore
Created October 11, 2011 18:47
Show Gist options
  • Save remogatto/1279000 to your computer and use it in GitHub Desktop.
Save remogatto/1279000 to your computer and use it in GitHub Desktop.
Go Websocket implementation, hybi and Firefox
Currently Go websocket package works on Chromium and Google Chrome,
but doesn't work on any Firefox versions, so we've started to look out
to better understand the problem and find a solution or a workaround.
Follows some internal notes on the debugging session and a proposed patch.
TESTING BEHAVIOR
----------------
We've added some logging to the websocket package
(tmp-websocket-morelogging-go9990.patch) and coded a minimal websocket
example (hello-websocket-server.go) to detect where the current
implementation fails on firefox.
- build and launch hello-websocket
- Go (6g version weekly.2011-10-06 9990)
- open a browser on localhost:8081/public
- look at the produced logs (on the browser page and on the console)
# BEFORE APPLYING THE PATCH (go version 9990):
- Chromium (13.0.782.218 (Developer Build 98754 Linux) Ubuntu 11.04)
- hybi FAIL (error: "mismatch challenge/response")
- hixie76 FALLBACK & WORKS
- Google Chrome (14.0.835.202)
- hybi WORKS
- Firefox nightly (10.0a1), Firefox aurora (9.0a2), Firefox beta (8.0b2),
Firefox stable (7.0.1), Firefox 6.0.2
- hybi FAIL (error: "not websocket protocol")
- hixie76 FALLBACK & FAIL (error: "not websocket protocol")
- hixie75 FALLBACK & FAIL (error: "not websocket protocol")
# AFTER APPLYING THE PATCH (websocket-fix-hiby-go9990.patch):
- Firefox 6.0.2
- BADWEBSOCKETVERSION missing or bad WebSocket Version
- Firefox >= 7.0.1
- hybi WORKS
PROBLEM ANALISYS
----------------
Quoting from the ietf hybi draft, page 29 n. 6:
The request MUST contain a "Connection" header whose value MUST
include the "Upgrade" token.
(http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10)
Current behavior (as in "MUST be equal")
pkg/websocket/hiby.go (function ReadHandshake):
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
...
}
Fixed behavior (as in "MUST include"):
pkg/websocket/hiby.go (function ReadHandshake):
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
...
}
package main
import (
"http"
"websocket"
"io"
"fmt"
)
var WebsocketHandler = websocket.Handler(WebsocketServer)
func WebsocketServer(ws *websocket.Conn) {
io.Copy(ws, ws) // Echo
}
func main() {
// Static handler for public resources
http.Handle("/public/", http.FileServer(http.Dir("./")))
http.Handle("/websocket", WebsocketHandler)
fmt.Println("Open your browser on http://127.0.0.1:8081/public")
if err := http.ListenAndServe("127.0.0.1:8081", nil); err != nil {
panic(err)
}
}
<div id="results">
</div>
<script>
var el = document.getElementById("results");
if(typeof MozWebSocket !== "undefined")
WebSocket = MozWebSocket;
var socket = new WebSocket("ws://"+window.location.host+"/websocket");
socket.onopen = function () {
el.innerHTML += "OPENED<br/>";
socket.send("testmessage");
}
socket.onmessage = function(msg) { el.innerHTML += "RECEIVED DATA "+msg.data+"<br/>"; }
socket.onclose = function() { el.innerHTML += "CLOSED<br/>"; }
</script>
include $(GOROOT)/src/Make.inc
TARG=hello-websocket-server
GOFILES=hello-websocket-server.go
include $(GOROOT)/src/Make.cmd
diff -r 9c743824e7d6 src/pkg/websocket/hybi.go
--- a/src/pkg/websocket/hybi.go Tue Oct 11 11:11:47 2011 +1100
+++ b/src/pkg/websocket/hybi.go Tue Oct 11 19:54:15 2011 +0200
@@ -21,6 +21,7 @@
"os"
"strings"
"url"
+ "log"
)
const (
@@ -477,6 +478,8 @@
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
+ log.Print("DEBUG ", strings.ToLower(req.Header.Get("Upgrade")), " - ", strings.ToLower(req.Header.Get("Connection")))
+
return http.StatusBadRequest, ErrNotWebSocket
}
diff -r 9c743824e7d6 src/pkg/websocket/server.go
--- a/src/pkg/websocket/server.go Tue Oct 11 11:11:47 2011 +1100
+++ b/src/pkg/websocket/server.go Tue Oct 11 19:54:15 2011 +0200
@@ -10,6 +10,7 @@
"http"
"io"
"os"
+ "log"
)
func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request) (conn *Conn, err os.Error) {
@@ -17,6 +18,7 @@
var hs serverHandshaker = &hybiServerHandshaker{Config: config}
code, err := hs.ReadHandshake(buf.Reader, req)
if err == ErrBadWebSocketVersion {
+ log.Print("BADWEBSOCKETVERSION ",err)
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion)
buf.WriteString("\r\n")
@@ -24,14 +26,17 @@
return
}
if err != nil {
+ log.Print("TRY hixie76 ",err)
hs = &hixie76ServerHandshaker{Config: config}
code, err = hs.ReadHandshake(buf.Reader, req)
}
if err != nil {
+ log.Print("TRY hixie75 ",err)
hs = &hixie75ServerHandshaker{Config: config}
code, err = hs.ReadHandshake(buf.Reader, req)
}
if err != nil {
+ log.Print("FAIL WEBSOCKET AND RETURN ",err)
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
buf.WriteString("\r\n")
buf.WriteString(err.String())
diff -r 9c743824e7d6 src/pkg/websocket/hybi.go
--- a/src/pkg/websocket/hybi.go Tue Oct 11 11:11:47 2011 +1100
+++ b/src/pkg/websocket/hybi.go Tue Oct 11 17:14:29 2011 +0200
@@ -476,7 +476,7 @@
// HTTP version can be safely ignored.
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
- strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
+ !strings.Contains(strings.ToLower(req.Header.Get("Connection")),"upgrade") {
return http.StatusBadRequest, ErrNotWebSocket
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment