MCPcopy Index your code
hub / github.com/coder/coder / WithWebsocketSupport

Function WithWebsocketSupport

tailnet/derp.go:24–76  ·  view source on GitHub ↗

WithWebsocketSupport returns an http.Handler that upgrades connections to the "derp" subprotocol to WebSockets and passes them to the DERP server. Taken from: https://github.com/tailscale/tailscale/blob/e3211ff88ba85435f70984cf67d9b353f3d650d8/cmd/derper/websocket.go#L21

(s *derp.Server, base http.Handler)

Source from the content-addressed store, hash-verified

22// passes them to the DERP server.
23// Taken from: https://github.com/tailscale/tailscale/blob/e3211ff88ba85435f70984cf67d9b353f3d650d8/cmd/derper/websocket.go#L21
24func WithWebsocketSupport(s *derp.Server, base http.Handler) (http.Handler, func()) {
25 var mu sync.Mutex
26 var waitGroup sync.WaitGroup
27 ctx, cancelFunc := context.WithCancel(context.Background())
28
29 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
30 up := strings.ToLower(r.Header.Get("Upgrade"))
31
32 // Very early versions of Tailscale set "Upgrade: WebSocket" but didn't actually
33 // speak WebSockets (they still assumed DERP's binary framing). So to distinguish
34 // clients that actually want WebSockets, look for an explicit "derp" subprotocol.
35 if up != "websocket" || !strings.Contains(r.Header.Get("Sec-Websocket-Protocol"), "derp") {
36 base.ServeHTTP(w, r)
37 return
38 }
39
40 mu.Lock()
41 if ctx.Err() != nil {
42 mu.Unlock()
43 return
44 }
45 waitGroup.Add(1)
46 mu.Unlock()
47 defer waitGroup.Done()
48 c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
49 Subprotocols: []string{"derp"},
50 OriginPatterns: []string{"*"},
51 // Disable compression because we transmit WireGuard messages that
52 // are not compressible.
53 // Additionally, Safari has a broken implementation of compression
54 // (see https://github.com/nhooyr/websocket/issues/218) that makes
55 // enabling it actively harmful.
56 CompressionMode: websocket.CompressionDisabled,
57 })
58 if err != nil {
59 log.Printf("websocket.Accept: %v", err)
60 return
61 }
62 defer c.Close(websocket.StatusInternalError, "closing")
63 if c.Subprotocol() != "derp" {
64 c.Close(websocket.StatusPolicyViolation, "client must speak the derp subprotocol")
65 return
66 }
67 wc := websocket.NetConn(ctx, c, websocket.MessageBinary)
68 brw := bufio.NewReadWriter(bufio.NewReader(wc), bufio.NewWriter(wc))
69 s.Accept(ctx, wc, brw, r.RemoteAddr)
70 }), func() {
71 cancelFunc()
72 mu.Lock()
73 waitGroup.Wait()
74 mu.Unlock()
75 }
76}
77
78type DERPMapRewriter interface {
79 RewriteDERPMap(derpMap *tailcfg.DERPMap)

Callers 5

NewFunction · 0.92
TestDERPFunction · 0.92
newDerpServerFunction · 0.92
RunDERPOnlyWebSocketsFunction · 0.92
NewFunction · 0.92

Calls 11

ErrMethod · 0.80
GetMethod · 0.65
AddMethod · 0.65
CloseMethod · 0.65
WaitMethod · 0.65
ContainsMethod · 0.45
ServeHTTPMethod · 0.45
LockMethod · 0.45
UnlockMethod · 0.45
DoneMethod · 0.45
AcceptMethod · 0.45

Tested by 1

TestDERPFunction · 0.74