From 5a63c7d5b4894d4e55c0a142bc5afdadfc685c1c Mon Sep 17 00:00:00 2001 From: 9seconds Date: Tue, 17 Feb 2026 21:39:12 +0100 Subject: [PATCH 01/11] Lock all mise dependencies --- .mise.toml | 8 ++++---- mise.lock | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.mise.toml b/.mise.toml index 2c16429..06b9ea5 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,7 +1,10 @@ [tools] -go = "latest" +"go:golang.org/x/pkgsite/cmd/pkgsite" = "latest" "go:golang.org/x/tools/gopls" = "latest" "go:mvdan.cc/gofumpt" = "latest" +go = "latest" +golangci-lint = "latest" +goreleaser = "latest" [vars] fuzzflags = "-fuzztime=120s" @@ -21,7 +24,6 @@ run = [ [tasks.lint] description = "Run linter" -tools.golangci-lint = "latest" run = "golangci-lint run" [tasks.test] @@ -77,7 +79,6 @@ go build \ [tasks.release] description = "Create release tarballs" -tools.goreleaser = "latest" sources = ["**/*.go", "go.mod", "go.sum", ".goreleaser.yml"] run = [ "goreleaser --snapshot --clean", @@ -92,7 +93,6 @@ run = "docker buildx build --pull -t mtg ." [tasks.docs] description = "Run doc server" -tools."go:golang.org/x/pkgsite/cmd/pkgsite" = "latest" run = "pkgsite -http 0.0.0.0:10000" [tasks.fmt] diff --git a/mise.lock b/mise.lock index c994355..3ffdb54 100644 --- a/mise.lock +++ b/mise.lock @@ -7,6 +7,10 @@ backend = "core:go" "platforms.macos-x64" = { checksum = "sha256:1ca28b7703cbea05a65b2a1d92d6b308610ef92f8824578a0874f2e60c9d5a22", url = "https://dl.google.com/go/go1.26.0.darwin-amd64.tar.gz"} "platforms.windows-x64" = { checksum = "sha256:9bbe0fc64236b2b51f6255c05c4232532b8ecc0e6d2e00950bd3021d8a4d07d4", url = "https://dl.google.com/go/go1.26.0.windows-amd64.zip"} +[[tools."go:golang.org/x/pkgsite/cmd/pkgsite"]] +version = "latest" +backend = "go:golang.org/x/pkgsite/cmd/pkgsite" + [[tools."go:golang.org/x/tools/gopls"]] version = "0.21.0" backend = "go:golang.org/x/tools/gopls" @@ -14,3 +18,21 @@ backend = "go:golang.org/x/tools/gopls" [[tools."go:mvdan.cc/gofumpt"]] version = "0.9.2" backend = "go:mvdan.cc/gofumpt" + +[[tools.golangci-lint]] +version = "2.9.0" +backend = "aqua:golangci/golangci-lint" +"platforms.linux-arm64" = { checksum = "sha256:94e80cdb51c73c20a313bd3afa1fb23137728813c19fd730248a1e8678fcc46d", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-linux-arm64.tar.gz"} +"platforms.linux-x64" = { checksum = "sha256:493aaaca2eba6c8bcef847d92716bbd91bbac4b22cdbb0ab5b6a581b32946091", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-linux-amd64.tar.gz"} +"platforms.macos-arm64" = { checksum = "sha256:a86eabba3507deddd21f2a01a1df2a0ee5bc5c8178d4165cdcaaad8597358760", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-darwin-arm64.tar.gz"} +"platforms.macos-x64" = { checksum = "sha256:ba29a353be54a74c45946763983808dc8305eeeca73db1761b5ab112f87f8157", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-darwin-amd64.tar.gz"} +"platforms.windows-x64" = { checksum = "sha256:130fca8fa959eb840267c5e231f030a948e0de1e768bf6a31785505d1061632c", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-windows-amd64.zip"} + +[[tools.goreleaser]] +version = "2.13.3" +backend = "aqua:goreleaser/goreleaser" +"platforms.linux-arm64" = { checksum = "sha256:156656d0f874542d618568bd50afd3d33ced2e8aab2c60cc7c21e1b9fa52031e", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Linux_arm64.tar.gz"} +"platforms.linux-x64" = { checksum = "sha256:4b66f2f78f78561330350651ade557b70328664718490f37834749073af21d20", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Linux_x86_64.tar.gz"} +"platforms.macos-arm64" = { checksum = "sha256:5516c37779efb3935d5b213cda3b0b9025ae94ddbcb51df6919acbcdef4194b0", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Darwin_all.tar.gz"} +"platforms.macos-x64" = { checksum = "sha256:5516c37779efb3935d5b213cda3b0b9025ae94ddbcb51df6919acbcdef4194b0", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Darwin_all.tar.gz"} +"platforms.windows-x64" = { checksum = "sha256:c5586c4ed749ca358ad61ed73ee4b8039cfa68daae8c23e69fb086d549dfb31d", url = "https://github.com/goreleaser/goreleaser/releases/download/v2.13.3/goreleaser_Windows_x86_64.zip"} From 1a247d18b1abf7587d2888369272a4c0262e9a55 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Tue, 17 Feb 2026 21:43:16 +0100 Subject: [PATCH 02/11] Tell about mise in README --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67b5643..395798a 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,12 @@ This project has several main branches ## Getting started +### Download mise + +mtg uses [mise](https://mise.jdx.dev/) to maintain its development +dependencies + replaces a make for building things. Please +[install](https://mise.jdx.dev/getting-started.html) it first. + ### Download a tool #### Download binaries @@ -198,13 +204,14 @@ go install github.com/9seconds/mtg/v2@latest ```console git clone https://github.com/9seconds/mtg.git cd mtg -make static +mise install +mise tasks run build ``` or for the docker image: ```console -make docker +mise tasks run image ``` ### Generate secret From 543f5cde9c732fae923077b43275389a92e1d331 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Tue, 17 Feb 2026 21:46:15 +0100 Subject: [PATCH 03/11] Mention telemt project in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 67b5643..2fccd45 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ are the most notable: * [Official](https://github.com/TelegramMessenger/MTProxy) * [Python](https://github.com/alexbers/mtprotoproxy) * [Erlang](https://github.com/seriyps/mtproto_proxy) +* [Telemt (Rust)](https://github.com/telemt/telemt) You can use any of these. They work great and all implementations have feature parity now. This includes support of adtag, replay attack From 3b03c4a90a5a549ba20786e51b03e316132bcbad Mon Sep 17 00:00:00 2001 From: 9seconds Date: Tue, 17 Feb 2026 23:38:12 +0100 Subject: [PATCH 04/11] More idioms related to go 1.26 --- events/event_stream.go | 2 +- internal/config/type_bool_test.go | 4 ++-- logger/init.go | 2 +- logger/noop.go | 2 +- logger/zerolog.go | 2 +- mtglib/init.go | 2 +- mtglib/init_internal_test.go | 22 +++++++++---------- mtglib/internal/faketls/client_hello.go | 2 +- mtglib/internal/faketls/pools.go | 2 +- mtglib/internal/faketls/record/pools.go | 2 +- .../internal/obfuscated2/handshake_frame.go | 2 +- mtglib/internal/obfuscated2/init_test.go | 2 +- mtglib/internal/obfuscated2/pools.go | 4 ++-- mtglib/internal/relay/init.go | 2 +- mtglib/internal/relay/init_test.go | 2 +- mtglib/internal/relay/pools.go | 2 +- mtglib/proxy.go | 2 +- mtglib/stream_context.go | 2 +- network/circuit_breaker_internal_test.go | 2 +- network/load_balanced_socks5_test.go | 2 +- stats/pools.go | 2 +- 21 files changed, 33 insertions(+), 33 deletions(-) diff --git a/events/event_stream.go b/events/event_stream.go index 7552d2e..dede8b0 100644 --- a/events/event_stream.go +++ b/events/event_stream.go @@ -64,7 +64,7 @@ func NewEventStream(observerFactories []ObserverFactory) EventStream { chans: make([]chan mtglib.Event, runtime.NumCPU()), } - for i := 0; i < runtime.NumCPU(); i++ { + for i := range runtime.NumCPU() { rv.chans[i] = make(chan mtglib.Event, 1) if len(observerFactories) == 1 { diff --git a/internal/config/type_bool_test.go b/internal/config/type_bool_test.go index 99d0488..8098f05 100644 --- a/internal/config/type_bool_test.go +++ b/internal/config/type_bool_test.go @@ -20,7 +20,7 @@ type TypeBoolTestSuite struct { } func (suite *TypeBoolTestSuite) TestUnmarshalFail() { - testData := []interface{}{ + testData := []any{ "", "np", "нет", @@ -29,7 +29,7 @@ func (suite *TypeBoolTestSuite) TestUnmarshalFail() { } for _, v := range testData { - data, err := json.Marshal(map[string]interface{}{ + data, err := json.Marshal(map[string]any{ "value": v, }) suite.NoError(err) diff --git a/logger/init.go b/logger/init.go index da4ff40..56906fc 100644 --- a/logger/init.go +++ b/logger/init.go @@ -8,5 +8,5 @@ package logger // commonly used by many 3pp tools. While mtglib itself does not need it, it is // always a good idea to support it and have a transient end to end logging. type StdLikeLogger interface { - Printf(format string, args ...interface{}) + Printf(format string, args ...any) } diff --git a/logger/noop.go b/logger/noop.go index 5d04b79..768e48b 100644 --- a/logger/noop.go +++ b/logger/noop.go @@ -8,7 +8,7 @@ func (n noopLogger) Named(_ string) mtglib.Logger { return n } func (n noopLogger) BindInt(_ string, _ int) mtglib.Logger { return n } func (n noopLogger) BindStr(_, _ string) mtglib.Logger { return n } func (n noopLogger) BindJSON(_, _ string) mtglib.Logger { return n } -func (n noopLogger) Printf(_ string, _ ...interface{}) {} +func (n noopLogger) Printf(_ string, _ ...any) {} func (n noopLogger) Info(_ string) {} func (n noopLogger) Warning(_ string) {} func (n noopLogger) Debug(_ string) {} diff --git a/logger/zerolog.go b/logger/zerolog.go index 4f9c9b7..4cf7cd6 100644 --- a/logger/zerolog.go +++ b/logger/zerolog.go @@ -78,7 +78,7 @@ func (z *zeroLogContext) BindJSON(name, value string) mtglib.Logger { } } -func (z *zeroLogContext) Printf(format string, args ...interface{}) { +func (z *zeroLogContext) Printf(format string, args ...any) { z.Debug(fmt.Sprintf(format, args...)) } diff --git a/mtglib/init.go b/mtglib/init.go index 40e3f41..81dee7a 100644 --- a/mtglib/init.go +++ b/mtglib/init.go @@ -249,7 +249,7 @@ type Logger interface { BindJSON(name, value string) Logger // Printf is to support log.Logger behavior. - Printf(format string, args ...interface{}) + Printf(format string, args ...any) // Info puts a message about some normal situation. Info(msg string) diff --git a/mtglib/init_internal_test.go b/mtglib/init_internal_test.go index b182d57..50833e8 100644 --- a/mtglib/init_internal_test.go +++ b/mtglib/init_internal_test.go @@ -8,17 +8,17 @@ import ( type NoopLogger struct{} -func (n NoopLogger) Named(_ string) Logger { return n } -func (n NoopLogger) BindInt(_ string, _ int) Logger { return n } -func (n NoopLogger) BindStr(_, _ string) Logger { return n } -func (n NoopLogger) BindJSON(_, _ string) Logger { return n } -func (n NoopLogger) Printf(_ string, _ ...interface{}) {} -func (n NoopLogger) Info(_ string) {} -func (n NoopLogger) Warning(_ string) {} -func (n NoopLogger) Debug(_ string) {} -func (n NoopLogger) InfoError(_ string, _ error) {} -func (n NoopLogger) WarningError(_ string, _ error) {} -func (n NoopLogger) DebugError(_ string, _ error) {} +func (n NoopLogger) Named(_ string) Logger { return n } +func (n NoopLogger) BindInt(_ string, _ int) Logger { return n } +func (n NoopLogger) BindStr(_, _ string) Logger { return n } +func (n NoopLogger) BindJSON(_, _ string) Logger { return n } +func (n NoopLogger) Printf(_ string, _ ...any) {} +func (n NoopLogger) Info(_ string) {} +func (n NoopLogger) Warning(_ string) {} +func (n NoopLogger) Debug(_ string) {} +func (n NoopLogger) InfoError(_ string, _ error) {} +func (n NoopLogger) WarningError(_ string, _ error) {} +func (n NoopLogger) DebugError(_ string, _ error) {} type EventStreamMock struct { mock.Mock diff --git a/mtglib/internal/faketls/client_hello.go b/mtglib/internal/faketls/client_hello.go index d27d769..0e5e5b4 100644 --- a/mtglib/internal/faketls/client_hello.go +++ b/mtglib/internal/faketls/client_hello.go @@ -76,7 +76,7 @@ func ParseClientHello(secret, handshake []byte) (ClientHello, error) { computedRandom := mac.Sum(nil) - for i := 0; i < RandomLen; i++ { + for i := range RandomLen { computedRandom[i] ^= hello.Random[i] } diff --git a/mtglib/internal/faketls/pools.go b/mtglib/internal/faketls/pools.go index 54183a0..b1f4275 100644 --- a/mtglib/internal/faketls/pools.go +++ b/mtglib/internal/faketls/pools.go @@ -6,7 +6,7 @@ import ( ) var bytesBufferPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &bytes.Buffer{} }, } diff --git a/mtglib/internal/faketls/record/pools.go b/mtglib/internal/faketls/record/pools.go index cc4739a..4f758b7 100644 --- a/mtglib/internal/faketls/record/pools.go +++ b/mtglib/internal/faketls/record/pools.go @@ -5,7 +5,7 @@ import ( ) var recordPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &Record{} }, } diff --git a/mtglib/internal/obfuscated2/handshake_frame.go b/mtglib/internal/obfuscated2/handshake_frame.go index 8fec889..0dd3c6c 100644 --- a/mtglib/internal/obfuscated2/handshake_frame.go +++ b/mtglib/internal/obfuscated2/handshake_frame.go @@ -63,7 +63,7 @@ func (h *handshakeFrame) connectionType() []byte { func (h *handshakeFrame) invert() handshakeFrame { copyFrame := *h - for i := 0; i < handshakeFrameLenKey+handshakeFrameLenIV; i++ { + for i := range handshakeFrameLenKey + handshakeFrameLenIV { copyFrame.data[handshakeFrameOffsetKey+i] = h.data[handshakeFrameOffsetConnectionType-1-i] } diff --git a/mtglib/internal/obfuscated2/init_test.go b/mtglib/internal/obfuscated2/init_test.go index 50d031c..5aa85a9 100644 --- a/mtglib/internal/obfuscated2/init_test.go +++ b/mtglib/internal/obfuscated2/init_test.go @@ -117,7 +117,7 @@ func NewServerHandshakeTestData(t *testing.T) ServerHandshakeTestData { serverEncryptedReverted := make([]byte, len(serverEncrypted)) - for i := 0; i < 32+16; i++ { + for i := range 32 + 16 { serverEncryptedReverted[8+i] = serverEncrypted[8+32+16-1-i] } diff --git a/mtglib/internal/obfuscated2/pools.go b/mtglib/internal/obfuscated2/pools.go index f714262..5f10fd2 100644 --- a/mtglib/internal/obfuscated2/pools.go +++ b/mtglib/internal/obfuscated2/pools.go @@ -9,12 +9,12 @@ import ( var ( sha256HasherPool = sync.Pool{ - New: func() interface{} { + New: func() any { return sha256.New() }, } bytesBufferPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &bytes.Buffer{} }, } diff --git a/mtglib/internal/relay/init.go b/mtglib/internal/relay/init.go index a0dcb0c..8bcc66c 100644 --- a/mtglib/internal/relay/init.go +++ b/mtglib/internal/relay/init.go @@ -5,5 +5,5 @@ const ( ) type Logger interface { - Printf(msg string, args ...interface{}) + Printf(msg string, args ...any) } diff --git a/mtglib/internal/relay/init_test.go b/mtglib/internal/relay/init_test.go index e43244a..c884532 100644 --- a/mtglib/internal/relay/init_test.go +++ b/mtglib/internal/relay/init_test.go @@ -2,4 +2,4 @@ package relay_test type loggerMock struct{} -func (l loggerMock) Printf(format string, args ...interface{}) {} +func (l loggerMock) Printf(format string, args ...any) {} diff --git a/mtglib/internal/relay/pools.go b/mtglib/internal/relay/pools.go index 49ac7ac..db42933 100644 --- a/mtglib/internal/relay/pools.go +++ b/mtglib/internal/relay/pools.go @@ -3,7 +3,7 @@ package relay import "sync" var copyBufferPool = sync.Pool{ - New: func() interface{} { + New: func() any { rv := make([]byte, copyBufferSize) return &rv diff --git a/mtglib/proxy.go b/mtglib/proxy.go index 830a2f9..104c723 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -323,7 +323,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) { } pool, err := ants.NewPoolWithFunc(opts.getConcurrency(), - func(arg interface{}) { + func(arg any) { proxy.ServeConn(arg.(essentials.Conn)) //nolint: forcetypeassert }, ants.WithLogger(opts.getLogger("ants")), diff --git a/mtglib/stream_context.go b/mtglib/stream_context.go index 3c6a739..3858282 100644 --- a/mtglib/stream_context.go +++ b/mtglib/stream_context.go @@ -32,7 +32,7 @@ func (s *streamContext) Err() error { return s.ctx.Err() //nolint: wrapcheck } -func (s *streamContext) Value(key interface{}) interface{} { +func (s *streamContext) Value(key any) any { return s.ctx.Value(key) } diff --git a/network/circuit_breaker_internal_test.go b/network/circuit_breaker_internal_test.go index 16a8443..fc7f671 100644 --- a/network/circuit_breaker_internal_test.go +++ b/network/circuit_breaker_internal_test.go @@ -59,7 +59,7 @@ func (suite *CircuitBreakerTestSuite) TestMultipleRunsOk() { suite.ctxCancel() }() - for i := 0; i < 5; i++ { + for range 5 { go func() { defer wg.Done() diff --git a/network/load_balanced_socks5_test.go b/network/load_balanced_socks5_test.go index 1d5e86c..96298ac 100644 --- a/network/load_balanced_socks5_test.go +++ b/network/load_balanced_socks5_test.go @@ -64,7 +64,7 @@ func (suite *LoadBalancedSocks5TestSuite) TestCannotDial() { }) suite.NoError(err) - for i := 0; i < network.ProxyDialerOpenThreshold*2; i++ { + for range network.ProxyDialerOpenThreshold * 2 { _, err = lbDialer.Dial("tcp", "127.1.1.1:80") suite.True(errors.Is(err, network.ErrCannotDialWithAllProxies)) } diff --git a/stats/pools.go b/stats/pools.go index a8c9621..f80286a 100644 --- a/stats/pools.go +++ b/stats/pools.go @@ -3,7 +3,7 @@ package stats import "sync" var streamInfoPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &streamInfo{ tags: make(map[string]string), } From a0aabf2391ead23bc6831b74457168a1091c5b0d Mon Sep 17 00:00:00 2001 From: 9seconds Date: Tue, 17 Feb 2026 23:43:48 +0100 Subject: [PATCH 05/11] Switch to rand/v2 --- events/event_stream.go | 2 +- mtglib/internal/faketls/conn.go | 4 ++-- mtglib/internal/faketls/conn_test.go | 4 ++-- mtglib/internal/faketls/welcome.go | 4 ++-- mtglib/internal/faketls/welcome_test.go | 2 +- network/load_balanced_socks5.go | 4 ++-- network/network.go | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/events/event_stream.go b/events/event_stream.go index dede8b0..b71db4c 100644 --- a/events/event_stream.go +++ b/events/event_stream.go @@ -2,7 +2,7 @@ package events import ( "context" - "math/rand" + "math/rand/v2" "runtime" "github.com/9seconds/mtg/v2/mtglib" diff --git a/mtglib/internal/faketls/conn.go b/mtglib/internal/faketls/conn.go index f88771c..85acee2 100644 --- a/mtglib/internal/faketls/conn.go +++ b/mtglib/internal/faketls/conn.go @@ -3,7 +3,7 @@ package faketls import ( "bytes" "fmt" - "math/rand" + "math/rand/v2" "github.com/9seconds/mtg/v2/essentials" "github.com/9seconds/mtg/v2/mtglib/internal/faketls/record" @@ -53,7 +53,7 @@ func (c *Conn) Write(p []byte) (int, error) { lenP := len(p) for len(p) > 0 { - chunkSize := rand.Intn(record.TLSMaxRecordSize) + chunkSize := rand.IntN(record.TLSMaxRecordSize) if chunkSize > len(p) || chunkSize == 0 { chunkSize = len(p) } diff --git a/mtglib/internal/faketls/conn_test.go b/mtglib/internal/faketls/conn_test.go index c60dec9..29a8c0d 100644 --- a/mtglib/internal/faketls/conn_test.go +++ b/mtglib/internal/faketls/conn_test.go @@ -2,9 +2,9 @@ package faketls_test import ( "bytes" + "crypto/rand" "errors" "io" - "math/rand" "testing" "github.com/9seconds/mtg/v2/internal/testlib" @@ -123,7 +123,7 @@ func (suite *ConnTestSuite) TestWrite() { suite.connMock.On("Write", mock.Anything).Return(0, nil) dataToRec := make([]byte, record.TLSMaxRecordSize*2) - rand.Read(dataToRec) //nolint: staticcheck + rand.Read(dataToRec) //nolint: staticcheck, errcheck n, err := suite.c.Write(dataToRec) suite.NoError(err) diff --git a/mtglib/internal/faketls/welcome.go b/mtglib/internal/faketls/welcome.go index 8af46e4..0d91dac 100644 --- a/mtglib/internal/faketls/welcome.go +++ b/mtglib/internal/faketls/welcome.go @@ -6,7 +6,7 @@ import ( "crypto/sha256" "encoding/binary" "io" - mrand "math/rand" + mrand "math/rand/v2" "github.com/9seconds/mtg/v2/mtglib/internal/faketls/record" "golang.org/x/crypto/curve25519" @@ -36,7 +36,7 @@ func SendWelcomePacket(writer io.Writer, secret []byte, clientHello ClientHello) rec.Type = record.TypeApplicationData rec.Version = record.Version12 - if _, err := io.CopyN(&rec.Payload, rand.Reader, int64(1024+mrand.Intn(3092))); err != nil { + if _, err := io.CopyN(&rec.Payload, rand.Reader, int64(1024+mrand.IntN(3092))); err != nil { panic(err) } diff --git a/mtglib/internal/faketls/welcome_test.go b/mtglib/internal/faketls/welcome_test.go index 43ba033..e722007 100644 --- a/mtglib/internal/faketls/welcome_test.go +++ b/mtglib/internal/faketls/welcome_test.go @@ -3,8 +3,8 @@ package faketls_test import ( "bytes" "crypto/hmac" + "crypto/rand" "crypto/sha256" - "math/rand" "testing" "time" diff --git a/network/load_balanced_socks5.go b/network/load_balanced_socks5.go index f6d0a82..8aec387 100644 --- a/network/load_balanced_socks5.go +++ b/network/load_balanced_socks5.go @@ -3,7 +3,7 @@ package network import ( "context" "fmt" - "math/rand" + "math/rand/v2" "net/url" "github.com/9seconds/mtg/v2/essentials" @@ -19,7 +19,7 @@ func (l loadBalancedSocks5Dialer) Dial(network, address string) (essentials.Conn func (l loadBalancedSocks5Dialer) DialContext(ctx context.Context, network, address string) (essentials.Conn, error) { length := len(l.dialers) - start := rand.Intn(length) + start := rand.IntN(length) moved := false for i := start; i != start || !moved; i = (i + 1) % length { diff --git a/network/network.go b/network/network.go index 3f68b11..5b536b4 100644 --- a/network/network.go +++ b/network/network.go @@ -3,7 +3,7 @@ package network import ( "context" "fmt" - "math/rand" + "math/rand/v2" "net" "net/http" "sync" From 0440ccf4ffaae257dd8bfdbe279ca2408443880d Mon Sep 17 00:00:00 2001 From: 9seconds Date: Wed, 18 Feb 2026 10:33:46 +0100 Subject: [PATCH 06/11] Rewrite to WaitGroup.Go --- events/multi_observer.go | 81 ++++++++---------------- internal/cli/access.go | 14 ++-- ipblocklist/firehol.go | 11 ++-- network/circuit_breaker_internal_test.go | 17 ++--- network/network.go | 16 ++--- 5 files changed, 46 insertions(+), 93 deletions(-) diff --git a/events/multi_observer.go b/events/multi_observer.go index 2b529cd..f59d63e 100644 --- a/events/multi_observer.go +++ b/events/multi_observer.go @@ -12,14 +12,11 @@ type multiObserver struct { func (m multiObserver) EventStart(evt mtglib.EventStart) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventStart(evt) - }(v) + wg.Go(func() { + v.EventStart(evt) + }) } wg.Wait() @@ -27,14 +24,11 @@ func (m multiObserver) EventStart(evt mtglib.EventStart) { func (m multiObserver) EventConnectedToDC(evt mtglib.EventConnectedToDC) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventConnectedToDC(evt) - }(v) + wg.Go(func() { + v.EventConnectedToDC(evt) + }) } wg.Wait() @@ -42,14 +36,11 @@ func (m multiObserver) EventConnectedToDC(evt mtglib.EventConnectedToDC) { func (m multiObserver) EventDomainFronting(evt mtglib.EventDomainFronting) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventDomainFronting(evt) - }(v) + wg.Go(func() { + v.EventDomainFronting(evt) + }) } wg.Wait() @@ -57,14 +48,11 @@ func (m multiObserver) EventDomainFronting(evt mtglib.EventDomainFronting) { func (m multiObserver) EventTraffic(evt mtglib.EventTraffic) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventTraffic(evt) - }(v) + wg.Go(func() { + v.EventTraffic(evt) + }) } wg.Wait() @@ -72,14 +60,11 @@ func (m multiObserver) EventTraffic(evt mtglib.EventTraffic) { func (m multiObserver) EventFinish(evt mtglib.EventFinish) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventFinish(evt) - }(v) + wg.Go(func() { + v.EventFinish(evt) + }) } wg.Wait() @@ -87,14 +72,11 @@ func (m multiObserver) EventFinish(evt mtglib.EventFinish) { func (m multiObserver) EventConcurrencyLimited(evt mtglib.EventConcurrencyLimited) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventConcurrencyLimited(evt) - }(v) + wg.Go(func() { + v.EventConcurrencyLimited(evt) + }) } wg.Wait() @@ -102,14 +84,11 @@ func (m multiObserver) EventConcurrencyLimited(evt mtglib.EventConcurrencyLimite func (m multiObserver) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventIPBlocklisted(evt) - }(v) + wg.Go(func() { + v.EventIPBlocklisted(evt) + }) } wg.Wait() @@ -117,14 +96,11 @@ func (m multiObserver) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) { func (m multiObserver) EventReplayAttack(evt mtglib.EventReplayAttack) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventReplayAttack(evt) - }(v) + wg.Go(func() { + v.EventReplayAttack(evt) + }) } wg.Wait() @@ -132,14 +108,11 @@ func (m multiObserver) EventReplayAttack(evt mtglib.EventReplayAttack) { func (m multiObserver) EventIPListSize(evt mtglib.EventIPListSize) { wg := &sync.WaitGroup{} - wg.Add(len(m.observers)) for _, v := range m.observers { - go func(obs Observer) { - defer wg.Done() - - obs.EventIPListSize(evt) - }(v) + wg.Go(func() { + v.EventIPListSize(evt) + }) } wg.Wait() diff --git a/internal/cli/access.go b/internal/cli/access.go index 2e99c98..4657822 100644 --- a/internal/cli/access.go +++ b/internal/cli/access.go @@ -61,11 +61,8 @@ func (a *Access) Run(cli *CLI, version string) error { } wg := &sync.WaitGroup{} - wg.Add(2) - - go func() { - defer wg.Done() + wg.Go(func() { ip := a.PublicIPv4 if ip == nil { ip = a.getIP(ntw, "tcp4") @@ -76,11 +73,8 @@ func (a *Access) Run(cli *CLI, version string) error { } resp.IPv4 = a.makeURLs(conf, ip) - }() - - go func() { - defer wg.Done() - + }) + wg.Go(func() { ip := a.PublicIPv6 if ip == nil { ip = a.getIP(ntw, "tcp6") @@ -91,7 +85,7 @@ func (a *Access) Run(cli *CLI, version string) error { } resp.IPv6 = a.makeURLs(conf, ip) - }() + }) wg.Wait() diff --git a/ipblocklist/firehol.go b/ipblocklist/firehol.go index c2682d6..f219560 100644 --- a/ipblocklist/firehol.go +++ b/ipblocklist/firehol.go @@ -112,18 +112,15 @@ func (f *Firehol) update() { defer cancel() wg := &sync.WaitGroup{} - wg.Add(len(f.blocklists)) mutex := &sync.Mutex{} ranger := cidranger.NewPCTrieRanger() for _, v := range f.blocklists { - go func(file files.File) { - defer wg.Done() + wg.Go(func() { + logger := f.logger.BindStr("filename", v.String()) - logger := f.logger.BindStr("filename", file.String()) - - fileContent, err := file.Open(ctx) + fileContent, err := v.Open(ctx) if err != nil { logger.WarningError("update has failed", err) @@ -135,7 +132,7 @@ func (f *Firehol) update() { if err := f.updateFromFile(mutex, ranger, bufio.NewScanner(fileContent)); err != nil { logger.WarningError("update has failed", err) } - }(v) + }) } wg.Wait() diff --git a/network/circuit_breaker_internal_test.go b/network/circuit_breaker_internal_test.go index fc7f671..a665e21 100644 --- a/network/circuit_breaker_internal_test.go +++ b/network/circuit_breaker_internal_test.go @@ -52,17 +52,9 @@ func (suite *CircuitBreakerTestSuite) TestMultipleRunsOk() { Return(suite.connMock, nil) wg := &sync.WaitGroup{} - wg.Add(5) - - go func() { - wg.Wait() - suite.ctxCancel() - }() for range 5 { - go func() { - defer wg.Done() - + wg.Go(func() { conn, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1") suite.mutex.Lock() @@ -70,9 +62,14 @@ func (suite *CircuitBreakerTestSuite) TestMultipleRunsOk() { suite.NoError(err) suite.Equal("127.0.0.1:3128", conn.RemoteAddr().String()) - }() + }) } + go func() { + wg.Wait() + suite.ctxCancel() + }() + suite.Eventually(func() bool { _, ok := <-suite.ctx.Done() diff --git a/network/network.go b/network/network.go index 5b536b4..7a7b332 100644 --- a/network/network.go +++ b/network/network.go @@ -81,32 +81,24 @@ func (n *network) dnsResolve(protocol, address string) ([]string, error) { switch protocol { case "tcp", "tcp4": - wg.Add(1) - - go func() { - defer wg.Done() - + wg.Go(func() { resolved := n.dns.LookupA(address) mutex.Lock() ips = append(ips, resolved...) mutex.Unlock() - }() + }) } switch protocol { case "tcp", "tcp6": - wg.Add(1) - - go func() { - defer wg.Done() - + wg.Go(func() { resolved := n.dns.LookupAAAA(address) mutex.Lock() ips = append(ips, resolved...) mutex.Unlock() - }() + }) } wg.Wait() From 08393e426ace84167cbcad7999f367e93680bd2a Mon Sep 17 00:00:00 2001 From: 9seconds Date: Wed, 18 Feb 2026 10:37:41 +0100 Subject: [PATCH 07/11] Update mise dependencies --- mise.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mise.lock b/mise.lock index 3ffdb54..d6b2d74 100644 --- a/mise.lock +++ b/mise.lock @@ -12,7 +12,7 @@ version = "latest" backend = "go:golang.org/x/pkgsite/cmd/pkgsite" [[tools."go:golang.org/x/tools/gopls"]] -version = "0.21.0" +version = "0.21.1" backend = "go:golang.org/x/tools/gopls" [[tools."go:mvdan.cc/gofumpt"]] @@ -20,13 +20,13 @@ version = "0.9.2" backend = "go:mvdan.cc/gofumpt" [[tools.golangci-lint]] -version = "2.9.0" +version = "2.10.1" backend = "aqua:golangci/golangci-lint" -"platforms.linux-arm64" = { checksum = "sha256:94e80cdb51c73c20a313bd3afa1fb23137728813c19fd730248a1e8678fcc46d", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-linux-arm64.tar.gz"} -"platforms.linux-x64" = { checksum = "sha256:493aaaca2eba6c8bcef847d92716bbd91bbac4b22cdbb0ab5b6a581b32946091", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-linux-amd64.tar.gz"} -"platforms.macos-arm64" = { checksum = "sha256:a86eabba3507deddd21f2a01a1df2a0ee5bc5c8178d4165cdcaaad8597358760", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-darwin-arm64.tar.gz"} -"platforms.macos-x64" = { checksum = "sha256:ba29a353be54a74c45946763983808dc8305eeeca73db1761b5ab112f87f8157", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-darwin-amd64.tar.gz"} -"platforms.windows-x64" = { checksum = "sha256:130fca8fa959eb840267c5e231f030a948e0de1e768bf6a31785505d1061632c", url = "https://github.com/golangci/golangci-lint/releases/download/v2.9.0/golangci-lint-2.9.0-windows-amd64.zip"} +"platforms.linux-arm64" = { checksum = "sha256:6652b42ae02915eb2f9cb2a2e0cac99514c8eded8388d88ae3e06e1a52c00de8", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-linux-arm64.tar.gz"} +"platforms.linux-x64" = { checksum = "sha256:dfa775874cf0561b404a02a8f4481fc69b28091da95aa697259820d429b09c99", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-linux-amd64.tar.gz"} +"platforms.macos-arm64" = { checksum = "sha256:03bfadf67e52b441b7ec21305e501c717df93c959836d66c7f97312654acb297", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-darwin-arm64.tar.gz"} +"platforms.macos-x64" = { checksum = "sha256:66fb0da81b8033b477f97eea420d4b46b230ca172b8bb87c6610109f3772b6b6", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-darwin-amd64.tar.gz"} +"platforms.windows-x64" = { checksum = "sha256:c60c87695e79db8e320f0e5be885059859de52bb5ee5f11be5577828570bc2a3", url = "https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-windows-amd64.zip"} [[tools.goreleaser]] version = "2.13.3" From 222cef8c41ee6cd4fe367465a00b35137b27bd2f Mon Sep 17 00:00:00 2001 From: 9seconds Date: Wed, 18 Feb 2026 11:13:55 +0100 Subject: [PATCH 08/11] Recommend to use telemt for adtag carvings --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index a3c5da9..170991d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,17 @@ Highly-opinionated (ex-bullshit-free) MTPROTO proxy for **If you use v1.0 or upgrade broke you proxy, please read the chapter [Version 2](#version-2)** +If you want to have a proxy that _supports adtag_ (possibility to promote a +channel with a special Telegram bot), I recommend to use +[telemt](https://github.com/telemt/telemt) project. v1 of mtg supports it +but I do not see any reasonable point of using it: adtag requires communication +via a fragile set of middle proxies, requires complex setup that must expose +a public IPs, has lower bandwidth and latency. + +~10-20k simultaneous connections, has no user management, but ticks all +checkboxes related to its main intent: provide a way to use Telegram. +mtg idea is simple: minimal unbloated proxy that can handle a reasonable scale + ## Rationale There are several available proxies for Telegram MTPROTO available. Here From d0e99dda2b0643092b2e92752e755fc048bc8fd2 Mon Sep 17 00:00:00 2001 From: Sergei Arkhipov <9seconds@users.noreply.github.com> Date: Wed, 18 Feb 2026 11:16:04 +0100 Subject: [PATCH 09/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 170991d..09dae08 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ but I do not see any reasonable point of using it: adtag requires communication via a fragile set of middle proxies, requires complex setup that must expose a public IPs, has lower bandwidth and latency. +mtg idea is simple: minimal unbloated proxy that can handle a reasonable scale ~10-20k simultaneous connections, has no user management, but ticks all checkboxes related to its main intent: provide a way to use Telegram. -mtg idea is simple: minimal unbloated proxy that can handle a reasonable scale ## Rationale From cf3437bb63506f14d6852e12db97bfd85f99ab72 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Thu, 19 Feb 2026 11:30:13 +0100 Subject: [PATCH 10/11] Add support of proxy protocol --- README.md | 6 ++++++ example.config.toml | 9 +++++++++ go.mod | 1 + go.sum | 2 ++ internal/cli/run_proxy.go | 10 ++++++++++ internal/config/config.go | 1 + internal/config/parse.go | 1 + internal/proxyprotocol/adapter.go | 20 ++++++++++++++++++++ internal/proxyprotocol/conn.go | 25 +++++++++++++++++++++++++ 9 files changed, 75 insertions(+) create mode 100644 internal/proxyprotocol/adapter.go create mode 100644 internal/proxyprotocol/conn.go diff --git a/README.md b/README.md index 09dae08..1a5bbb9 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,12 @@ that probably matter. way of doing business I suppose. I think the only viable way is to have a proxy that can be restored anywhere easily. +* Supports proxy protocol v1/v2 + +This makes integration with loadbalancers like HAProxy and ELB a first class +citizen by supporting their +[commuication protocols](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt). + * **A single secret** I think that multiple secrets solve no problems and just complex diff --git a/example.config.toml b/example.config.toml index eb2c62c..68f7926 100644 --- a/example.config.toml +++ b/example.config.toml @@ -23,6 +23,15 @@ secret = "ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c6561706973 # Host:port pair to run proxy on. bind-to = "0.0.0.0:3128" +# This defines what types of traffic mtg listens to. If you are not sure, +# then definitely keep it disable. Enable it only and only if incoming traffic +# is coming from some sort of load-balancer like HAProxy or ELB. +# https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt +# +# mtg uses a library that supports v1 and v2 versions of ProxyProtocol. +# default value is false. +# proxy-protocol-listener = false + # Defines how many concurrent connections are allowed to this proxy. # All other incoming connections are going to be dropped. concurrency = 8192 diff --git a/go.mod b/go.mod index b4c34f7..5520d48 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( require ( github.com/pelletier/go-toml/v2 v2.2.4 + github.com/pires/go-proxyproto v0.11.0 github.com/txthinking/socks5 v0.0.0-20251011041537-5c31f201a10e github.com/yl2chen/cidranger v1.0.2 ) diff --git a/go.sum b/go.sum index c7d814b..13d71bb 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4= +github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/internal/cli/run_proxy.go b/internal/cli/run_proxy.go index 5357309..23580eb 100644 --- a/internal/cli/run_proxy.go +++ b/internal/cli/run_proxy.go @@ -10,6 +10,7 @@ import ( "github.com/9seconds/mtg/v2/antireplay" "github.com/9seconds/mtg/v2/events" "github.com/9seconds/mtg/v2/internal/config" + "github.com/9seconds/mtg/v2/internal/proxyprotocol" "github.com/9seconds/mtg/v2/internal/utils" "github.com/9seconds/mtg/v2/ipblocklist" "github.com/9seconds/mtg/v2/ipblocklist/files" @@ -17,6 +18,7 @@ import ( "github.com/9seconds/mtg/v2/mtglib" "github.com/9seconds/mtg/v2/network" "github.com/9seconds/mtg/v2/stats" + "github.com/pires/go-proxyproto" "github.com/rs/zerolog" "github.com/yl2chen/cidranger" ) @@ -275,6 +277,14 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen return fmt.Errorf("cannot start proxy: %w", err) } + if conf.ProxyProtocolListener.Get(false) { + listener = &proxyprotocol.ListenerAdapter{ + Listener: proxyproto.Listener{ + Listener: listener, + }, + } + } + ctx := utils.RootContext() go proxy.Serve(listener) //nolint: errcheck diff --git a/internal/config/config.go b/internal/config/config.go index 3bab778..74abdf0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -25,6 +25,7 @@ type Config struct { AllowFallbackOnUnknownDC TypeBool `json:"allowFallbackOnUnknownDc"` Secret mtglib.Secret `json:"secret"` BindTo TypeHostPort `json:"bindTo"` + ProxyProtocolListener TypeBool `json:"proxyProtocolListener"` PreferIP TypePreferIP `json:"preferIp"` DomainFrontingPort TypePort `json:"domainFrontingPort"` TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"` diff --git a/internal/config/parse.go b/internal/config/parse.go index fc1a148..7cdf6da 100644 --- a/internal/config/parse.go +++ b/internal/config/parse.go @@ -13,6 +13,7 @@ type tomlConfig struct { AllowFallbackOnUnknownDC bool `toml:"allow-fallback-on-unknown-dc" json:"allowFallbackOnUnknownDc,omitempty"` Secret string `toml:"secret" json:"secret"` BindTo string `toml:"bind-to" json:"bindTo"` + ProxyProtocolListener bool `toml:"proxy-protocol-listener" json:"proxyProtocolListener"` PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"` DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"` TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"` diff --git a/internal/proxyprotocol/adapter.go b/internal/proxyprotocol/adapter.go new file mode 100644 index 0000000..3b69d7b --- /dev/null +++ b/internal/proxyprotocol/adapter.go @@ -0,0 +1,20 @@ +package proxyprotocol + +import ( + "net" + + "github.com/pires/go-proxyproto" +) + +type ListenerAdapter struct { + proxyproto.Listener +} + +func (l *ListenerAdapter) Accept() (net.Conn, error) { + conn, err := l.Listener.Accept() + if err != nil { + return nil, err + } + + return connWrapper{conn.(*proxyproto.Conn)}, nil +} diff --git a/internal/proxyprotocol/conn.go b/internal/proxyprotocol/conn.go new file mode 100644 index 0000000..aff1457 --- /dev/null +++ b/internal/proxyprotocol/conn.go @@ -0,0 +1,25 @@ +package proxyprotocol + +import "github.com/pires/go-proxyproto" + +type connWrapper struct { + *proxyproto.Conn +} + +func (c connWrapper) CloseRead() error { + tcpConn, ok := c.TCPConn() + if !ok { + panic("we support only tcp connections") + } + + return tcpConn.CloseRead() +} + +func (c connWrapper) CloseWrite() error { + tcpConn, ok := c.TCPConn() + if !ok { + panic("we support only tcp connections") + } + + return tcpConn.CloseWrite() +} From 432e530f6894ed46e20bff2d5889942ab92604a9 Mon Sep 17 00:00:00 2001 From: Sergei Arkhipov <9seconds@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:35:04 +0100 Subject: [PATCH 11/11] Adjust formatting in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1a5bbb9..82f1eee 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,11 @@ that probably matter. way of doing business I suppose. I think the only viable way is to have a proxy that can be restored anywhere easily. -* Supports proxy protocol v1/v2 +* **Supports proxy protocol v1/v2** -This makes integration with loadbalancers like HAProxy and ELB a first class -citizen by supporting their -[commuication protocols](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt). + This makes integration with loadbalancers like HAProxy and ELB a first class + citizen by supporting their + [commuication protocols](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt). * **A single secret**