mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 22:34:02 +03:00
FILE / ScuroNeko/mtg
mtglib/conns.go
Исходный файл и его история в репозитории.
162 lines
3.0 KiB
Go
162 lines
3.0 KiB
Go
package mtglib
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/9seconds/mtg/v2/essentials"
|
|
"github.com/pires/go-proxyproto"
|
|
)
|
|
|
|
type connTraffic struct {
|
|
essentials.Conn
|
|
|
|
streamID string
|
|
stream EventStream
|
|
ctx context.Context
|
|
}
|
|
|
|
func (c connTraffic) Read(b []byte) (int, error) {
|
|
n, err := c.Conn.Read(b)
|
|
|
|
if n > 0 {
|
|
c.stream.Send(c.ctx, NewEventTraffic(c.streamID, uint(n), true))
|
|
}
|
|
|
|
return n, err //nolint: wrapcheck
|
|
}
|
|
|
|
func (c connTraffic) Write(b []byte) (int, error) {
|
|
n, err := c.Conn.Write(b)
|
|
|
|
if n > 0 {
|
|
c.stream.Send(c.ctx, NewEventTraffic(c.streamID, uint(n), false))
|
|
}
|
|
|
|
return n, err //nolint: wrapcheck
|
|
}
|
|
|
|
type connRewind struct {
|
|
essentials.Conn
|
|
|
|
buf bytes.Buffer
|
|
active io.Reader
|
|
}
|
|
|
|
func (c *connRewind) Read(p []byte) (int, error) {
|
|
return c.active.Read(p)
|
|
}
|
|
|
|
func (c *connRewind) Rewind() {
|
|
c.active = io.MultiReader(&c.buf, c.Conn)
|
|
}
|
|
|
|
func newConnRewind(conn essentials.Conn) *connRewind {
|
|
rv := &connRewind{
|
|
Conn: conn,
|
|
}
|
|
rv.active = io.TeeReader(conn, &rv.buf)
|
|
|
|
return rv
|
|
}
|
|
|
|
type connProxyProtocol struct {
|
|
essentials.Conn
|
|
|
|
sourceAddr net.Addr
|
|
headersWritten bool
|
|
}
|
|
|
|
func (c *connProxyProtocol) Write(p []byte) (int, error) {
|
|
if !c.headersWritten {
|
|
headers := proxyproto.HeaderProxyFromAddrs(2, c.sourceAddr, c.RemoteAddr())
|
|
|
|
toSend, err := headers.Format()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if _, err := c.Conn.Write(toSend); err != nil {
|
|
return 0, fmt.Errorf("cannot send proxy protocol header: %w", err)
|
|
}
|
|
|
|
c.headersWritten = true
|
|
}
|
|
|
|
return c.Conn.Write(p)
|
|
}
|
|
|
|
func newConnProxyProtocol(source, target essentials.Conn) *connProxyProtocol {
|
|
return &connProxyProtocol{
|
|
Conn: target,
|
|
sourceAddr: source.RemoteAddr(),
|
|
}
|
|
}
|
|
|
|
// idleTracker is a shared idle tracker for a pair of relay connections.
|
|
// Both directions update the same timestamp so that activity in one direction
|
|
// prevents the other (idle) direction from timing out.
|
|
type idleTracker struct {
|
|
lastActive atomic.Pointer[time.Time]
|
|
timeout time.Duration
|
|
}
|
|
|
|
func newIdleTracker(timeout time.Duration) *idleTracker {
|
|
t := &idleTracker{timeout: timeout}
|
|
t.touch()
|
|
|
|
return t
|
|
}
|
|
|
|
func (t *idleTracker) touch() {
|
|
stamp := time.Now()
|
|
t.lastActive.Store(&stamp)
|
|
}
|
|
|
|
func (t *idleTracker) isIdle() bool {
|
|
return time.Since(*t.lastActive.Load()) >= t.timeout
|
|
}
|
|
|
|
type connIdleTimeout struct {
|
|
essentials.Conn
|
|
|
|
tracker *idleTracker
|
|
}
|
|
|
|
func (c connIdleTimeout) Read(b []byte) (int, error) {
|
|
var netErr net.Error
|
|
|
|
for {
|
|
c.SetReadDeadline(time.Now().Add(c.tracker.timeout)) //nolint: errcheck
|
|
|
|
n, err := c.Conn.Read(b)
|
|
|
|
switch {
|
|
case err == nil:
|
|
c.tracker.touch()
|
|
return n, nil
|
|
case errors.As(err, &netErr) && netErr.Timeout() && !c.tracker.isIdle():
|
|
continue
|
|
}
|
|
|
|
return n, err
|
|
}
|
|
}
|
|
|
|
func (c connIdleTimeout) Write(b []byte) (int, error) {
|
|
c.SetWriteDeadline(time.Now().Add(c.tracker.timeout)) //nolint: errcheck
|
|
|
|
n, err := c.Conn.Write(b)
|
|
if n > 0 {
|
|
c.tracker.touch()
|
|
}
|
|
|
|
return n, err //nolint: wrapcheck
|
|
}
|