Skip to content

Commit ff7f15b

Browse files
author
Hannes Tribus
committed
Add configurable keepalive
1 parent 3723dbe commit ff7f15b

18 files changed

+191
-120
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ release: check test clean build package
9292
build:
9393
mkdir -p ${OUTPUT_DIR}
9494
CGO_ENABLED=0 GOARM=5 gox -ldflags "-w -X main.version=$(GIT_COMMIT)" \
95-
-os=${OS} -arch=${ARCH} -osarch=${OSARCH} -output "${OUTPUT_DIR}/pkg/{{.OS}}_{{.Arch}}/{{.Dir}}" \
95+
-mod=vendor -os=${OS} -arch=${ARCH} -osarch=${OSARCH} -output "${OUTPUT_DIR}/pkg/{{.OS}}_{{.Arch}}/{{.Dir}}" \
9696
./cmd/tunnel ./cmd/tunneld
9797

9898
.PHONY: package

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ Configuration options:
173173
* `multiplier`: interval multiplier if reconnect failed, *default:* `1.5`
174174
* `max_interval`: maximal time client would wait before redialing the server, *default:* `1m`
175175
* `max_time`: maximal time client would try to reconnect to the server if connection was lost, set `0` to never stop trying, *default:* `15m`
176+
* `keep_alive`**
177+
* `idle_time`: how long to wait on an idle tcp connection before sending a keepalive packet, *default:* `15 min`
178+
* `count`: how many keepalive packets to send before declaring that the tcp connection is down, *default:* `8`
179+
* `interval`: the amount of time to wait between sending consequent keepalive packets, *default:* `5 sec`
180+
181+
\** Keep alive configuration not available for window since on windows it can only be either on or off.
182+
It is defaulted to on and cannot be turned off via configuration.
176183

177184
## Configuration - Server
178185

backoff.go

Lines changed: 0 additions & 19 deletions
This file was deleted.

client.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"golang.org/x/net/http2"
1818

19+
"github.com/hons82/go-http-tunnel/connection"
1920
"github.com/hons82/go-http-tunnel/log"
2021
"github.com/hons82/go-http-tunnel/proto"
2122
)
@@ -32,14 +33,16 @@ type ClientConfig struct {
3233
DialTLS func(network, addr string, config *tls.Config) (net.Conn, error)
3334
// Backoff specifies backoff policy on server connection retry. If nil
3435
// when dial fails it will not be retried.
35-
Backoff Backoff
36+
Backoff connection.Backoff
3637
// Tunnels specifies the tunnels client requests to be opened on server.
3738
Tunnels map[string]*proto.Tunnel
3839
// Proxy is ProxyFunc responsible for transferring data between server
3940
// and local services.
4041
Proxy ProxyFunc
4142
// Logger is optional logger. If nil logging is disabled.
4243
Logger log.Logger
44+
// Used to configure the tcp keepalive for the client -> server tcp connection
45+
KeepAlive connection.KeepAliveConfig
4346
}
4447

