websocket.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "bufio"
  7. "expvar"
  8. "log"
  9. "net/http"
  10. "strings"
  11. "nhooyr.io/websocket"
  12. "tailscale.com/derp"
  13. "tailscale.com/derp/wsconn"
  14. )
  15. var counterWebSocketAccepts = expvar.NewInt("derp_websocket_accepts")
  16. // addWebSocketSupport returns a Handle wrapping base that adds WebSocket server support.
  17. func addWebSocketSupport(s *derp.Server, base http.Handler) http.Handler {
  18. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  19. up := strings.ToLower(r.Header.Get("Upgrade"))
  20. // Very early versions of Tailscale set "Upgrade: WebSocket" but didn't actually
  21. // speak WebSockets (they still assumed DERP's binary framining). So to distinguish
  22. // clients that actually want WebSockets, look for an explicit "derp" subprotocol.
  23. if up != "websocket" || !strings.Contains(r.Header.Get("Sec-Websocket-Protocol"), "derp") {
  24. base.ServeHTTP(w, r)
  25. return
  26. }
  27. c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
  28. Subprotocols: []string{"derp"},
  29. OriginPatterns: []string{"*"},
  30. })
  31. if err != nil {
  32. log.Printf("websocket.Accept: %v", err)
  33. return
  34. }
  35. defer c.Close(websocket.StatusInternalError, "closing")
  36. if c.Subprotocol() != "derp" {
  37. c.Close(websocket.StatusPolicyViolation, "client must speak the derp subprotocol")
  38. return
  39. }
  40. counterWebSocketAccepts.Add(1)
  41. wc := wsconn.New(c)
  42. brw := bufio.NewReadWriter(bufio.NewReader(wc), bufio.NewWriter(wc))
  43. s.Accept(wc, brw, r.RemoteAddr)
  44. })
  45. }