mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-01 11:04:01 +03:00
REPOSITORY / ScuroNeko/mtg
Compare commits
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b7364238a | ||
|
|
866906e770 | ||
|
|
b3c8c4a47d | ||
|
|
fbc7499cda | ||
|
|
ca5800cf60 | ||
|
|
00bb7be900 | ||
|
|
e8c70603f7 | ||
|
|
9d72904508 | ||
|
|
456ed5b051 | ||
|
|
4b7be8c565 |
@@ -30,7 +30,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go_version:
|
go_version:
|
||||||
- ^1.16
|
- ~1.16
|
||||||
|
- ^1.17
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -70,7 +71,7 @@ jobs:
|
|||||||
- name: Run linter
|
- name: Run linter
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
version: v1.41.1
|
version: v1.42.0
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
name: Docker
|
name: Docker
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# BUILD STAGE
|
# BUILD STAGE
|
||||||
|
|
||||||
FROM golang:1.16-alpine AS build
|
FROM golang:1.17-alpine AS build
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apk --no-cache --update add \
|
&& apk --no-cache --update add \
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
|||||||
IMAGE_NAME := mtg
|
IMAGE_NAME := mtg
|
||||||
APP_NAME := $(IMAGE_NAME)
|
APP_NAME := $(IMAGE_NAME)
|
||||||
|
|
||||||
GOLANGCI_LINT_VERSION := v1.41.1
|
GOLANGCI_LINT_VERSION := v1.42.0
|
||||||
|
|
||||||
VERSION_GO := $(shell go version)
|
VERSION_GO := $(shell go version)
|
||||||
VERSION_DATE := $(shell date -Ru)
|
VERSION_DATE := $(shell date -Ru)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/9seconds/mtg/v2
|
module github.com/9seconds/mtg/v2
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OneOfOne/xxhash v1.2.8
|
github.com/OneOfOne/xxhash v1.2.8
|
||||||
@@ -13,14 +13,13 @@ require (
|
|||||||
github.com/gotd/td v0.34.0
|
github.com/gotd/td v0.34.0
|
||||||
github.com/jarcoal/httpmock v1.0.8
|
github.com/jarcoal/httpmock v1.0.8
|
||||||
github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1
|
github.com/kentik/patricia v0.0.0-20201202224819-f9447a6e25f1
|
||||||
github.com/libp2p/go-reuseport v0.0.2
|
|
||||||
github.com/mccutchen/go-httpbin v1.1.1
|
github.com/mccutchen/go-httpbin v1.1.1
|
||||||
github.com/panjf2000/ants/v2 v2.4.6
|
github.com/panjf2000/ants/v2 v2.4.6
|
||||||
github.com/pelletier/go-toml v1.9.3
|
github.com/pelletier/go-toml v1.9.3
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/prometheus/common v0.30.0 // indirect
|
github.com/prometheus/common v0.30.0 // indirect
|
||||||
github.com/prometheus/procfs v0.7.1 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
github.com/rs/zerolog v1.23.0
|
github.com/rs/zerolog v1.24.0
|
||||||
github.com/smira/go-statsd v1.3.2
|
github.com/smira/go-statsd v1.3.2
|
||||||
github.com/stretchr/objx v0.3.0 // indirect
|
github.com/stretchr/objx v0.3.0 // indirect
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
@@ -30,3 +29,22 @@ require (
|
|||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/gotd/ige v0.1.5 // indirect
|
||||||
|
github.com/gotd/xor v0.1.1 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
go.uber.org/zap v1.16.0 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
)
|
||||||
|
|||||||
@@ -59,8 +59,9 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||||||
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
|
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@@ -179,8 +180,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU=
|
|
||||||
github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ=
|
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
@@ -224,14 +223,14 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/procfs v0.7.1 h1:TlEtJq5GvGqMykEwWzbZWjjztF86swFhsPix1i0bkgA=
|
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||||
github.com/prometheus/procfs v0.7.1/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/quasilyte/go-ruleguard/dsl v0.3.2/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
github.com/quasilyte/go-ruleguard/dsl v0.3.2/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g=
|
github.com/rs/zerolog v1.24.0 h1:76ivFxmVSRs1u2wUwJVg5VZDYQgeH1JpoS6ndgr9Wy8=
|
||||||
github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo=
|
github.com/rs/zerolog v1.24.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=
|
||||||
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
@@ -248,7 +247,6 @@ github.com/stretchr/testify v1.1.5-0.20170809224252-890a5c3458b4/go.mod h1:a8OnR
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@@ -257,7 +255,7 @@ github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:O
|
|||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
@@ -311,8 +309,9 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -344,6 +343,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
|||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
@@ -361,15 +361,14 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -400,9 +399,10 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||||
@@ -460,8 +460,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
|||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
@@ -183,7 +183,6 @@ func runProxy(conf *config.Config, version string) error {
|
|||||||
Secret: conf.Secret,
|
Secret: conf.Secret,
|
||||||
BufferSize: conf.TCPBuffer.Get(mtglib.DefaultBufferSize),
|
BufferSize: conf.TCPBuffer.Get(mtglib.DefaultBufferSize),
|
||||||
DomainFrontingPort: conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort),
|
DomainFrontingPort: conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort),
|
||||||
IdleTimeout: conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout),
|
|
||||||
PreferIP: conf.PreferIP.Get(mtglib.DefaultPreferIP),
|
PreferIP: conf.PreferIP.Get(mtglib.DefaultPreferIP),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +191,7 @@ func runProxy(conf *config.Config, version string) error {
|
|||||||
return fmt.Errorf("cannot create a proxy: %w", err)
|
return fmt.Errorf("cannot create a proxy: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", conf.BindTo.Get(""))
|
listener, err := utils.NewListener(conf.BindTo.Get(""), int(opts.BufferSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot start proxy: %w", err)
|
return fmt.Errorf("cannot start proxy: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/9seconds/mtg/v2/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
net.Listener
|
||||||
|
|
||||||
|
bufferSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Listener) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err // nolint: wrapcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := network.SetClientSocketOptions(conn, l.bufferSize); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("cannot set TCP options: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListener(bindTo string, bufferSize int) (net.Listener, error) {
|
||||||
|
base, err := net.Listen("tcp", bindTo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot build a base listener: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Listener{
|
||||||
|
Listener: base,
|
||||||
|
bufferSize: bufferSize,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
+10
-2
@@ -69,6 +69,9 @@ const (
|
|||||||
|
|
||||||
// DefaultIdleTimeout is a default timeout for closing a connection
|
// DefaultIdleTimeout is a default timeout for closing a connection
|
||||||
// in case of idling.
|
// in case of idling.
|
||||||
|
//
|
||||||
|
// Deprecated: no longer in use because of changed TCP relay
|
||||||
|
// algorithm.
|
||||||
DefaultIdleTimeout = time.Minute
|
DefaultIdleTimeout = time.Minute
|
||||||
|
|
||||||
// DefaultTolerateTimeSkewness is a default timeout for time
|
// DefaultTolerateTimeSkewness is a default timeout for time
|
||||||
@@ -83,9 +86,14 @@ const (
|
|||||||
// by Telegram and a proxy.
|
// by Telegram and a proxy.
|
||||||
SecretKeyLength = 16
|
SecretKeyLength = 16
|
||||||
|
|
||||||
// ConnectionIDBytesLength defines a count of random bytes
|
// ConnectionIDBytesLength defines a count of random bytes used to generate
|
||||||
// used to generate a stream/connection ids.
|
// a stream/connection ids.
|
||||||
ConnectionIDBytesLength = 16
|
ConnectionIDBytesLength = 16
|
||||||
|
|
||||||
|
// TCPRelayReadTimeout defines a max time period between two consecuitive
|
||||||
|
// reads from Telegram after which connection will be terminated. This is
|
||||||
|
// required to abort stale connections.
|
||||||
|
TCPRelayReadTimeout = 20 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// Network defines a knowledge how to work with a network. It may sound
|
// Network defines a knowledge how to work with a network. It may sound
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ func (c *Conn) Write(p []byte) (int, error) {
|
|||||||
|
|
||||||
rec.Type = record.TypeApplicationData
|
rec.Type = record.TypeApplicationData
|
||||||
rec.Version = record.Version12
|
rec.Version = record.Version12
|
||||||
written := 0
|
|
||||||
|
sendBuffer := acquireBytesBuffer()
|
||||||
|
defer releaseBytesBuffer(sendBuffer)
|
||||||
|
|
||||||
|
lenP := len(p)
|
||||||
|
|
||||||
for len(p) > 0 {
|
for len(p) > 0 {
|
||||||
chunkSize := rand.Intn(record.TLSMaxRecordSize)
|
chunkSize := rand.Intn(record.TLSMaxRecordSize)
|
||||||
@@ -56,14 +60,14 @@ func (c *Conn) Write(p []byte) (int, error) {
|
|||||||
|
|
||||||
rec.Payload.Reset()
|
rec.Payload.Reset()
|
||||||
rec.Payload.Write(p[:chunkSize])
|
rec.Payload.Write(p[:chunkSize])
|
||||||
|
rec.Dump(sendBuffer) // nolint: errcheck
|
||||||
|
|
||||||
if err := rec.Dump(c.Conn); err != nil {
|
|
||||||
return written, err // nolint: wrapcheck
|
|
||||||
}
|
|
||||||
|
|
||||||
written += chunkSize
|
|
||||||
p = p[chunkSize:]
|
p = p[chunkSize:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return written, nil
|
if _, err := c.Conn.Write(sendBuffer.Bytes()); err != nil {
|
||||||
|
return 0, err // nolint: wrapcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
return lenP, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,19 @@
|
|||||||
package relay
|
package relay
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"fmt"
|
||||||
"io"
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type conn struct {
|
type conn struct {
|
||||||
io.ReadWriteCloser
|
net.Conn
|
||||||
|
|
||||||
ctx context.Context
|
|
||||||
tickChannel chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c conn) Read(p []byte) (int, error) {
|
func (c conn) Read(p []byte) (int, error) {
|
||||||
n, err := c.ReadWriteCloser.Read(p)
|
if err := c.SetReadDeadline(time.Now().Add(getTimeout())); err != nil {
|
||||||
|
return 0, fmt.Errorf("cannot set read deadline: %w", err)
|
||||||
select {
|
|
||||||
case <-c.ctx.Done():
|
|
||||||
case c.tickChannel <- struct{}{}:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return n, err // nolint: wrapcheck
|
return c.Conn.Read(p) // nolint: wrapcheck
|
||||||
}
|
|
||||||
|
|
||||||
func (c conn) Write(p []byte) (int, error) {
|
|
||||||
n, err := c.ReadWriteCloser.Write(p)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-c.ctx.Done():
|
|
||||||
case c.tickChannel <- struct{}{}:
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, err // nolint: wrapcheck
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConnTestSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
|
|
||||||
ctxCancel context.CancelFunc
|
|
||||||
connMock *testlib.NetConnMock
|
|
||||||
tickChannel chan struct{}
|
|
||||||
buf []byte
|
|
||||||
c conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) SetupTest() {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
suite.tickChannel = make(chan struct{}, 1)
|
|
||||||
suite.connMock = &testlib.NetConnMock{}
|
|
||||||
suite.ctxCancel = cancel
|
|
||||||
suite.buf = make([]byte, 5)
|
|
||||||
|
|
||||||
suite.c = conn{
|
|
||||||
ReadWriteCloser: suite.connMock,
|
|
||||||
ctx: ctx,
|
|
||||||
tickChannel: suite.tickChannel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) TestReadOk() {
|
|
||||||
suite.connMock.On("Read", mock.Anything).Once().Return(len(suite.buf), nil)
|
|
||||||
|
|
||||||
n, err := suite.c.Read(suite.buf)
|
|
||||||
suite.NoError(err)
|
|
||||||
suite.Equal(len(suite.buf), n)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-suite.tickChannel:
|
|
||||||
default:
|
|
||||||
suite.FailNow("cannot find a tick event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) TestReadErr() {
|
|
||||||
suite.connMock.On("Read", mock.Anything).Once().Return(0, io.EOF)
|
|
||||||
|
|
||||||
_, err := suite.c.Read(suite.buf)
|
|
||||||
suite.True(errors.Is(err, io.EOF))
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-suite.tickChannel:
|
|
||||||
default:
|
|
||||||
suite.FailNow("cannot find a tick event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) TestReadContextDone() {
|
|
||||||
suite.connMock.On("Read", mock.Anything).Once().Return(len(suite.buf), nil)
|
|
||||||
suite.ctxCancel()
|
|
||||||
|
|
||||||
suite.tickChannel <- struct{}{}
|
|
||||||
|
|
||||||
suite.c.Read(suite.buf) // nolint: errcheck
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) TestWriteOk() {
|
|
||||||
suite.connMock.On("Write", mock.Anything).Once().Return(len(suite.buf), nil)
|
|
||||||
|
|
||||||
n, err := suite.c.Write(suite.buf)
|
|
||||||
suite.NoError(err)
|
|
||||||
suite.Equal(len(suite.buf), n)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-suite.tickChannel:
|
|
||||||
default:
|
|
||||||
suite.FailNow("cannot find a tick event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) TestWriteErr() {
|
|
||||||
suite.connMock.On("Write", mock.Anything).Once().Return(0, io.EOF)
|
|
||||||
|
|
||||||
_, err := suite.c.Write(suite.buf)
|
|
||||||
suite.True(errors.Is(err, io.EOF))
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-suite.tickChannel:
|
|
||||||
default:
|
|
||||||
suite.FailNow("cannot find a tick event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) TestWriteContextDone() {
|
|
||||||
suite.connMock.On("Write", mock.Anything).Once().Return(len(suite.buf), nil)
|
|
||||||
suite.ctxCancel()
|
|
||||||
|
|
||||||
suite.tickChannel <- struct{}{}
|
|
||||||
|
|
||||||
suite.c.Write(suite.buf) // nolint: errcheck
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ConnTestSuite) TearDownTest() {
|
|
||||||
select {
|
|
||||||
case <-suite.tickChannel:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
close(suite.tickChannel)
|
|
||||||
|
|
||||||
suite.connMock.AssertExpectations(suite.T())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConn(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
suite.Run(t, &ConnTestSuite{})
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
package relay
|
package relay
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnectionTimeToLiveMin = 2 * time.Minute
|
||||||
|
ConnectionTimeToLiveMax = 10 * time.Minute
|
||||||
|
TimeoutMin = 20 * time.Second
|
||||||
|
TimeoutMax = time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
Printf(msg string, args ...interface{})
|
Printf(msg string, args ...interface{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,5 @@
|
|||||||
package relay_test
|
package relay_test
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type loggerMock struct{}
|
type loggerMock struct{}
|
||||||
|
|
||||||
func (l loggerMock) Printf(format string, args ...interface{}) {}
|
func (l loggerMock) Printf(format string, args ...interface{}) {}
|
||||||
|
|
||||||
type rwcMock struct {
|
|
||||||
bytes.Buffer
|
|
||||||
|
|
||||||
closed bool
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rwcMock) Read(p []byte) (int, error) {
|
|
||||||
r.mutex.Lock()
|
|
||||||
defer r.mutex.Unlock()
|
|
||||||
|
|
||||||
if r.closed {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Buffer.Read(p) // nolint: wrapcheck
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rwcMock) Write(p []byte) (int, error) {
|
|
||||||
r.mutex.Lock()
|
|
||||||
defer r.mutex.Unlock()
|
|
||||||
|
|
||||||
if r.closed {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Buffer.Write(p) // nolint: wrapcheck
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rwcMock) Close() error {
|
|
||||||
r.mutex.Lock()
|
|
||||||
defer r.mutex.Unlock()
|
|
||||||
|
|
||||||
r.closed = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,45 +1,32 @@
|
|||||||
package relay
|
package relay
|
||||||
|
|
||||||
import (
|
import "sync"
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var relayPool = sync.Pool{
|
type eastWest struct {
|
||||||
|
east []byte
|
||||||
|
west []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var eastWestPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
return &Relay{
|
return &eastWest{}
|
||||||
tickChannel: make(chan struct{}),
|
|
||||||
errorChannel: make(chan error, 1),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func AcquireRelay(ctx context.Context, logger Logger, bufferSize int, idleTimeout time.Duration) *Relay {
|
func acquireEastWest(bufferSize int) *eastWest {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
wanted := eastWestPool.Get().(*eastWest) // nolint: forcetypeassert
|
||||||
|
|
||||||
r, ok := relayPool.Get().(*Relay)
|
if len(wanted.east) != bufferSize {
|
||||||
if !ok {
|
wanted.east = make([]byte, bufferSize)
|
||||||
panic("Relay pool has no relay!")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ctx = ctx
|
if len(wanted.west) != bufferSize {
|
||||||
r.ctxCancel = cancel
|
wanted.west = make([]byte, bufferSize)
|
||||||
r.logger = logger
|
|
||||||
r.tickTimeout = idleTimeout
|
|
||||||
|
|
||||||
if len(r.eastBuffer) != bufferSize {
|
|
||||||
r.eastBuffer = make([]byte, bufferSize)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r.westBuffer) != bufferSize {
|
return wanted
|
||||||
r.westBuffer = make([]byte, bufferSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReleaseRelay(r *Relay) {
|
func releaseEastWest(ew *eastWest) {
|
||||||
r.Reset()
|
eastWestPool.Put(ew)
|
||||||
relayPool.Put(r)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-104
@@ -3,127 +3,48 @@ package relay
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Relay struct {
|
func Relay(ctx context.Context, log Logger, bufferSize int,
|
||||||
ctx context.Context
|
telegramConn net.Conn, clientConn io.ReadWriteCloser) {
|
||||||
ctxCancel context.CancelFunc
|
defer telegramConn.Close()
|
||||||
logger Logger
|
defer clientConn.Close()
|
||||||
processMutex sync.Mutex
|
|
||||||
eastBuffer []byte
|
|
||||||
westBuffer []byte
|
|
||||||
tickChannel chan struct{}
|
|
||||||
errorChannel chan error
|
|
||||||
tickTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Relay) Reset() {
|
ctx, cancel := context.WithTimeout(ctx, getConnectionTimeToLive())
|
||||||
r.processMutex.Lock()
|
defer cancel()
|
||||||
defer r.processMutex.Unlock()
|
|
||||||
|
|
||||||
if r.ctxCancel != nil {
|
go func() {
|
||||||
r.ctxCancel()
|
<-ctx.Done()
|
||||||
}
|
telegramConn.Close()
|
||||||
|
clientConn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
r.ctx = nil
|
buffers := acquireEastWest(bufferSize)
|
||||||
r.ctxCancel = nil
|
defer releaseEastWest(buffers)
|
||||||
r.logger = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Relay) Process(eastConn, westConn io.ReadWriteCloser) error {
|
telegramConn = conn{
|
||||||
r.processMutex.Lock()
|
Conn: telegramConn,
|
||||||
defer r.processMutex.Unlock()
|
|
||||||
|
|
||||||
eastConn = conn{
|
|
||||||
ReadWriteCloser: eastConn,
|
|
||||||
ctx: r.ctx,
|
|
||||||
tickChannel: r.tickChannel,
|
|
||||||
}
|
|
||||||
westConn = conn{
|
|
||||||
ReadWriteCloser: westConn,
|
|
||||||
ctx: r.ctx,
|
|
||||||
tickChannel: r.tickChannel,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(3) // nolint: gomnd
|
wg.Add(2) // nolint: gomnd
|
||||||
|
|
||||||
go r.runObserver(eastConn, westConn, wg)
|
go pump(log, telegramConn, clientConn, wg, buffers.east, "east -> west")
|
||||||
|
|
||||||
go r.transmit(eastConn, westConn, r.westBuffer, "west", wg)
|
pump(log, clientConn, telegramConn, wg, buffers.west, "west -> east")
|
||||||
|
|
||||||
r.transmit(westConn, eastConn, r.eastBuffer, "east", wg)
|
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
select {
|
|
||||||
case err := <-r.errorChannel:
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Relay) transmit(src io.ReadCloser, dst io.WriteCloser,
|
func pump(log Logger, src io.ReadCloser, dst io.WriteCloser, wg *sync.WaitGroup,
|
||||||
buffer []byte, direction string, wg *sync.WaitGroup) {
|
buf []byte, direction string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer src.Close()
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
defer func() {
|
if n, err := io.CopyBuffer(dst, src, buf); err != nil {
|
||||||
r.ctxCancel()
|
log.Printf("cannot pump %s (written %d bytes): %w", direction, n, err)
|
||||||
src.Close()
|
|
||||||
dst.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if _, err := io.CopyBuffer(dst, src, buffer); err != nil {
|
|
||||||
r.logger.Printf("error '%v' happened on direction %s", err, direction)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-r.ctx.Done():
|
|
||||||
err = r.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case r.errorChannel <- err:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Relay) runObserver(one, another io.Closer, wg *sync.WaitGroup) {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
one.Close()
|
|
||||||
another.Close()
|
|
||||||
|
|
||||||
ticker.Stop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
lastTickAt := time.Now()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-r.ctx.Done():
|
|
||||||
return
|
|
||||||
case <-r.tickChannel:
|
|
||||||
lastTickAt = time.Now()
|
|
||||||
case <-ticker.C:
|
|
||||||
if time.Since(lastTickAt) > r.tickTimeout {
|
|
||||||
r.logger.Printf("exit due to a timeout")
|
|
||||||
r.ctxCancel()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
"github.com/9seconds/mtg/v2/internal/testlib"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/relay"
|
"github.com/9seconds/mtg/v2/mtglib/internal/relay"
|
||||||
@@ -15,60 +14,40 @@ import (
|
|||||||
type RelayTestSuite struct {
|
type RelayTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
ctx context.Context
|
loggerMock relay.Logger
|
||||||
ctxCancel context.CancelFunc
|
ctx context.Context
|
||||||
r *relay.Relay
|
ctxCancel context.CancelFunc
|
||||||
|
telegramConnMock *testlib.NetConnMock
|
||||||
|
clientConnMock *testlib.NetConnMock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *RelayTestSuite) SetupTest() {
|
func (suite *RelayTestSuite) SetupTest() {
|
||||||
suite.ctx, suite.ctxCancel = context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
suite.r = relay.AcquireRelay(suite.ctx, loggerMock{}, 4096, time.Second)
|
suite.ctx = ctx
|
||||||
|
suite.ctxCancel = cancel
|
||||||
|
suite.loggerMock = &loggerMock{}
|
||||||
|
suite.telegramConnMock = &testlib.NetConnMock{}
|
||||||
|
suite.clientConnMock = &testlib.NetConnMock{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *RelayTestSuite) TearDownTest() {
|
func (suite *RelayTestSuite) TearDownTest() {
|
||||||
suite.ctxCancel()
|
suite.ctxCancel()
|
||||||
relay.ReleaseRelay(suite.r)
|
suite.telegramConnMock.AssertExpectations(suite.T())
|
||||||
suite.r = nil
|
suite.clientConnMock.AssertExpectations(suite.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *RelayTestSuite) TestCancelled() {
|
func (suite *RelayTestSuite) TestExit() {
|
||||||
suite.ctxCancel()
|
suite.telegramConnMock.On("SetReadDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.telegramConnMock.On("Close").Return(nil)
|
||||||
|
suite.telegramConnMock.On("Read", mock.Anything).Return(10, io.EOF).Once()
|
||||||
|
suite.telegramConnMock.On("Write", mock.Anything).Return(10, io.EOF).Maybe()
|
||||||
|
|
||||||
eastConn := &rwcMock{}
|
suite.clientConnMock.On("Read", mock.Anything).Return(0, io.EOF).Once()
|
||||||
eastConn.Write([]byte{1, 2, 3, 4, 5}) // nolint: errcheck
|
suite.clientConnMock.On("Write", mock.Anything).Return(10, io.EOF).Maybe()
|
||||||
|
suite.clientConnMock.On("Close").Return(nil)
|
||||||
|
|
||||||
westConn := &rwcMock{}
|
relay.Relay(suite.ctx, suite.loggerMock, 1024,
|
||||||
westConn.Write([]byte{100, 101, 102}) // nolint: errcheck
|
suite.telegramConnMock, suite.clientConnMock)
|
||||||
|
|
||||||
suite.Nil(suite.r.Process(eastConn, westConn))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *RelayTestSuite) TestCopyFine() {
|
|
||||||
eastConn := &rwcMock{}
|
|
||||||
eastConn.Write([]byte{1, 2, 3, 4, 5}) // nolint: errcheck
|
|
||||||
|
|
||||||
westConn := &rwcMock{}
|
|
||||||
westConn.Write([]byte{100, 101, 102}) // nolint: errcheck
|
|
||||||
|
|
||||||
// yes, this test is not good enough. but apparently, if it hangs,
|
|
||||||
// we can debug most of possible issues.
|
|
||||||
_ = suite.r.Process(eastConn, westConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *RelayTestSuite) TestTimeout() {
|
|
||||||
eastConn := &rwcMock{}
|
|
||||||
eastConn.Write([]byte{1, 2, 3, 4, 5}) // nolint: errcheck
|
|
||||||
|
|
||||||
westConn := &testlib.NetConnMock{}
|
|
||||||
westConn.On("Close").Return(nil)
|
|
||||||
westConn.On("Read", mock.Anything).Return(0, io.EOF).Run(func(_ mock.Arguments) {
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
})
|
|
||||||
westConn.On("Write", mock.Anything).Return(0, io.EOF).Run(func(_ mock.Arguments) {
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
})
|
|
||||||
|
|
||||||
suite.Error(suite.r.Process(eastConn, westConn))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRelay(t *testing.T) {
|
func TestRelay(t *testing.T) {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package relay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getConnectionTimeToLive() time.Duration {
|
||||||
|
return getTime(ConnectionTimeToLiveMin, ConnectionTimeToLiveMax)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimeout() time.Duration {
|
||||||
|
return getTime(TimeoutMin, TimeoutMax)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTime(minDuration, maxDuration time.Duration) time.Duration {
|
||||||
|
minDurationInSeconds := int(minDuration.Seconds())
|
||||||
|
maxDurationInSeconds := int(maxDuration.Seconds())
|
||||||
|
number := minDurationInSeconds + rand.Intn(maxDurationInSeconds-minDurationInSeconds)
|
||||||
|
|
||||||
|
return time.Duration(number) * time.Second
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package relay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TimeoutsTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TimeoutsTestSuite) TestGetConnectionTimeToLive() {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
value := getConnectionTimeToLive()
|
||||||
|
message := fmt.Sprintf("generated value is %v", value)
|
||||||
|
|
||||||
|
suite.GreaterOrEqual(value, ConnectionTimeToLiveMin, message)
|
||||||
|
suite.LessOrEqual(value, ConnectionTimeToLiveMax, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TimeoutsTestSuite) TestGetTimeout() {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
value := getTimeout()
|
||||||
|
message := fmt.Sprintf("generated value is %v", value)
|
||||||
|
|
||||||
|
suite.GreaterOrEqual(value, TimeoutMin, message)
|
||||||
|
suite.LessOrEqual(value, TimeoutMax, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeouts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
suite.Run(t, &TimeoutsTestSuite{})
|
||||||
|
}
|
||||||
+14
-16
@@ -23,7 +23,6 @@ type Proxy struct {
|
|||||||
ctxCancel context.CancelFunc
|
ctxCancel context.CancelFunc
|
||||||
streamWaitGroup sync.WaitGroup
|
streamWaitGroup sync.WaitGroup
|
||||||
|
|
||||||
idleTimeout time.Duration
|
|
||||||
tolerateTimeSkewness time.Duration
|
tolerateTimeSkewness time.Duration
|
||||||
bufferSize int
|
bufferSize int
|
||||||
domainFrontingPort int
|
domainFrontingPort int
|
||||||
@@ -81,13 +80,13 @@ func (p *Proxy) ServeConn(conn net.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rel := relay.AcquireRelay(ctx,
|
relay.Relay(
|
||||||
p.logger.Named("relay"), p.bufferSize, p.idleTimeout)
|
ctx,
|
||||||
defer relay.ReleaseRelay(rel)
|
ctx.logger.Named("relay"),
|
||||||
|
p.bufferSize,
|
||||||
if err := rel.Process(ctx.clientConn, ctx.telegramConn); err != nil {
|
ctx.telegramConn,
|
||||||
p.logger.DebugError("relay has been finished", err)
|
ctx.clientConn,
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve starts a proxy on a given listener.
|
// Serve starts a proxy on a given listener.
|
||||||
@@ -255,13 +254,13 @@ func (p *Proxy) doDomainFronting(ctx *streamContext, conn *connRewind) {
|
|||||||
stream: p.eventStream,
|
stream: p.eventStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
rel := relay.AcquireRelay(ctx,
|
relay.Relay(
|
||||||
p.logger.Named("domain-fronting"), p.bufferSize, p.idleTimeout)
|
ctx,
|
||||||
defer relay.ReleaseRelay(rel)
|
ctx.logger.Named("domain-fronting"),
|
||||||
|
p.bufferSize,
|
||||||
if err := rel.Process(conn, frontConn); err != nil {
|
frontConn,
|
||||||
p.logger.DebugError("domain fronting relay has been finished", err)
|
conn,
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProxy makes a new proxy instance.
|
// NewProxy makes a new proxy instance.
|
||||||
@@ -287,7 +286,6 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
|
|||||||
logger: opts.getLogger("proxy"),
|
logger: opts.getLogger("proxy"),
|
||||||
domainFrontingPort: opts.getDomainFrontingPort(),
|
domainFrontingPort: opts.getDomainFrontingPort(),
|
||||||
tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
|
tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
|
||||||
idleTimeout: opts.getIdleTimeout(),
|
|
||||||
bufferSize: opts.getBufferSize(),
|
bufferSize: opts.getBufferSize(),
|
||||||
telegram: tg,
|
telegram: tg,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,14 +143,6 @@ func (p ProxyOpts) getDomainFrontingPort() int {
|
|||||||
return int(p.DomainFrontingPort)
|
return int(p.DomainFrontingPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ProxyOpts) getIdleTimeout() time.Duration {
|
|
||||||
if p.IdleTimeout == 0 {
|
|
||||||
return DefaultIdleTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.IdleTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p ProxyOpts) getTolerateTimeSkewness() time.Duration {
|
func (p ProxyOpts) getTolerateTimeSkewness() time.Duration {
|
||||||
if p.TolerateTimeSkewness == 0 {
|
if p.TolerateTimeSkewness == 0 {
|
||||||
return DefaultTolerateTimeSkewness
|
return DefaultTolerateTimeSkewness
|
||||||
|
|||||||
+4
-29
@@ -5,8 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/libp2p/go-reuseport"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type defaultDialer struct {
|
type defaultDialer struct {
|
||||||
@@ -31,36 +29,14 @@ func (d *defaultDialer) DialContext(ctx context.Context, network, address string
|
|||||||
return nil, fmt.Errorf("cannot dial to %s: %w", address, err)
|
return nil, fmt.Errorf("cannot dial to %s: %w", address, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tcpConn, ok := conn.(*net.TCPConn)
|
// we do not need to call to end user. End users call us.
|
||||||
if !ok {
|
if err := SetServerSocketOptions(conn, d.bufferSize); err != nil {
|
||||||
panic("conn type is not tcp")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tcpConn.SetNoDelay(true); err != nil {
|
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
|
||||||
return nil, fmt.Errorf("cannot set TCP_NO_DELAY: %w", err)
|
return nil, fmt.Errorf("cannot set socket options: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tcpConn.SetReadBuffer(d.bufferSize); err != nil {
|
return conn, nil
|
||||||
tcpConn.Close()
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("cannot set read buffer size: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tcpConn.SetWriteBuffer(d.bufferSize); err != nil {
|
|
||||||
tcpConn.Close()
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("cannot set write buffer size: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tcpConn.SetKeepAlive(true); err != nil {
|
|
||||||
tcpConn.Close()
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("cannot enable keep-alive: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tcpConn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultDialer build a new dialer which dials bypassing proxies
|
// NewDefaultDialer build a new dialer which dials bypassing proxies
|
||||||
@@ -87,7 +63,6 @@ func NewDefaultDialer(timeout time.Duration, bufferSize int) (Dialer, error) {
|
|||||||
return &defaultDialer{
|
return &defaultDialer{
|
||||||
Dialer: net.Dialer{
|
Dialer: net.Dialer{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Control: reuseport.Control,
|
|
||||||
},
|
},
|
||||||
bufferSize: bufferSize,
|
bufferSize: bufferSize,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ const (
|
|||||||
|
|
||||||
// DNSTimeout defines a timeout for DNS queries.
|
// DNSTimeout defines a timeout for DNS queries.
|
||||||
DNSTimeout = 5 * time.Second
|
DNSTimeout = 5 * time.Second
|
||||||
|
|
||||||
|
// tcpLingerTimeout defines a number of seconds to wait for sending
|
||||||
|
// unacknowledged data.
|
||||||
|
tcpLingerTimeout = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetClientSocketOptions tunes a TCP socket that represents a connection to
|
||||||
|
// end user (not Telegram service or fronting domain).
|
||||||
|
func SetClientSocketOptions(conn net.Conn, bufferSize int) error {
|
||||||
|
tcpConn := conn.(*net.TCPConn) // nolint: forcetypeassert
|
||||||
|
|
||||||
|
if err := tcpConn.SetNoDelay(false); err != nil {
|
||||||
|
return fmt.Errorf("cannot disable TCP_NO_DELAY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return setCommonSocketOptions(tcpConn, bufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetServerSocketOptions tunes a TCP socket that represents a connection to
|
||||||
|
// remote server like Telegram or fronting domain (but not end user).
|
||||||
|
func SetServerSocketOptions(conn net.Conn, bufferSize int) error {
|
||||||
|
tcpConn := conn.(*net.TCPConn) // nolint: forcetypeassert
|
||||||
|
|
||||||
|
if err := tcpConn.SetNoDelay(true); err != nil {
|
||||||
|
return fmt.Errorf("cannot enable TCP_NO_DELAY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return setCommonSocketOptions(tcpConn, bufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCommonSocketOptions(conn *net.TCPConn, bufferSize int) error {
|
||||||
|
if err := conn.SetReadBuffer(bufferSize); err != nil {
|
||||||
|
return fmt.Errorf("cannot set read buffer size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conn.SetWriteBuffer(bufferSize); err != nil {
|
||||||
|
return fmt.Errorf("cannot set write buffer size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conn.SetKeepAlive(false); err != nil {
|
||||||
|
return fmt.Errorf("cannot disable TCP keepalive probes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conn.SetLinger(tcpLingerTimeout); err != nil {
|
||||||
|
return fmt.Errorf("cannot set TCP linger timeout: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawConn, err := conn.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot get underlying raw connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
rawConn.Control(func(fd uintptr) { // nolint: errcheck
|
||||||
|
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("cannot set SO_REUSEADDR: %w", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("cannot set SO_REUSEPORT: %w", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user