Merge pull request #9 from 9seconds/buffers

Tune socket options
This commit is contained in:
Sergey Arkhipov
2018-06-24 17:25:20 +03:00
committed by GitHub
7 changed files with 59 additions and 87 deletions
+13 -3
View File
@@ -3,6 +3,7 @@ package client
import ( import (
"io" "io"
"net" "net"
"time"
"github.com/juju/errors" "github.com/juju/errors"
@@ -12,10 +13,19 @@ import (
"github.com/9seconds/mtg/wrappers" "github.com/9seconds/mtg/wrappers"
) )
const (
handshakeTimeout = 10 * time.Second
)
// DirectInit initializes client to access Telegram bypassing middleproxies. // DirectInit initializes client to access Telegram bypassing middleproxies.
func DirectInit(conn net.Conn, conf *config.Config) (*mtproto.ConnectionOpts, io.ReadWriteCloser, error) { func DirectInit(conn net.Conn, conf *config.Config) (*mtproto.ConnectionOpts, io.ReadWriteCloser, error) {
socket := wrappers.NewTimeoutRWC(conn, conf.TimeoutRead, conf.TimeoutWrite) if err := config.SetSocketOptions(conn); err != nil {
frame, err := obfuscated2.ExtractFrame(socket) return nil, nil, errors.Annotate(err, "Cannot set socket options")
}
conn.SetReadDeadline(time.Now().Add(handshakeTimeout)) // nolint: errcheck
frame, err := obfuscated2.ExtractFrame(conn)
conn.SetReadDeadline(time.Time{}) // nolint: errcheck
if err != nil { if err != nil {
return nil, nil, errors.Annotate(err, "Cannot extract frame") return nil, nil, errors.Annotate(err, "Cannot extract frame")
} }
@@ -26,7 +36,7 @@ func DirectInit(conn net.Conn, conf *config.Config) (*mtproto.ConnectionOpts, io
return nil, nil, errors.Annotate(err, "Cannot parse obfuscated frame") return nil, nil, errors.Annotate(err, "Cannot parse obfuscated frame")
} }
socket = wrappers.NewStreamCipherRWC(socket, obfs2.Encryptor, obfs2.Decryptor) socket := wrappers.NewStreamCipherRWC(conn, obfs2.Encryptor, obfs2.Decryptor)
return connOpts, socket, nil return connOpts, socket, nil
} }
+32 -6
View File
@@ -10,6 +10,15 @@ import (
"github.com/juju/errors" "github.com/juju/errors"
) )
// Buffer sizes define internal socket buffer sizes.
const (
BufferWriteSize = 32 * 1024
BufferReadSize = 32 * 1024
BufferSizeCopy = 32 * 1024
keepAlivePeriod = 20 * time.Second
)
// Config represents common configuration of mtg. // Config represents common configuration of mtg.
type Config struct { type Config struct {
Debug bool Debug bool
@@ -20,9 +29,6 @@ type Config struct {
PublicIPv6Port uint16 PublicIPv6Port uint16
StatsPort uint16 StatsPort uint16
TimeoutRead time.Duration
TimeoutWrite time.Duration
BindIP net.IP BindIP net.IP
PublicIPv4 net.IP PublicIPv4 net.IP
PublicIPv6 net.IP PublicIPv6 net.IP
@@ -85,7 +91,6 @@ func NewConfig(debug, verbose bool, // nolint: gocyclo
publicIPv4 net.IP, PublicIPv4Port uint16, publicIPv4 net.IP, PublicIPv4Port uint16,
publicIPv6 net.IP, publicIPv6Port uint16, publicIPv6 net.IP, publicIPv6Port uint16,
statsIP net.IP, statsPort uint16, statsIP net.IP, statsPort uint16,
timeoutRead, timeoutWrite time.Duration,
secret string) (*Config, error) { secret string) (*Config, error) {
if len(secret) != 32 { if len(secret) != 32 {
return nil, errors.New("Telegram demands secret of length 32") return nil, errors.New("Telegram demands secret of length 32")
@@ -136,10 +141,31 @@ func NewConfig(debug, verbose bool, // nolint: gocyclo
PublicIPv6Port: publicIPv6Port, PublicIPv6Port: publicIPv6Port,
StatsIP: statsIP, StatsIP: statsIP,
StatsPort: statsPort, StatsPort: statsPort,
TimeoutRead: timeoutRead,
TimeoutWrite: timeoutWrite,
Secret: secretBytes, Secret: secretBytes,
} }
return conf, nil return conf, nil
} }
// SetSocketOptions makes socket keepalive, sets buffer sizes
func SetSocketOptions(conn net.Conn) error {
socket := conn.(*net.TCPConn)
if err := socket.SetReadBuffer(BufferReadSize); err != nil {
return errors.Annotate(err, "Cannot set read buffer size")
}
if err := socket.SetWriteBuffer(BufferWriteSize); err != nil {
return errors.Annotate(err, "Cannot set write buffer size")
}
if err := socket.SetKeepAlive(true); err != nil {
return errors.Annotate(err, "Cannot make socket keepalive")
}
if err := socket.SetKeepAlivePeriod(keepAlivePeriod); err != nil {
return errors.Annotate(err, "Cannot set keepalive period")
}
if err := socket.SetNoDelay(true); err != nil {
return errors.Annotate(err, "Cannot activate nodelay for the socket")
}
return nil
}
-12
View File
@@ -69,17 +69,6 @@ var (
Default("3129"). Default("3129").
Uint16() Uint16()
readTimeout = app.Flag("read-timeout", "Socket read timeout.").
Short('r').
Envar("MTG_READ_TIMEOUT").
Default("30s").
Duration()
writeTimeout = app.Flag("write-timeout", "Socket write timeout.").
Short('w').
Envar("MTG_WRITE_TIMEOUT").
Default("30s").
Duration()
secret = app.Arg("secret", "Secret of this proxy.").Required().String() secret = app.Arg("secret", "Secret of this proxy.").Required().String()
) )
@@ -102,7 +91,6 @@ func main() {
*publicIPv4, *publicIPv4Port, *publicIPv4, *publicIPv4Port,
*publicIPv6, *publicIPv6Port, *publicIPv6, *publicIPv6Port,
*statsIP, *statsPort, *statsIP, *statsPort,
*readTimeout, *writeTimeout,
*secret, *secret,
) )
if err != nil { if err != nil {
+5 -3
View File
@@ -1,15 +1,17 @@
package proxy package proxy
import "sync" import (
"sync"
const copyBufferSize = 30 * 1024 "github.com/9seconds/mtg/config"
)
var copyPool sync.Pool var copyPool sync.Pool
func init() { func init() {
copyPool = sync.Pool{ copyPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
data := make([]byte, copyBufferSize) data := make([]byte, config.BufferSizeCopy)
return &data return &data
}, },
} }
+7 -20
View File
@@ -8,29 +8,23 @@ import (
"github.com/juju/errors" "github.com/juju/errors"
"github.com/9seconds/mtg/config" "github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/wrappers"
) )
const telegramKeepAlive = 30 * time.Second const (
telegramDialTimeout = 10 * time.Second
)
type tgDialer struct { type tgDialer struct {
net.Dialer net.Dialer
conf *config.Config
} }
func (t *tgDialer) dial(addr string) (net.Conn, error) { func (t *tgDialer) dial(addr string) (net.Conn, error) {
connRaw, err := t.Dialer.Dial("tcp", addr) conn, err := t.Dialer.Dial("tcp", addr)
if err != nil { if err != nil {
return nil, errors.Annotate(err, "Cannot connect to Telegram") return nil, errors.Annotate(err, "Cannot connect to Telegram")
} }
conn := connRaw.(*net.TCPConn) if err = config.SetSocketOptions(conn); err != nil {
return nil, errors.Annotate(err, "Cannot set socket options")
if err = conn.SetKeepAlive(true); err != nil {
return nil, errors.Annotate(err, "Cannot establish keepalive connection")
}
if err = conn.SetKeepAlivePeriod(telegramKeepAlive); err != nil {
return nil, errors.Annotate(err, "Cannot set keepalive timeout")
} }
return conn, nil return conn, nil
@@ -42,12 +36,5 @@ func (t *tgDialer) dialRWC(addr string) (io.ReadWriteCloser, error) {
return nil, err return nil, err
} }
return wrappers.NewTimeoutRWC(conn, t.conf.TimeoutRead, t.conf.TimeoutWrite), nil return conn, nil
}
func newDialer(conf *config.Config) tgDialer {
return tgDialer{
Dialer: net.Dialer{Timeout: conf.TimeoutRead},
conf: conf,
}
} }
+2 -1
View File
@@ -2,6 +2,7 @@ package telegram
import ( import (
"io" "io"
"net"
"github.com/juju/errors" "github.com/juju/errors"
@@ -58,7 +59,7 @@ func (t *directTelegram) Init(connOpts *mtproto.ConnectionOpts, conn io.ReadWrit
// to Telegram bypassing middleproxies. // to Telegram bypassing middleproxies.
func NewDirectTelegram(conf *config.Config) Telegram { func NewDirectTelegram(conf *config.Config) Telegram {
return &directTelegram{baseTelegram{ return &directTelegram{baseTelegram{
dialer: newDialer(conf), dialer: tgDialer{net.Dialer{Timeout: telegramDialTimeout}},
v4Addresses: directV4Addresses, v4Addresses: directV4Addresses,
v6Addresses: directV6Addresses, v6Addresses: directV6Addresses,
}} }}
-42
View File
@@ -1,42 +0,0 @@
package wrappers
import (
"io"
"net"
"time"
)
// TimeoutReadWriteCloser sets timeouts for read/write into underlying
// network connection.
type TimeoutReadWriteCloser struct {
conn net.Conn
readTimeout time.Duration
writeTimeout time.Duration
}
// Read reads from connection
func (t *TimeoutReadWriteCloser) Read(p []byte) (int, error) {
t.conn.SetReadDeadline(time.Now().Add(t.readTimeout)) // nolint: errcheck, gas
return t.conn.Read(p)
}
// Write writes into connection.
func (t *TimeoutReadWriteCloser) Write(p []byte) (int, error) {
t.conn.SetWriteDeadline(time.Now().Add(t.writeTimeout)) // nolint: errcheck, gas
return t.conn.Write(p)
}
// Close closes underlying connection.
func (t *TimeoutReadWriteCloser) Close() error {
return t.conn.Close()
}
// NewTimeoutRWC returns wrapper over net.Conn which sets deadlines for
// every wrapped Read/Write.
func NewTimeoutRWC(conn net.Conn, readTimeout, writeTimeout time.Duration) io.ReadWriteCloser {
return &TimeoutReadWriteCloser{
conn: conn,
readTimeout: readTimeout,
writeTimeout: writeTimeout,
}
}