package sftp
import (
"encoding"
"fmt"
"io"
"sync"
)
type conn struct {
io .Reader
io .WriteCloser
alloc *allocator
sync .Mutex
}
func (c *conn ) recvPacket (orderID uint32 ) (uint8 , []byte , error ) {
return recvPacket (c , c .alloc , orderID )
}
func (c *conn ) sendPacket (m encoding .BinaryMarshaler ) error {
c .Lock ()
defer c .Unlock ()
return sendPacket (c , m )
}
func (c *conn ) Close () error {
c .Lock ()
defer c .Unlock ()
return c .WriteCloser .Close ()
}
type clientConn struct {
conn
wg sync .WaitGroup
sync .Mutex
inflight map [uint32 ]chan <- result
closed chan struct {}
err error
}
func (c *clientConn ) Wait () error {
<-c .closed
return c .err
}
func (c *clientConn ) Close () error {
defer c .wg .Wait ()
return c .conn .Close ()
}
func (c *clientConn ) recv () error {
defer c .conn .Close ()
for {
typ , data , err := c .recvPacket (0 )
if err != nil {
return err
}
sid , _ , err := unmarshalUint32Safe (data )
if err != nil {
return err
}
ch , ok := c .getChannel (sid )
if !ok {
return fmt .Errorf ("sid not found: %d" , sid )
}
ch <- result {typ : typ , data : data }
}
}
func (c *clientConn ) putChannel (ch chan <- result , sid uint32 ) bool {
c .Lock ()
defer c .Unlock ()
select {
case <- c .closed :
ch <- result {err : ErrSSHFxConnectionLost }
return false
default :
}
c .inflight [sid ] = ch
return true
}
func (c *clientConn ) getChannel (sid uint32 ) (chan <- result , bool ) {
c .Lock ()
defer c .Unlock ()
ch , ok := c .inflight [sid ]
delete (c .inflight , sid )
return ch , ok
}
type result struct {
typ byte
data []byte
err error
}
type idmarshaler interface {
id() uint32
encoding .BinaryMarshaler
}
func (c *clientConn ) sendPacket (ch chan result , p idmarshaler ) (byte , []byte , error ) {
if cap (ch ) < 1 {
ch = make (chan result , 1 )
}
c .dispatchRequest (ch , p )
s := <-ch
return s .typ , s .data , s .err
}
func (c *clientConn ) dispatchRequest (ch chan <- result , p idmarshaler ) {
sid := p .id ()
if !c .putChannel (ch , sid ) {
return
}
if err := c .conn .sendPacket (p ); err != nil {
if ch , ok := c .getChannel (sid ); ok {
ch <- result {err : err }
}
}
}
func (c *clientConn ) broadcastErr (err error ) {
c .Lock ()
defer c .Unlock ()
bcastRes := result {err : ErrSSHFxConnectionLost }
for sid , ch := range c .inflight {
ch <- bcastRes
c .inflight [sid ] = make (chan <- result , 1 )
}
c .err = err
close (c .closed )
}
type serverConn struct {
conn
}
func (s *serverConn ) sendError (id uint32 , err error ) error {
return s .sendPacket (statusFromError (id , err ))
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .