Skip to content

Commit a365611

Browse files
committed
fix: correct WebSocket magic GUID in debugws tool
cmd/debugws/main.go had a wrong magic GUID suffix (5AB5F1A13B20 vs the correct C5AB0DC85B11 per RFC 6455). The production ws.go was always correct — only the debug tool was affected. A browser or real WS client connecting to debugws would fail the handshake.
1 parent f316266 commit a365611

1 file changed

Lines changed: 130 additions & 0 deletions

File tree

cmd/debugws/main.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"crypto/sha1"
6+
"encoding/base64"
7+
"net"
8+
"net/http"
9+
)
10+
11+
const magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
12+
13+
func main() {
14+
mux := http.NewServeMux()
15+
mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
16+
log.Printf("=== WS UPGRADE REQUEST ===")
17+
log.Printf("Method: %s", r.Method)
18+
log.Printf("URL: %s", r.URL.String())
19+
log.Printf("Headers:")
20+
for k, v := range r.Header {
21+
log.Printf(" %s: %s", k, v)
22+
}
23+
24+
key := r.Header.Get("Sec-WebSocket-Key")
25+
log.Printf("Key from header: %q", key)
26+
log.Printf("Key bytes: %x", []byte(key))
27+
28+
// Compute accept
29+
h := sha1.New()
30+
h.Write([]byte(key))
31+
h.Write([]byte(magicGUID))
32+
accept := base64.StdEncoding.EncodeToString(h.Sum(nil))
33+
log.Printf("Computed accept: %q", accept)
34+
35+
hj, ok := w.(http.Hijacker)
36+
if !ok {
37+
http.Error(w, "no hijack", 500)
38+
return
39+
}
40+
netConn, bufrw, err := hj.Hijack()
41+
if err != nil {
42+
http.Error(w, err.Error(), 500)
43+
return
44+
}
45+
defer netConn.Close()
46+
47+
resp := "HTTP/1.1 101 Switching Protocols\r\n" +
48+
"Upgrade: websocket\r\n" +
49+
"Connection: Upgrade\r\n" +
50+
"Sec-WebSocket-Accept: " + accept + "\r\n\r\n"
51+
52+
log.Printf("Sending response: %q", resp)
53+
54+
n, err := bufrw.WriteString(resp)
55+
log.Printf("Wrote %d bytes, err=%v", n, err)
56+
57+
err = bufrw.Flush()
58+
log.Printf("Flush err=%v", err)
59+
60+
// Now try to read
61+
for {
62+
header := make([]byte, 2)
63+
_, err := bufrw.Read(header)
64+
if err != nil {
65+
log.Printf("Read error: %v", err)
66+
return
67+
}
68+
log.Printf("Got frame header: %x (opcode=%d)", header, header[0]&0x0F)
69+
70+
masked := header[1]&0x80 != 0
71+
length := int64(header[1] & 0x7F)
72+
log.Printf("Masked=%v, length=%d", masked, length)
73+
74+
if length == 126 {
75+
ext := make([]byte, 2)
76+
bufrw.Read(ext)
77+
length = int64(ext[0])<<8 | int64(ext[1])
78+
} else if length == 127 {
79+
ext := make([]byte, 8)
80+
bufrw.Read(ext)
81+
for i := 0; i < 8; i++ {
82+
length = length<<8 | int64(ext[i])
83+
}
84+
}
85+
86+
var mask [4]byte
87+
if masked {
88+
bufrw.Read(mask[:])
89+
}
90+
91+
payload := make([]byte, length)
92+
bufrw.Read(payload)
93+
94+
if masked {
95+
for i := range payload {
96+
payload[i] ^= mask[i%4]
97+
}
98+
}
99+
log.Printf("Payload: %s", string(payload))
100+
101+
// Echo back
102+
out := make([]byte, 2+len(payload))
103+
out[0] = 0x81 // FIN + text
104+
out[1] = byte(len(payload))
105+
copy(out[2:], payload)
106+
bufrw.Write(out)
107+
bufrw.Flush()
108+
}
109+
})
110+
111+
// Serve a test page too
112+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
113+
w.Header().Set("Content-Type", "text/html")
114+
w.Write([]byte(`<!DOCTYPE html><html><body>
115+
<script>
116+
var ws = new WebSocket('ws://127.0.0.1:9976/ws');
117+
ws.onopen = function() { document.body.innerHTML += '<p>WS OPEN</p>'; };
118+
ws.onerror = function(e) { document.body.innerHTML += '<p>WS ERROR: ' + (e.message || '') + '</p>'; };
119+
ws.onclose = function(e) { document.body.innerHTML += '<p>WS CLOSE: code=' + e.code + '</p>'; };
120+
</script>
121+
</body></html>`))
122+
})
123+
124+
ln, err := net.Listen("tcp", "127.0.0.1:9976")
125+
if err != nil {
126+
log.Fatal(err)
127+
}
128+
log.Printf("Server on 127.0.0.1:9976")
129+
log.Fatal(http.Serve(ln, mux))
130+
}

0 commit comments

Comments
 (0)