websocket.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package main
  4. import (
  5. "bufio"
  6. "expvar"
  7. "log"
  8. "net/http"
  9. "strings"
  10. "github.com/coder/websocket"
  11. "tailscale.com/derp/derpserver"
  12. "tailscale.com/net/wsconn"
  13. )
  14. var counterWebSocketAccepts = expvar.NewInt("derp_websocket_accepts")
  15. // addWebSocketSupport returns a Handle wrapping base that adds WebSocket server support.
  16. func addWebSocketSupport(s *derpserver.Server, base http.Handler) http.Handler {
  17. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  18. up := strings.ToLower(r.Header.Get("Upgrade"))
  19. // Very early versions of Tailscale set "Upgrade: WebSocket" but didn't actually
  20. // speak WebSockets (they still assumed DERP's binary framing). So to distinguish
  21. // clients that actually want WebSockets, look for an explicit "derp" subprotocol.
  22. if up != "websocket" || !strings.Contains(r.Header.Get("Sec-Websocket-Protocol"), "derp") {
  23. base.ServeHTTP(w, r)
  24. return
  25. }
  26. c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
  27. Subprotocols: []string{"derp"},
  28. OriginPatterns: []string{"*"},
  29. // Disable compression because we transmit WireGuard messages that
  30. // are not compressible.
  31. // Additionally, Safari has a broken implementation of compression
  32. // (see https://github.com/nhooyr/websocket/issues/218) that makes
  33. // enabling it actively harmful.
  34. CompressionMode: websocket.CompressionDisabled,
  35. })
  36. if err != nil {
  37. log.Printf("websocket.Accept: %v", err)
  38. return
  39. }
  40. defer c.Close(websocket.StatusInternalError, "closing")
  41. if c.Subprotocol() != "derp" {
  42. c.Close(websocket.StatusPolicyViolation, "client must speak the derp subprotocol")
  43. return
  44. }
  45. counterWebSocketAccepts.Add(1)
  46. wc := wsconn.NetConn(r.Context(), c, websocket.MessageBinary, r.RemoteAddr)
  47. brw := bufio.NewReadWriter(bufio.NewReader(wc), bufio.NewWriter(wc))
  48. s.Accept(r.Context(), wc, brw, r.RemoteAddr)
  49. })
  50. }