telebit/mplexer/packer/telebit.go

133 lines
2.9 KiB
Go
Raw Normal View History

2020-05-21 05:30:24 +00:00
package packer
2020-05-21 10:29:05 +00:00
import (
"errors"
"fmt"
"io"
"net"
"time"
)
2020-05-21 05:30:24 +00:00
// Note: 64k is the TCP max, but 1460b is the 100mbit Ethernet max (1500 MTU - overhead),
// but 1Gbit Ethernet (Jumbo frame) has an 9000b MTU
// Nerds posting benchmarks on SO show that 8k seems about right,
// but even 1024b could work well.
var defaultBufferSize = 8192
// ErrBadGateway means that the target did not accept the connection
var ErrBadGateway = errors.New("EBADGATEWAY")
2020-05-21 10:29:05 +00:00
// A Handler routes, proxies, terminates, or responds to a net.Conn.
type Handler interface {
Serve(*Conn) error
}
type HandlerFunc func(*Conn) error
// Serve calls f(conn).
func (f HandlerFunc) Serve(conn *Conn) error {
return f(conn)
}
// NewForwarder creates a handler that port-forwards to a target
func NewForwarder(target string, timeout time.Duration) HandlerFunc {
return func(client *Conn) error {
tconn, err := net.Dial("tcp", target)
if nil != err {
return err
}
return Forward(client, tconn, timeout)
}
}
// Forward port-forwards a relay (websocket) client to a target (local) server
func Forward(client *Conn, target net.Conn, timeout time.Duration) error {
// Something like ReadAhead(size) should signal
// to read and send up to `size` bytes without waiting
// for a response - since we can't signal 'non-read' as
// is the normal operation of tcp... or can we?
// And how do we distinguish idle from dropped?
// Maybe this should have been a udp protocol???
defer client.Close()
defer target.Close()
srcCh := make(chan []byte)
dstCh := make(chan []byte)
srcErrCh := make(chan error)
dstErrCh := make(chan error)
// Source (Relay) Read Channel
go func() {
for {
b := make([]byte, defaultBufferSize)
n, err := client.Read(b)
if n > 0 {
srcCh <- b[:n]
}
if nil != err {
// TODO let client log this server-side error (unless EOF)
// (nil here because we probably can't send the error to the relay)
srcErrCh <- err
break
}
}
}()
// Target (Local) Read Channel
go func() {
for {
b := make([]byte, defaultBufferSize)
n, err := target.Read(b)
if n > 0 {
dstCh <- b[:n]
}
if nil != err {
if io.EOF == err {
err = nil
}
dstErrCh <- err
break
}
}
}()
var err error = nil
for {
select {
// TODO do we need a context here?
//case <-ctx.Done():
// break
case b := <-srcCh:
client.SetDeadline(time.Now().Add(timeout))
_, err = target.Write(b)
if nil != err {
fmt.Printf("write to target failed: %q", err.Error())
break
}
case b := <-dstCh:
target.SetDeadline(time.Now().Add(timeout))
_, err = client.Write(b)
if nil != err {
fmt.Printf("write to remote failed: %q", err.Error())
break
}
case err = <-srcErrCh:
if nil != err {
fmt.Printf("read from remote failed: %q", err.Error())
}
break
case err = <-dstErrCh:
if nil != err {
fmt.Printf("read from target failed: %q", err.Error())
}
break
}
}
client.Close()
return err
}