mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 02:01:14 +01:00 
			
		
		
		
	This makes wsconn.Conns somewhat present reasonably when they are the client of an http.Request, rather than just put a placeholder in that field. Updates tailscale/corp#13777 Signed-off-by: David Anderson <danderson@tailscale.com>
		
			
				
	
	
		
			62 lines
		
	
	
		
			1.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			62 lines
		
	
	
		
			1.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
package controlhttp
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/base64"
 | 
						|
	"errors"
 | 
						|
	"net"
 | 
						|
	"net/url"
 | 
						|
 | 
						|
	"nhooyr.io/websocket"
 | 
						|
	"tailscale.com/control/controlbase"
 | 
						|
	"tailscale.com/net/wsconn"
 | 
						|
)
 | 
						|
 | 
						|
// Variant of Dial that tunnels the request over WebSockets, since we cannot do
 | 
						|
// bi-directional communication over an HTTP connection when in JS.
 | 
						|
func (d *Dialer) Dial(ctx context.Context) (*ClientConn, error) {
 | 
						|
	if d.Hostname == "" {
 | 
						|
		return nil, errors.New("required Dialer.Hostname empty")
 | 
						|
	}
 | 
						|
 | 
						|
	init, cont, err := controlbase.ClientDeferred(d.MachineKey, d.ControlKey, d.ProtocolVersion)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	wsScheme := "wss"
 | 
						|
	host := d.Hostname
 | 
						|
	// If using a custom control server (on a non-standard port), prefer that.
 | 
						|
	// This mirrors the port selection in newNoiseClient from noise.go.
 | 
						|
	if d.HTTPPort != "" && d.HTTPPort != "80" && d.HTTPSPort == "443" {
 | 
						|
		wsScheme = "ws"
 | 
						|
		host = net.JoinHostPort(host, d.HTTPPort)
 | 
						|
	}
 | 
						|
	wsURL := &url.URL{
 | 
						|
		Scheme: wsScheme,
 | 
						|
		Host:   host,
 | 
						|
		Path:   serverUpgradePath,
 | 
						|
		// Can't set HTTP headers on the websocket request, so we have to to send
 | 
						|
		// the handshake via an HTTP header.
 | 
						|
		RawQuery: url.Values{
 | 
						|
			handshakeHeaderName: []string{base64.StdEncoding.EncodeToString(init)},
 | 
						|
		}.Encode(),
 | 
						|
	}
 | 
						|
	wsConn, _, err := websocket.Dial(ctx, wsURL.String(), &websocket.DialOptions{
 | 
						|
		Subprotocols: []string{upgradeHeaderValue},
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	netConn := wsconn.NetConn(context.Background(), wsConn, websocket.MessageBinary, wsURL.String())
 | 
						|
	cbConn, err := cont(ctx, netConn)
 | 
						|
	if err != nil {
 | 
						|
		netConn.Close()
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &ClientConn{Conn: cbConn}, nil
 | 
						|
}
 |