Created
October 11, 2011 17:55
-
-
Save rpl/1278843 to your computer and use it in GitHub Desktop.
Go Websocket implementation, hybi and Firefox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_obj | |
*~ | |
bin | |
*.6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 server output 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") { | |
... | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
include $(GOROOT)/src/Make.inc | |
TARG=bin/hello-websocket-server | |
GOFILES=hello-websocket-server.go | |
include $(GOROOT)/src/Make.cmd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 Wed Oct 12 12:25:49 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 | |
} | |
diff -r 9c743824e7d6 src/pkg/websocket/hybi_test.go | |
--- a/src/pkg/websocket/hybi_test.go Tue Oct 11 11:11:47 2011 +1100 | |
+++ b/src/pkg/websocket/hybi_test.go Wed Oct 12 12:25:49 2011 +0200 | |
@@ -30,6 +30,55 @@ | |
} | |
} | |
+// Test the hybiServerHandshaker supports firefox implementation and | |
+// checks Connection header include (but it's not necessary equal to) | |
+// "upgrade" | |
+func TestHybiFirefoxHandshake(t *testing.T) { | |
+ config := new(Config) | |
+ handshaker := &hybiServerHandshaker{Config: config} | |
+ br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 | |
+Host: server.example.com | |
+Upgrade: websocket | |
+Connection: keep-alive, upgrade | |
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== | |
+Origin: http://example.com | |
+Sec-WebSocket-Protocol: chat, superchat | |
+Sec-WebSocket-Version: 13 | |
+ | |
+`)) | |
+ req, err := http.ReadRequest(br) | |
+ if err != nil { | |
+ t.Fatal("request", err) | |
+ } | |
+ code, err := handshaker.ReadHandshake(br, req) | |
+ if err != nil { | |
+ t.Errorf("handshake failed: %v", err) | |
+ } | |
+ if code != http.StatusSwitchingProtocols { | |
+ t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) | |
+ } | |
+ b := bytes.NewBuffer([]byte{}) | |
+ bw := bufio.NewWriter(b) | |
+ | |
+ config.Protocol = []string{"chat"} | |
+ | |
+ err = handshaker.AcceptHandshake(bw) | |
+ if err != nil { | |
+ t.Errorf("handshake response failed: %v", err) | |
+ } | |
+ expectedResponse := strings.Join([]string{ | |
+ "HTTP/1.1 101 Switching Protocols", | |
+ "Upgrade: websocket", | |
+ "Connection: Upgrade", | |
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", | |
+ "Sec-WebSocket-Protocol: chat", | |
+ "", ""}, "\r\n") | |
+ | |
+ if b.String() != expectedResponse { | |
+ t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) | |
+ } | |
+} | |
+ | |
func TestHybiClientHandshake(t *testing.T) { | |
b := bytes.NewBuffer([]byte{}) | |
bw := bufio.NewWriter(b) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment