@ -23,8 +23,11 @@ import (
"tailscale.com/client/tailscale/apitype"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/feature/taildrop"
"tailscale.com/feature/taildrop"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/tailcfg"
"tailscale.com/tailcfg"
"tailscale.com/util/progresstracking"
"tailscale.com/util/rands"
)
)
// Compile-time check that jsFileOps implements taildrop.FileOps.
// Compile-time check that jsFileOps implements taildrop.FileOps.
@ -78,7 +81,8 @@ func (i *jsIPN) listFileTargets() js.Value {
} )
} )
}
}
// sendFile sends data as filename to the peer identified by stableNodeID.
// sendFile sends data as filename to the peer identified by stableNodeID,
// reporting progress via notifyOutgoingFiles callbacks roughly once per second.
func ( i * jsIPN ) sendFile ( stableNodeID , filename string , data js . Value ) js . Value {
func ( i * jsIPN ) sendFile ( stableNodeID , filename string , data js . Value ) js . Value {
return makePromise ( func ( ) ( any , error ) {
return makePromise ( func ( ) ( any , error ) {
ext , err := i . taildropExt ( )
ext , err := i . taildropExt ( )
@ -105,20 +109,46 @@ func (i *jsIPN) sendFile(stableNodeID, filename string, data js.Value) js.Value
}
}
b := make ( [ ] byte , data . Get ( "byteLength" ) . Int ( ) )
b := make ( [ ] byte , data . Get ( "byteLength" ) . Int ( ) )
js . CopyBytesToGo ( b , data )
js . CopyBytesToGo ( b , data )
req , err := http . NewRequest ( "PUT" , dstURL . String ( ) + "/v0/put/" + url . PathEscape ( filename ) , bytes . NewReader ( b ) )
outgoing := & ipn . OutgoingFile {
ID : rands . HexString ( 30 ) ,
PeerID : tailcfg . StableNodeID ( stableNodeID ) ,
Name : filename ,
DeclaredSize : int64 ( len ( b ) ) ,
Started : time . Now ( ) ,
}
updates := map [ string ] * ipn . OutgoingFile { outgoing . ID : outgoing }
// Report final state (success or failure) when the function returns.
var sendErr error
defer func ( ) {
outgoing . Finished = true
outgoing . Succeeded = sendErr == nil
ext . UpdateOutgoingFiles ( updates )
} ( )
body := progresstracking . NewReader ( bytes . NewReader ( b ) , time . Second , func ( n int , _ error ) {
outgoing . Sent = int64 ( n )
ext . UpdateOutgoingFiles ( updates )
} )
req , err := http . NewRequest ( "PUT" , dstURL . String ( ) + "/v0/put/" + url . PathEscape ( filename ) , body )
if err != nil {
if err != nil {
sendErr = err
return nil , err
return nil , err
}
}
req . ContentLength = int64 ( len ( b ) )
req . ContentLength = int64 ( len ( b ) )
client := & http . Client { Transport : i . lb . Dialer ( ) . PeerAPITransport ( ) }
client := & http . Client { Transport : i . lb . Dialer ( ) . PeerAPITransport ( ) }
resp , err := client . Do ( req )
resp , err := client . Do ( req )
if err != nil {
if err != nil {
sendErr = err
return nil , err
return nil , err
}
}
defer resp . Body . Close ( )
defer resp . Body . Close ( )
if resp . StatusCode != http . StatusOK && resp . StatusCode != http . StatusCreated {
if resp . StatusCode != http . StatusOK && resp . StatusCode != http . StatusCreated {
body , _ := io . ReadAll ( resp . Body )
respBody , _ := io . ReadAll ( resp . Body )
return nil , fmt . Errorf ( "send file: %s: %s" , resp . Status , bytes . TrimSpace ( body ) )
sendErr = fmt . Errorf ( "send file: %s: %s" , resp . Status , bytes . TrimSpace ( respBody ) )
return nil , sendErr
}
}
return nil , nil
return nil , nil
} )
} )