forked from rezmoss/https-traffic-inspector
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebsocket.go
More file actions
59 lines (53 loc) · 1.68 KB
/
Copy pathwebsocket.go
File metadata and controls
59 lines (53 loc) · 1.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package main
import (
"bufio"
"io"
"net"
"net/http"
)
// spliceWebSocket performs a WebSocket upgrade handshake with upConn, writes
// the server response to clientW, then splices raw bytes bidirectionally
// between upConn and clientConn until either side closes.
//
// clientConn is the underlying connection (e.g. *tls.Conn) used for the raw
// splice phase. clientW (its bufio wrapper) is only used for the HTTP
// handshake response; raw WebSocket frames bypass it to avoid stale buffering.
func spliceWebSocket(upConn net.Conn, req *http.Request, clientConn net.Conn, clientW *bufio.Writer, clientR io.Reader) {
if err := req.Write(upConn); err != nil {
writeConnError(clientW, http.StatusBadGateway, err.Error())
clientW.Flush() //nolint:errcheck
return
}
upReader := bufio.NewReader(upConn)
resp, err := http.ReadResponse(upReader, req)
if err != nil {
writeConnError(clientW, http.StatusBadGateway, err.Error())
clientW.Flush() //nolint:errcheck
return
}
resp.Body.Close()
if err := resp.Write(clientW); err != nil {
return
}
if err := clientW.Flush(); err != nil {
return
}
if resp.StatusCode != http.StatusSwitchingProtocols {
return
}
// Splice bidirectionally using the raw connection so WebSocket frames are
// not buffered inside bufio.Writer (which would stall delivery until flush).
errc := make(chan struct{}, 2)
go func() {
io.Copy(upConn, clientR) //nolint:errcheck
upConn.Close() // signal upstream EOF; unblocks the other goroutine
errc <- struct{}{}
}()
go func() {
io.Copy(clientConn, upReader) //nolint:errcheck
clientConn.Close() // signal client EOF; unblocks goroutine 1
errc <- struct{}{}
}()
<-errc
<-errc
}