telebit/mplexer/packer/encoder.go

137 lines
3.1 KiB
Go
Raw Normal View History

2020-05-19 09:35:22 +00:00
package packer
import (
"context"
"errors"
"io"
"sync"
)
2020-05-20 22:23:58 +00:00
// TODO: try to be more like encoding/csv, or more like encoding/pem and encoding/json?
// Encoder converts TCP to MPLEXY-TCP
2020-05-19 09:35:22 +00:00
type Encoder struct {
2020-05-20 22:23:58 +00:00
ctx context.Context
subctx context.Context
mux sync.Mutex
out io.WriteCloser
outErr chan error
bufferSize int
2020-05-19 09:35:22 +00:00
}
2020-05-20 22:23:58 +00:00
// NewEncoder returns an Encoder instance
func NewEncoder(ctx context.Context, wout io.WriteCloser) *Encoder {
2020-05-19 09:35:22 +00:00
enc := &Encoder{
2020-05-20 22:23:58 +00:00
ctx: ctx,
out: wout,
outErr: make(chan error),
bufferSize: defaultBufferSize,
2020-05-19 09:35:22 +00:00
}
return enc
}
2020-05-20 22:23:58 +00:00
// Run loops over a select of contexts and error channels
// to cancel and close south-side connections, if needed.
// TODO should this be pushed elsewhere to handled?
2020-05-19 09:35:22 +00:00
func (enc *Encoder) Run() error {
ctx, cancel := context.WithCancel(enc.ctx)
defer cancel()
enc.subctx = ctx
for {
select {
// TODO: do children respond to children cancelling?
case <-enc.ctx.Done():
// TODO
2020-05-20 22:23:58 +00:00
_ = enc.out.Close()
2020-05-19 09:35:22 +00:00
return errors.New("context cancelled")
2020-05-20 22:23:58 +00:00
case err := <-enc.outErr:
// if a write fails for one, it fail for all
2020-05-19 09:35:22 +00:00
return err
}
}
}
2020-05-20 22:23:58 +00:00
// Start will Run() the encoder in a goroutine
2020-05-19 09:35:22 +00:00
func (enc *Encoder) Start() error {
go enc.Run()
return nil
}
2020-05-20 22:23:58 +00:00
// Encode adds MPLEXY headers to raw net traffic, and is intended to be used on each client connection
func (enc *Encoder) Encode(rin io.ReadCloser, src Addr) error {
2020-05-19 09:35:22 +00:00
rx := make(chan []byte)
rxErr := make(chan error)
go func() {
for {
2020-05-20 22:23:58 +00:00
b := make([]byte, enc.bufferSize)
2020-05-19 09:35:22 +00:00
//fmt.Println("loopers gonna loop")
2020-05-20 22:23:58 +00:00
n, err := rin.Read(b)
2020-05-19 09:35:22 +00:00
if n > 0 {
rx <- b[:n]
}
if nil != err {
rxErr <- err
return
}
}
}()
for {
//fmt.Println("poopers gonna poop")
select {
// TODO, do we actually need ctx here?
// would it be sufficient to expect the reader to be closed by the caller instead?
case <-enc.ctx.Done():
// TODO: verify that closing the reader will cause the goroutine to be released
2020-05-20 22:23:58 +00:00
rin.Close()
2020-05-19 09:35:22 +00:00
return errors.New("cancelled by context")
case <-enc.subctx.Done():
2020-05-20 22:23:58 +00:00
rin.Close()
2020-05-19 09:35:22 +00:00
return errors.New("cancelled by context")
case b := <-rx:
header, _, err := Encode(src, Addr{}, "", b)
if nil != err {
2020-05-20 22:23:58 +00:00
rin.Close()
2020-05-19 09:35:22 +00:00
return err
}
2020-05-20 22:23:58 +00:00
_, err = enc.write(header, b)
2020-05-19 09:35:22 +00:00
if nil != err {
2020-05-20 22:23:58 +00:00
rin.Close()
2020-05-19 09:35:22 +00:00
return err
}
case err := <-rxErr:
// it can be assumed that err will close though, right?
2020-05-20 22:23:58 +00:00
rin.Close()
2020-05-19 09:35:22 +00:00
if io.EOF == err {
header, _, _ := Encode(src, Addr{scheme: "end"}, "", nil)
// ignore err, which may have already closed
2020-05-20 22:23:58 +00:00
_, _ = enc.write(header, nil)
2020-05-19 09:35:22 +00:00
return nil
}
header, _, _ := Encode(src, Addr{scheme: "error"}, "", []byte(err.Error()))
// ignore err, which may have already closed
2020-05-20 22:23:58 +00:00
_, _ = enc.write(header, nil)
2020-05-19 09:35:22 +00:00
return err
}
}
}
2020-05-20 22:23:58 +00:00
func (enc *Encoder) write(h, b []byte) (int, error) {
2020-05-19 09:35:22 +00:00
// mutex here so that we can get back error info
enc.mux.Lock()
2020-05-20 22:23:58 +00:00
var m int
n, err := enc.out.Write(h)
if nil == err && len(b) > 0 {
m, err = enc.out.Write(b)
}
2020-05-19 09:35:22 +00:00
enc.mux.Unlock()
if nil != err {
2020-05-20 22:23:58 +00:00
enc.outErr <- err
2020-05-19 09:35:22 +00:00
}
2020-05-20 22:23:58 +00:00
return n + m, err
2020-05-19 09:35:22 +00:00
}