4548
// Client is responsible for creating connection to the server, handling control
@@ -172,7 +175,11 @@ func (c *Client) dial() (net.Conn, error) {
172175
conn, err = d.Dial(network, addr)
173176

174177
if err == nil {
175-
err = keepAlive(conn)
178+
c.logger.Log(
179+
"level", 1,
180+
"msg", fmt.Sprintf("Setting up keep alive using config: %v", c.config.KeepAlive.String()),
181+
)
182+
err = c.config.KeepAlive.Set(conn)
176183
}
177184
if err == nil {
178185
conn = tls.Client(conn, tlsConfig)

client_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
"github.com/golang/mock/gomock"
16+
"github.com/hons82/go-http-tunnel/connection"
1617
"github.com/hons82/go-http-tunnel/proto"
1718
"github.com/hons82/go-http-tunnel/tunnelmock"
1819
)
@@ -30,6 +31,9 @@ func TestClient_Dial(t *testing.T) {
3031
},
3132
Tunnels: map[string]*proto.Tunnel{"test": {}},
3233
Proxy: Proxy(ProxyFuncs{}),
34+
KeepAlive: connection.KeepAliveConfig{
35+
KeepAliveInterval: connection.DefaultKeepAliveInterval,
36+
},
3337
})
3438
if err != nil {
3539
t.Fatal(err)
@@ -68,6 +72,9 @@ func TestClient_DialBackoff(t *testing.T) {
6872
Backoff: b,
6973
Tunnels: map[string]*proto.Tunnel{"test": {}},
7074
Proxy: Proxy(ProxyFuncs{}),
75+
KeepAlive: connection.KeepAliveConfig{
76+
KeepAliveInterval: connection.DefaultKeepAliveInterval,
77+
},
7178
})
7279
if err != nil {
7380
t.Fatal(err)

cmd/tunnel/config.go

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,13 @@ import (
88
"fmt"
99
"io/ioutil"
1010
"path/filepath"
11-
"time"
1211

1312
"gopkg.in/yaml.v2"
1413

14+
"github.com/hons82/go-http-tunnel/connection"
1515
"github.com/hons82/go-http-tunnel/proto"
1616
)
1717

18-
// Default backoff configuration.
19-
const (
20-
DefaultBackoffInterval = 500 * time.Millisecond
21-
DefaultBackoffMultiplier = 1.5
22-
DefaultBackoffMaxInterval = 60 * time.Second
23-
DefaultBackoffMaxTime = 15 * time.Minute
24-
)
25-
26-
// BackoffConfig defines behavior of staggering reconnection retries.
27-
type BackoffConfig struct {
28-
Interval time.Duration `yaml:"interval"`
29-
Multiplier float64 `yaml:"multiplier"`
30-
MaxInterval time.Duration `yaml:"max_interval"`
31-
MaxTime time.Duration `yaml:"max_time"`
32-
}
33-
3418
// Tunnel defines a tunnel.
3519
type Tunnel struct {
3620
Protocol string `yaml:"proto,omitempty"`
@@ -42,12 +26,13 @@ type Tunnel struct {
4226

4327
// ClientConfig is a tunnel client configuration.
4428
type ClientConfig struct {
45-
ServerAddr string `yaml:"server_addr"`
46-
TLSCrt string `yaml:"tls_crt"`
47-
TLSKey string `yaml:"tls_key"`
48-
RootCA string `yaml:"root_ca"`
49-
Backoff BackoffConfig `yaml:"backoff"`
50-
Tunnels map[string]*Tunnel `yaml:"tunnels"`
29+
ServerAddr string `yaml:"server_addr"`
30+
TLSCrt string `yaml:"tls_crt"`
31+
TLSKey string `yaml:"tls_key"`
32+
RootCA string `yaml:"root_ca"`
33+
Tunnels map[string]*Tunnel `yaml:"tunnels"`
34+
Backoff connection.BackoffConfig `yaml:"backoff"`
35+
KeepAlive connection.KeepAliveConfig `yaml:"keep_alive"`
5136
}
5237

5338
func loadClientConfigFromFile(file string) (*ClientConfig, error) {
@@ -57,14 +42,10 @@ func loadClientConfigFromFile(file string) (*ClientConfig, error) {
5742
}
5843

5944
c := ClientConfig{
60-
TLSCrt: filepath.Join(filepath.Dir(file), "client.crt"),
61-
TLSKey: filepath.Join(filepath.Dir(file), "client.key"),
62-
Backoff: BackoffConfig{
63-
Interval: DefaultBackoffInterval,
64-
Multiplier: DefaultBackoffMultiplier,
65-
MaxInterval: DefaultBackoffMaxInterval,
66-
MaxTime: DefaultBackoffMaxTime,
67-
},
45+
TLSCrt: filepath.Join(filepath.Dir(file), "client.crt"),
46+
TLSKey: filepath.Join(filepath.Dir(file), "client.key"),
47+
Backoff: *connection.NewDefaultBackoffConfig(),
48+
KeepAlive: *connection.NewDefaultKeepAliveConfig(),
6849
}
6950

7051
if err = yaml.Unmarshal(buf, &c); err != nil {

cmd/tunnel/tunnel.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import (
1818
"gopkg.in/yaml.v2"
1919

2020
"github.com/cenkalti/backoff"
21-
"github.com/hons82/go-http-tunnel"
21+
tunnel "github.com/hons82/go-http-tunnel"
22+
"github.com/hons82/go-http-tunnel/connection"
2223
"github.com/hons82/go-http-tunnel/id"
2324
"github.com/hons82/go-http-tunnel/log"
2425
"github.com/hons82/go-http-tunnel/proto"
@@ -108,6 +109,7 @@ func main() {
108109
Tunnels: tunnels(config.Tunnels),
109110
Proxy: proxy(config.Tunnels, logger),
110111
Logger: logger,
112+
KeepAlive: config.KeepAlive,
111113
})
112114
if err != nil {
113115
fatal("failed to create client: %s", err)
@@ -149,7 +151,7 @@ func tlsConfig(config *ClientConfig) (*tls.Config, error) {
149151
}, nil
150152
}
151153

152-
func expBackoff(c BackoffConfig) *backoff.ExponentialBackOff {
154+
func expBackoff(c connection.BackoffConfig) *backoff.ExponentialBackOff {
153155
b := backoff.NewExponentialBackOff()
154156
b.InitialInterval = c.Interval
155157
b.Multiplier = c.Multiplier

cmd/tunneld/options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type options struct {
4848
tlsKey string
4949
rootCA string
5050
clients string
51+
keepAlive string
5152
logLevel int
5253
version bool
5354
}
@@ -62,6 +63,7 @@ func parseArgs() *options {
6263
tlsKey := flag.String("tlsKey", "server.key", "Path to a TLS key file")
6364
rootCA := flag.String("rootCA", "", "Path to the trusted certificate chian used for client certificate authentication, if empty any client certificate is accepted")
6465
clients := flag.String("clients", "", "Comma-separated list of tunnel client ids, if empty accept all clients")
66+
keepAlive := flag.String("keepAlive", "5s", "TCP keep alive configuration")
6567
logLevel := flag.Int("log-level", 1, "Level of messages to log, 0-3")
6668
version := flag.Bool("version", false, "Prints tunneld version")
6769
flag.Parse()
@@ -76,6 +78,7 @@ func parseArgs() *options {
7678
tlsKey: *tlsKey,
7779
rootCA: *rootCA,
7880
clients: *clients,
81+
keepAlive: *keepAlive,
7982
logLevel: *logLevel,
8083
version: *version,
8184
}

cmd/tunneld/tunneld.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import (
1616

1717
"golang.org/x/net/http2"
1818

19-
"github.com/hons82/go-http-tunnel"
19+
tunnel "github.com/hons82/go-http-tunnel"
20+
"github.com/hons82/go-http-tunnel/connection"
2021
"github.com/hons82/go-http-tunnel/id"
2122
"github.com/hons82/go-http-tunnel/log"
2223
)
@@ -40,13 +41,19 @@ func main() {
4041

4142
autoSubscribe := opts.clients == ""
4243

44+
keepAlive, err := connection.Parse(opts.keepAlive)
45+
if err != nil {
46+
fatal("failed to parse KeepaliveConfig: %s", err)
47+
}
48+
4349
// setup server
4450
server, err := tunnel.NewServer(&tunnel.ServerConfig{
4551
Addr: opts.tunnelAddr,
4652
SNIAddr: opts.sniAddr,
4753
AutoSubscribe: autoSubscribe,
4854
TLSConfig: tlsconf,
4955
Logger: logger,
56+
KeepAlive: *keepAlive,
5057
})
5158
if err != nil {
5259
fatal("failed to create server: %s", err)

connection/backoff.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (C) 2017 Michał Matczuk
2+
// Use of this source code is governed by an AGPL-style
3+
// license that can be found in the LICENSE file.
4+
5+
package connection
6+
7+
import "time"
8+
9+
// Backoff defines behavior of staggering reconnection retries.
10+
type Backoff interface {
11+
// Next returns the duration to sleep before retrying to reconnect.
12+
// If the returned value is negative, the retry is aborted.
13+
NextBackOff() time.Duration
14+
15+
// Reset is used to signal a reconnection was successful and next
16+
// call to Next should return desired time duration for 1st reconnection
17+
// attempt.
18+
Reset()
19+
}
20+
21+
func NewDefaultBackoffConfig() *BackoffConfig {
22+
return &BackoffConfig{
23+
Interval: DefaultBackoffInterval,
24+
Multiplier: DefaultBackoffMultiplier,
25+
MaxInterval: DefaultBackoffMaxInterval,
26+
MaxTime: DefaultBackoffMaxTime,
27+
}
28+
}
29+
30+
// Default backoff configuration.
31+
const (
32+
DefaultBackoffInterval = 500 * time.Millisecond
33+
DefaultBackoffMultiplier = 1.5
34+
DefaultBackoffMaxInterval = 60 * time.Second
35+
DefaultBackoffMaxTime = 15 * time.Minute
36+
)
37+
38+
// BackoffConfig defines behavior of staggering reconnection retries.
39+
type BackoffConfig struct {
40+
Interval time.Duration `yaml:"interval"`
41+
Multiplier float64 `yaml:"multiplier"`
42+
MaxInterval time.Duration `yaml:"max_interval"`
43+
MaxTime time.Duration `yaml:"max_time"`
44+
}

0 commit comments

Comments
 (0)