diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7a12ddb..5a08413 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,7 +30,8 @@ jobs: strategy: matrix: go_version: - - ^1.16 + - ~1.16 + - ^1.17 steps: - name: Checkout uses: actions/checkout@v2 @@ -70,7 +71,7 @@ jobs: - name: Run linter uses: golangci/golangci-lint-action@v2 with: - version: v1.41.1 + version: v1.42.0 docker: name: Docker diff --git a/Dockerfile b/Dockerfile index 4a71943..08003f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ############################################################################### # BUILD STAGE -FROM golang:1.16-alpine AS build +FROM golang:1.17-alpine AS build RUN set -x \ && apk --no-cache --update add \ diff --git a/Makefile b/Makefile index 137c111..b3aed6e 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) IMAGE_NAME := mtg APP_NAME := $(IMAGE_NAME) -GOLANGCI_LINT_VERSION := v1.41.1 +GOLANGCI_LINT_VERSION := v1.42.0 VERSION_GO := $(shell go version) VERSION_DATE := $(shell date -Ru) diff --git a/go.mod b/go.mod index 7bb69b7..7552dcf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/9seconds/mtg/v2 -go 1.16 +go 1.17 require ( github.com/OneOfOne/xxhash v1.2.8 @@ -13,14 +13,13 @@ require ( github.com/gotd/td v0.34.0 github.com/jarcoal/httpmock v1.0.8 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/panjf2000/ants/v2 v2.4.6 github.com/pelletier/go-toml v1.9.3 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/common v0.30.0 // indirect - github.com/prometheus/procfs v0.7.1 // indirect - github.com/rs/zerolog v1.23.0 + github.com/prometheus/procfs v0.7.3 // indirect + github.com/rs/zerolog v1.24.0 github.com/smira/go-statsd v1.3.2 github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.7.0 @@ -30,3 +29,22 @@ require ( golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c 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 +) diff --git a/go.sum b/go.sum index ff2bf92..db4a509 100644 --- a/go.sum +++ b/go.sum @@ -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/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= 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.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/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 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.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.7.1 h1:TlEtJq5GvGqMykEwWzbZWjjztF86swFhsPix1i0bkgA= -github.com/prometheus/procfs v0.7.1/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +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/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/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g= -github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.24.0 h1:76ivFxmVSRs1u2wUwJVg5VZDYQgeH1JpoS6ndgr9Wy8= +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/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 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.27/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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 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.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.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-20180724234803-3673e40ba225/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-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-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-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= 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-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-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-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-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-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-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-20190412213103-97732733099d/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-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-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-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-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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20200820010801-b793a1359eac/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.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= +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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/cli/run_proxy.go b/internal/cli/run_proxy.go index 95507e4..dd08265 100644 --- a/internal/cli/run_proxy.go +++ b/internal/cli/run_proxy.go @@ -183,7 +183,6 @@ func runProxy(conf *config.Config, version string) error { Secret: conf.Secret, BufferSize: conf.TCPBuffer.Get(mtglib.DefaultBufferSize), DomainFrontingPort: conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort), - IdleTimeout: conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout), 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) } - listener, err := net.Listen("tcp", conf.BindTo.Get("")) + listener, err := utils.NewListener(conf.BindTo.Get(""), int(opts.BufferSize)) if err != nil { return fmt.Errorf("cannot start proxy: %w", err) } diff --git a/internal/utils/net_listener.go b/internal/utils/net_listener.go new file mode 100644 index 0000000..c168e88 --- /dev/null +++ b/internal/utils/net_listener.go @@ -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 +} diff --git a/mtglib/init.go b/mtglib/init.go index d4f265b..11a2fdf 100644 --- a/mtglib/init.go +++ b/mtglib/init.go @@ -69,6 +69,9 @@ const ( // DefaultIdleTimeout is a default timeout for closing a connection // in case of idling. + // + // Deprecated: no longer in use because of changed TCP relay + // algorithm. DefaultIdleTimeout = time.Minute // DefaultTolerateTimeSkewness is a default timeout for time @@ -83,9 +86,14 @@ const ( // by Telegram and a proxy. SecretKeyLength = 16 - // ConnectionIDBytesLength defines a count of random bytes - // used to generate a stream/connection ids. + // ConnectionIDBytesLength defines a count of random bytes used to generate + // a stream/connection ids. 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 diff --git a/mtglib/internal/faketls/conn.go b/mtglib/internal/faketls/conn.go index 868563e..d7e7837 100644 --- a/mtglib/internal/faketls/conn.go +++ b/mtglib/internal/faketls/conn.go @@ -46,7 +46,11 @@ func (c *Conn) Write(p []byte) (int, error) { rec.Type = record.TypeApplicationData rec.Version = record.Version12 - written := 0 + + sendBuffer := acquireBytesBuffer() + defer releaseBytesBuffer(sendBuffer) + + lenP := len(p) for len(p) > 0 { chunkSize := rand.Intn(record.TLSMaxRecordSize) @@ -56,14 +60,14 @@ func (c *Conn) Write(p []byte) (int, error) { rec.Payload.Reset() 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:] } - return written, nil + if _, err := c.Conn.Write(sendBuffer.Bytes()); err != nil { + return 0, err // nolint: wrapcheck + } + + return lenP, nil } diff --git a/mtglib/internal/relay/conn.go b/mtglib/internal/relay/conn.go index 31f8bb2..cdeaff1 100644 --- a/mtglib/internal/relay/conn.go +++ b/mtglib/internal/relay/conn.go @@ -1,35 +1,19 @@ package relay import ( - "context" - "io" + "fmt" + "net" + "time" ) type conn struct { - io.ReadWriteCloser - - ctx context.Context - tickChannel chan struct{} + net.Conn } func (c conn) Read(p []byte) (int, error) { - n, err := c.ReadWriteCloser.Read(p) - - select { - case <-c.ctx.Done(): - case c.tickChannel <- struct{}{}: + if err := c.SetReadDeadline(time.Now().Add(getTimeout())); err != nil { + return 0, fmt.Errorf("cannot set read deadline: %w", err) } - return n, err // 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 + return c.Conn.Read(p) // nolint: wrapcheck } diff --git a/mtglib/internal/relay/conn_internal_test.go b/mtglib/internal/relay/conn_internal_test.go deleted file mode 100644 index 86bd81b..0000000 --- a/mtglib/internal/relay/conn_internal_test.go +++ /dev/null @@ -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{}) -} diff --git a/mtglib/internal/relay/init.go b/mtglib/internal/relay/init.go index c381a4a..df1f720 100644 --- a/mtglib/internal/relay/init.go +++ b/mtglib/internal/relay/init.go @@ -1,5 +1,14 @@ package relay +import "time" + +const ( + ConnectionTimeToLiveMin = 2 * time.Minute + ConnectionTimeToLiveMax = 10 * time.Minute + TimeoutMin = 20 * time.Second + TimeoutMax = time.Minute +) + type Logger interface { Printf(msg string, args ...interface{}) } diff --git a/mtglib/internal/relay/init_test.go b/mtglib/internal/relay/init_test.go index d42b825..e43244a 100644 --- a/mtglib/internal/relay/init_test.go +++ b/mtglib/internal/relay/init_test.go @@ -1,49 +1,5 @@ package relay_test -import ( - "bytes" - "io" - "sync" -) - type loggerMock struct{} 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 -} diff --git a/mtglib/internal/relay/pools.go b/mtglib/internal/relay/pools.go index 091fa8e..0f0a34a 100644 --- a/mtglib/internal/relay/pools.go +++ b/mtglib/internal/relay/pools.go @@ -1,45 +1,32 @@ package relay -import ( - "context" - "sync" - "time" -) +import "sync" -var relayPool = sync.Pool{ +type eastWest struct { + east []byte + west []byte +} + +var eastWestPool = sync.Pool{ New: func() interface{} { - return &Relay{ - tickChannel: make(chan struct{}), - errorChannel: make(chan error, 1), - } + return &eastWest{} }, } -func AcquireRelay(ctx context.Context, logger Logger, bufferSize int, idleTimeout time.Duration) *Relay { - ctx, cancel := context.WithCancel(ctx) +func acquireEastWest(bufferSize int) *eastWest { + wanted := eastWestPool.Get().(*eastWest) // nolint: forcetypeassert - r, ok := relayPool.Get().(*Relay) - if !ok { - panic("Relay pool has no relay!") + if len(wanted.east) != bufferSize { + wanted.east = make([]byte, bufferSize) } - r.ctx = ctx - r.ctxCancel = cancel - r.logger = logger - r.tickTimeout = idleTimeout - - if len(r.eastBuffer) != bufferSize { - r.eastBuffer = make([]byte, bufferSize) + if len(wanted.west) != bufferSize { + wanted.west = make([]byte, bufferSize) } - if len(r.westBuffer) != bufferSize { - r.westBuffer = make([]byte, bufferSize) - } - - return r + return wanted } -func ReleaseRelay(r *Relay) { - r.Reset() - relayPool.Put(r) +func releaseEastWest(ew *eastWest) { + eastWestPool.Put(ew) } diff --git a/mtglib/internal/relay/relay.go b/mtglib/internal/relay/relay.go index 04cda40..223f9bc 100644 --- a/mtglib/internal/relay/relay.go +++ b/mtglib/internal/relay/relay.go @@ -3,127 +3,48 @@ package relay import ( "context" "io" + "net" "sync" - "time" ) -type Relay struct { - ctx context.Context - ctxCancel context.CancelFunc - logger Logger - processMutex sync.Mutex - eastBuffer []byte - westBuffer []byte - tickChannel chan struct{} - errorChannel chan error - tickTimeout time.Duration -} +func Relay(ctx context.Context, log Logger, bufferSize int, + telegramConn net.Conn, clientConn io.ReadWriteCloser) { + defer telegramConn.Close() + defer clientConn.Close() -func (r *Relay) Reset() { - r.processMutex.Lock() - defer r.processMutex.Unlock() + ctx, cancel := context.WithTimeout(ctx, getConnectionTimeToLive()) + defer cancel() - if r.ctxCancel != nil { - r.ctxCancel() - } + go func() { + <-ctx.Done() + telegramConn.Close() + clientConn.Close() + }() - r.ctx = nil - r.ctxCancel = nil - r.logger = nil -} + buffers := acquireEastWest(bufferSize) + defer releaseEastWest(buffers) -func (r *Relay) Process(eastConn, westConn io.ReadWriteCloser) error { - r.processMutex.Lock() - defer r.processMutex.Unlock() - - eastConn = conn{ - ReadWriteCloser: eastConn, - ctx: r.ctx, - tickChannel: r.tickChannel, - } - westConn = conn{ - ReadWriteCloser: westConn, - ctx: r.ctx, - tickChannel: r.tickChannel, + telegramConn = conn{ + Conn: telegramConn, } 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) - - r.transmit(westConn, eastConn, r.eastBuffer, "east", wg) + pump(log, clientConn, telegramConn, wg, buffers.west, "west -> east") wg.Wait() - - select { - case err := <-r.errorChannel: - return err - default: - return nil - } } -func (r *Relay) transmit(src io.ReadCloser, dst io.WriteCloser, - buffer []byte, direction string, wg *sync.WaitGroup) { +func pump(log Logger, src io.ReadCloser, dst io.WriteCloser, wg *sync.WaitGroup, + buf []byte, direction string) { defer wg.Done() + defer src.Close() + defer dst.Close() - defer func() { - r.ctxCancel() - 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 - } - } + if n, err := io.CopyBuffer(dst, src, buf); err != nil { + log.Printf("cannot pump %s (written %d bytes): %w", direction, n, err) } } diff --git a/mtglib/internal/relay/relay_test.go b/mtglib/internal/relay/relay_test.go index 9e3f19c..d1b5362 100644 --- a/mtglib/internal/relay/relay_test.go +++ b/mtglib/internal/relay/relay_test.go @@ -4,7 +4,6 @@ import ( "context" "io" "testing" - "time" "github.com/9seconds/mtg/v2/internal/testlib" "github.com/9seconds/mtg/v2/mtglib/internal/relay" @@ -15,60 +14,40 @@ import ( type RelayTestSuite struct { suite.Suite - ctx context.Context - ctxCancel context.CancelFunc - r *relay.Relay + loggerMock relay.Logger + ctx context.Context + ctxCancel context.CancelFunc + telegramConnMock *testlib.NetConnMock + clientConnMock *testlib.NetConnMock } func (suite *RelayTestSuite) SetupTest() { - suite.ctx, suite.ctxCancel = context.WithCancel(context.Background()) - suite.r = relay.AcquireRelay(suite.ctx, loggerMock{}, 4096, time.Second) + ctx, cancel := context.WithCancel(context.Background()) + suite.ctx = ctx + suite.ctxCancel = cancel + suite.loggerMock = &loggerMock{} + suite.telegramConnMock = &testlib.NetConnMock{} + suite.clientConnMock = &testlib.NetConnMock{} } func (suite *RelayTestSuite) TearDownTest() { suite.ctxCancel() - relay.ReleaseRelay(suite.r) - suite.r = nil + suite.telegramConnMock.AssertExpectations(suite.T()) + suite.clientConnMock.AssertExpectations(suite.T()) } -func (suite *RelayTestSuite) TestCancelled() { - suite.ctxCancel() +func (suite *RelayTestSuite) TestExit() { + 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{} - eastConn.Write([]byte{1, 2, 3, 4, 5}) // nolint: errcheck + suite.clientConnMock.On("Read", mock.Anything).Return(0, io.EOF).Once() + suite.clientConnMock.On("Write", mock.Anything).Return(10, io.EOF).Maybe() + suite.clientConnMock.On("Close").Return(nil) - westConn := &rwcMock{} - westConn.Write([]byte{100, 101, 102}) // nolint: errcheck - - 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)) + relay.Relay(suite.ctx, suite.loggerMock, 1024, + suite.telegramConnMock, suite.clientConnMock) } func TestRelay(t *testing.T) { diff --git a/mtglib/internal/relay/timeouts.go b/mtglib/internal/relay/timeouts.go new file mode 100644 index 0000000..54ec753 --- /dev/null +++ b/mtglib/internal/relay/timeouts.go @@ -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 +} diff --git a/mtglib/internal/relay/timeouts_internal_test.go b/mtglib/internal/relay/timeouts_internal_test.go new file mode 100644 index 0000000..47e49e4 --- /dev/null +++ b/mtglib/internal/relay/timeouts_internal_test.go @@ -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{}) +} diff --git a/mtglib/proxy.go b/mtglib/proxy.go index 9a34fdd..b0a21db 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -23,7 +23,6 @@ type Proxy struct { ctxCancel context.CancelFunc streamWaitGroup sync.WaitGroup - idleTimeout time.Duration tolerateTimeSkewness time.Duration bufferSize int domainFrontingPort int @@ -81,13 +80,13 @@ func (p *Proxy) ServeConn(conn net.Conn) { return } - rel := relay.AcquireRelay(ctx, - p.logger.Named("relay"), p.bufferSize, p.idleTimeout) - defer relay.ReleaseRelay(rel) - - if err := rel.Process(ctx.clientConn, ctx.telegramConn); err != nil { - p.logger.DebugError("relay has been finished", err) - } + relay.Relay( + ctx, + ctx.logger.Named("relay"), + p.bufferSize, + ctx.telegramConn, + ctx.clientConn, + ) } // Serve starts a proxy on a given listener. @@ -255,13 +254,13 @@ func (p *Proxy) doDomainFronting(ctx *streamContext, conn *connRewind) { stream: p.eventStream, } - rel := relay.AcquireRelay(ctx, - p.logger.Named("domain-fronting"), p.bufferSize, p.idleTimeout) - defer relay.ReleaseRelay(rel) - - if err := rel.Process(conn, frontConn); err != nil { - p.logger.DebugError("domain fronting relay has been finished", err) - } + relay.Relay( + ctx, + ctx.logger.Named("domain-fronting"), + p.bufferSize, + frontConn, + conn, + ) } // NewProxy makes a new proxy instance. @@ -287,7 +286,6 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) { logger: opts.getLogger("proxy"), domainFrontingPort: opts.getDomainFrontingPort(), tolerateTimeSkewness: opts.getTolerateTimeSkewness(), - idleTimeout: opts.getIdleTimeout(), bufferSize: opts.getBufferSize(), telegram: tg, } diff --git a/mtglib/proxy_opts.go b/mtglib/proxy_opts.go index 2eac74e..8d1f6b5 100644 --- a/mtglib/proxy_opts.go +++ b/mtglib/proxy_opts.go @@ -143,14 +143,6 @@ func (p ProxyOpts) getDomainFrontingPort() int { 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 { if p.TolerateTimeSkewness == 0 { return DefaultTolerateTimeSkewness diff --git a/network/default.go b/network/default.go index 7ade79a..e976daa 100644 --- a/network/default.go +++ b/network/default.go @@ -5,8 +5,6 @@ import ( "fmt" "net" "time" - - "github.com/libp2p/go-reuseport" ) 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) } - tcpConn, ok := conn.(*net.TCPConn) - if !ok { - panic("conn type is not tcp") - } - - if err := tcpConn.SetNoDelay(true); err != nil { + // we do not need to call to end user. End users call us. + if err := SetServerSocketOptions(conn, d.bufferSize); err != nil { 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 { - 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 + return conn, nil } // NewDefaultDialer build a new dialer which dials bypassing proxies @@ -87,7 +63,6 @@ func NewDefaultDialer(timeout time.Duration, bufferSize int) (Dialer, error) { return &defaultDialer{ Dialer: net.Dialer{ Timeout: timeout, - Control: reuseport.Control, }, bufferSize: bufferSize, }, nil diff --git a/network/init.go b/network/init.go index b53f37c..c12c7a0 100644 --- a/network/init.go +++ b/network/init.go @@ -70,6 +70,10 @@ const ( // DNSTimeout defines a timeout for DNS queries. DNSTimeout = 5 * time.Second + + // tcpLingerTimeout defines a number of seconds to wait for sending + // unacknowledged data. + tcpLingerTimeout = 1 ) var ( diff --git a/network/sockopts.go b/network/sockopts.go new file mode 100644 index 0000000..e737513 --- /dev/null +++ b/network/sockopts.go @@ -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 +}