client_js.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. // Copyright (c) 2022 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 controlhttp
  5. import (
  6. "context"
  7. "encoding/base64"
  8. "net"
  9. "net/url"
  10. "nhooyr.io/websocket"
  11. "tailscale.com/control/controlbase"
  12. "tailscale.com/net/dnscache"
  13. "tailscale.com/types/key"
  14. )
  15. // Variant of Dial that tunnels the request over WebSockets, since we cannot do
  16. // bi-directional communication over an HTTP connection when in JS.
  17. func Dial(ctx context.Context, addr string, machineKey key.MachinePrivate, controlKey key.MachinePublic, protocolVersion uint16, dialer dnscache.DialContextFunc) (*controlbase.Conn, error) {
  18. init, cont, err := controlbase.ClientDeferred(machineKey, controlKey, protocolVersion)
  19. if err != nil {
  20. return nil, err
  21. }
  22. host, _, err := net.SplitHostPort(addr)
  23. if err != nil {
  24. return nil, err
  25. }
  26. wsScheme := "wss"
  27. wsHost := host
  28. if host == "localhost" {
  29. wsScheme = "ws"
  30. wsHost = addr
  31. }
  32. wsURL := &url.URL{
  33. Scheme: wsScheme,
  34. Host: wsHost,
  35. Path: serverUpgradePath,
  36. // Can't set HTTP headers on the websocket request, so we have to to send
  37. // the handshake via an HTTP header.
  38. RawQuery: url.Values{
  39. handshakeHeaderName: []string{base64.StdEncoding.EncodeToString(init)},
  40. }.Encode(),
  41. }
  42. wsConn, _, err := websocket.Dial(ctx, wsURL.String(), &websocket.DialOptions{
  43. Subprotocols: []string{upgradeHeaderValue},
  44. })
  45. if err != nil {
  46. return nil, err
  47. }
  48. netConn := websocket.NetConn(context.Background(), wsConn, websocket.MessageBinary)
  49. cbConn, err := cont(ctx, netConn)
  50. if err != nil {
  51. netConn.Close()
  52. return nil, err
  53. }
  54. return cbConn, nil
  55. }