@ -35,7 +35,7 @@ import (
type noiseConn struct {
type noiseConn struct {
* controlbase . Conn
* controlbase . Conn
id int
id int
pool * n oiseClient
pool * N oiseClient
h2cc * http2 . ClientConn
h2cc * http2 . ClientConn
readHeaderOnce sync . Once // guards init of reader field
readHeaderOnce sync . Once // guards init of reader field
@ -135,9 +135,9 @@ func (c *noiseConn) Close() error {
return nil
return nil
}
}
// n oiseClient provides a http.Client to connect to tailcontrol over
// N oiseClient provides a http.Client to connect to tailcontrol over
// the ts2021 protocol.
// the ts2021 protocol.
type n oiseClient struct {
type N oiseClient struct {
// Client is an HTTP client to talk to the coordination server.
// Client is an HTTP client to talk to the coordination server.
// It automatically makes a new Noise connection as needed.
// It automatically makes a new Noise connection as needed.
// It does not support node key proofs. To do that, call
// It does not support node key proofs. To do that, call
@ -175,11 +175,11 @@ type noiseClient struct {
connPool map [ int ] * noiseConn // active connections not yet closed; see noiseConn.Close
connPool map [ int ] * noiseConn // active connections not yet closed; see noiseConn.Close
}
}
// n ewNoiseClient returns a new noiseClient for the provided server and machine key.
// N ewNoiseClient returns a new noiseClient for the provided server and machine key.
// serverURL is of the form https://<host>:<port> (no trailing slash).
// serverURL is of the form https://<host>:<port> (no trailing slash).
//
//
// dialPlan may be nil
// dialPlan may be nil
func n ewNoiseClient( privKey key . MachinePrivate , serverPubKey key . MachinePublic , serverURL string , dialer * tsdial . Dialer , dialPlan func ( ) * tailcfg . ControlDialPlan ) ( * n oiseClient, error ) {
func N ewNoiseClient( privKey key . MachinePrivate , serverPubKey key . MachinePublic , serverURL string , dialer * tsdial . Dialer , dialPlan func ( ) * tailcfg . ControlDialPlan ) ( * N oiseClient, error ) {
u , err := url . Parse ( serverURL )
u , err := url . Parse ( serverURL )
if err != nil {
if err != nil {
return nil , err
return nil , err
@ -200,7 +200,7 @@ func newNoiseClient(privKey key.MachinePrivate, serverPubKey key.MachinePublic,
httpPort = "80"
httpPort = "80"
httpsPort = "443"
httpsPort = "443"
}
}
np := & n oiseClient{
np := & N oiseClient{
serverPubKey : serverPubKey ,
serverPubKey : serverPubKey ,
privKey : privKey ,
privKey : privKey ,
host : u . Hostname ( ) ,
host : u . Hostname ( ) ,
@ -227,7 +227,30 @@ func newNoiseClient(privKey key.MachinePrivate, serverPubKey key.MachinePublic,
return np , nil
return np , nil
}
}
func ( nc * noiseClient ) getConn ( ctx context . Context ) ( * noiseConn , error ) {
// GetSingleUseRoundTripper returns a RoundTripper that can be only be used once
// (and must be used once) to make a single HTTP request over the noise channel
// to the coordination server.
//
// In addition to the RoundTripper, it returns the HTTP/2 channel's early noise
// payload, if any.
func ( nc * NoiseClient ) GetSingleUseRoundTripper ( ctx context . Context ) ( http . RoundTripper , * tailcfg . EarlyNoise , error ) {
for tries := 0 ; tries < 3 ; tries ++ {
conn , err := nc . getConn ( ctx )
if err != nil {
return nil , nil , err
}
earlyPayloadMaybeNil , err := conn . getEarlyPayload ( ctx )
if err != nil {
return nil , nil , err
}
if conn . h2cc . ReserveNewRequest ( ) {
return conn , earlyPayloadMaybeNil , nil
}
}
return nil , nil , errors . New ( "[unexpected] failed to reserve a request on a connection" )
}
func ( nc * NoiseClient ) getConn ( ctx context . Context ) ( * noiseConn , error ) {
nc . mu . Lock ( )
nc . mu . Lock ( )
if last := nc . last ; last != nil && last . canTakeNewRequest ( ) {
if last := nc . last ; last != nil && last . canTakeNewRequest ( ) {
nc . mu . Unlock ( )
nc . mu . Unlock ( )
@ -242,7 +265,7 @@ func (nc *noiseClient) getConn(ctx context.Context) (*noiseConn, error) {
return conn , nil
return conn , nil
}
}
func ( nc * n oiseClient) RoundTrip ( req * http . Request ) ( * http . Response , error ) {
func ( nc * N oiseClient) RoundTrip ( req * http . Request ) ( * http . Response , error ) {
ctx := req . Context ( )
ctx := req . Context ( )
conn , err := nc . getConn ( ctx )
conn , err := nc . getConn ( ctx )
if err != nil {
if err != nil {
@ -253,7 +276,7 @@ func (nc *noiseClient) RoundTrip(req *http.Request) (*http.Response, error) {
// connClosed removes the connection with the provided ID from the pool
// connClosed removes the connection with the provided ID from the pool
// of active connections.
// of active connections.
func ( nc * n oiseClient) connClosed ( id int ) {
func ( nc * N oiseClient) connClosed ( id int ) {
nc . mu . Lock ( )
nc . mu . Lock ( )
defer nc . mu . Unlock ( )
defer nc . mu . Unlock ( )
conn := nc . connPool [ id ]
conn := nc . connPool [ id ]
@ -267,7 +290,7 @@ func (nc *noiseClient) connClosed(id int) {
// Close closes all the underlying noise connections.
// Close closes all the underlying noise connections.
// It is a no-op and returns nil if the connection is already closed.
// It is a no-op and returns nil if the connection is already closed.
func ( nc * n oiseClient) Close ( ) error {
func ( nc * N oiseClient) Close ( ) error {
nc . mu . Lock ( )
nc . mu . Lock ( )
conns := nc . connPool
conns := nc . connPool
nc . connPool = nil
nc . connPool = nil
@ -284,7 +307,7 @@ func (nc *noiseClient) Close() error {
// dial opens a new connection to tailcontrol, fetching the server noise key
// dial opens a new connection to tailcontrol, fetching the server noise key
// if not cached.
// if not cached.
func ( nc * n oiseClient) dial ( ) ( * noiseConn , error ) {
func ( nc * N oiseClient) dial ( ) ( * noiseConn , error ) {
nc . mu . Lock ( )
nc . mu . Lock ( )
connID := nc . nextID
connID := nc . nextID
nc . nextID ++
nc . nextID ++
@ -369,7 +392,7 @@ func (nc *noiseClient) dial() (*noiseConn, error) {
return ncc , nil
return ncc , nil
}
}
func ( nc * n oiseClient) post ( ctx context . Context , path string , body any ) ( * http . Response , error ) {
func ( nc * N oiseClient) post ( ctx context . Context , path string , body any ) ( * http . Response , error ) {
jbody , err := json . Marshal ( body )
jbody , err := json . Marshal ( body )
if err != nil {
if err != nil {
return nil , err
return nil , err