Skip to content

Commit 5352b3c

Browse files
committed
TUN-5801: Add custom wrapper for OriginConfig for JSON serde
1 parent 9552bb7 commit 5352b3c

File tree

5 files changed

+111
-42
lines changed

5 files changed

+111
-42
lines changed

config/configuration.go

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package config
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"io"
67
"net/url"
78
"os"
89
"path/filepath"
910
"runtime"
11+
"strconv"
1012
"time"
1113

1214
homedir "github.com/mitchellh/go-homedir"
@@ -49,7 +51,7 @@ func DefaultConfigDirectory() string {
4951
path := os.Getenv("CFDPATH")
5052
if path == "" {
5153
path = filepath.Join(os.Getenv("ProgramFiles(x86)"), "cloudflared")
52-
if _, err := os.Stat(path); os.IsNotExist(err) { //doesn't exist, so return an empty failure string
54+
if _, err := os.Stat(path); os.IsNotExist(err) { // doesn't exist, so return an empty failure string
5355
return ""
5456
}
5557
}
@@ -138,7 +140,7 @@ func FindOrCreateConfigPath() string {
138140
defer file.Close()
139141

140142
logDir := DefaultLogDirectory()
141-
_ = os.MkdirAll(logDir, os.ModePerm) //try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs
143+
_ = os.MkdirAll(logDir, os.ModePerm) // try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs
142144

143145
c := Root{
144146
LogDirectory: logDir,
@@ -190,17 +192,17 @@ type UnvalidatedIngressRule struct {
190192
// - To specify a time.Duration in json, use int64 of the nanoseconds
191193
type OriginRequestConfig struct {
192194
// HTTP proxy timeout for establishing a new connection
193-
ConnectTimeout *time.Duration `yaml:"connectTimeout" json:"connectTimeout"`
195+
ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout"`
194196
// HTTP proxy timeout for completing a TLS handshake
195-
TLSTimeout *time.Duration `yaml:"tlsTimeout" json:"tlsTimeout"`
197+
TLSTimeout *CustomDuration `yaml:"tlsTimeout" json:"tlsTimeout"`
196198
// HTTP proxy TCP keepalive duration
197-
TCPKeepAlive *time.Duration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"`
199+
TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"`
198200
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
199201
NoHappyEyeballs *bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs"`
200202
// HTTP proxy maximum keepalive connection pool size
201203
KeepAliveConnections *int `yaml:"keepAliveConnections" json:"keepAliveConnections"`
202204
// HTTP proxy timeout for closing an idle connection
203-
KeepAliveTimeout *time.Duration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"`
205+
KeepAliveTimeout *CustomDuration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"`
204206
// Sets the HTTP Host header for the local webserver.
205207
HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader"`
206208
// Hostname on the origin server certificate.
@@ -399,3 +401,34 @@ func ReadConfigFile(c *cli.Context, log *zerolog.Logger) (settings *configFileSe
399401

400402
return &configuration, warnings, nil
401403
}
404+
405+
// A CustomDuration is a Duration that has custom serialization for JSON.
406+
// JSON in Javascript assumes that int fields are 32 bits and Duration fields are deserialized assuming that numbers
407+
// are in nanoseconds, which in 32bit integers limits to just 2 seconds.
408+
// This type assumes that when serializing/deserializing from JSON, that the number is in seconds, while it maintains
409+
// the YAML serde assumptions.
410+
type CustomDuration struct {
411+
time.Duration
412+
}
413+
414+
func (s *CustomDuration) MarshalJSON() ([]byte, error) {
415+
return json.Marshal(s.Duration.Seconds())
416+
}
417+
418+
func (s *CustomDuration) UnmarshalJSON(data []byte) error {
419+
seconds, err := strconv.ParseInt(string(data), 10, 64)
420+
if err != nil {
421+
return err
422+
}
423+
424+
s.Duration = time.Duration(seconds * int64(time.Second))
425+
return nil
426+
}
427+
428+
func (s *CustomDuration) MarshalYAML() (interface{}, error) {
429+
return s.Duration.String(), nil
430+
}
431+
432+
func (s *CustomDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
433+
return unmarshal(&s.Duration)
434+
}

config/configuration_test.go

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
910
yaml "gopkg.in/yaml.v2"
1011
)
1112

@@ -110,14 +111,13 @@ counters:
110111

111112
}
112113

113-
func TestUnmarshalOriginRequestConfig(t *testing.T) {
114-
raw := []byte(`
114+
var rawConfig = []byte(`
115115
{
116-
"connectTimeout": 10000000000,
117-
"tlsTimeout": 30000000000,
118-
"tcpKeepAlive": 30000000000,
116+
"connectTimeout": 10,
117+
"tlsTimeout": 30,
118+
"tcpKeepAlive": 30,
119119
"noHappyEyeballs": true,
120-
"keepAliveTimeout": 60000000000,
120+
"keepAliveTimeout": 60,
121121
"keepAliveConnections": 10,
122122
"httpHostHeader": "app.tunnel.com",
123123
"originServerName": "app.tunnel.com",
@@ -142,13 +142,41 @@ func TestUnmarshalOriginRequestConfig(t *testing.T) {
142142
]
143143
}
144144
`)
145+
146+
func TestMarshalUnmarshalOriginRequest(t *testing.T) {
147+
testCases := []struct {
148+
name string
149+
marshalFunc func(in interface{}) (out []byte, err error)
150+
unMarshalFunc func(in []byte, out interface{}) (err error)
151+
baseUnit time.Duration
152+
}{
153+
{"json", json.Marshal, json.Unmarshal, time.Second},
154+
{"yaml", yaml.Marshal, yaml.Unmarshal, time.Nanosecond},
155+
}
156+
157+
for _, tc := range testCases {
158+
t.Run(tc.name, func(t *testing.T) {
159+
assertConfig(t, tc.marshalFunc, tc.unMarshalFunc, tc.baseUnit)
160+
})
161+
}
162+
}
163+
164+
func assertConfig(
165+
t *testing.T,
166+
marshalFunc func(in interface{}) (out []byte, err error),
167+
unMarshalFunc func(in []byte, out interface{}) (err error),
168+
baseUnit time.Duration,
169+
) {
145170
var config OriginRequestConfig
146-
assert.NoError(t, json.Unmarshal(raw, &config))
147-
assert.Equal(t, time.Second*10, *config.ConnectTimeout)
148-
assert.Equal(t, time.Second*30, *config.TLSTimeout)
149-
assert.Equal(t, time.Second*30, *config.TCPKeepAlive)
171+
var config2 OriginRequestConfig
172+
173+
assert.NoError(t, unMarshalFunc(rawConfig, &config))
174+
175+
assert.Equal(t, baseUnit*10, config.ConnectTimeout.Duration)
176+
assert.Equal(t, baseUnit*30, config.TLSTimeout.Duration)
177+
assert.Equal(t, baseUnit*30, config.TCPKeepAlive.Duration)
150178
assert.Equal(t, true, *config.NoHappyEyeballs)
151-
assert.Equal(t, time.Second*60, *config.KeepAliveTimeout)
179+
assert.Equal(t, baseUnit*60, config.KeepAliveTimeout.Duration)
152180
assert.Equal(t, 10, *config.KeepAliveConnections)
153181
assert.Equal(t, "app.tunnel.com", *config.HTTPHostHeader)
154182
assert.Equal(t, "app.tunnel.com", *config.OriginServerName)
@@ -176,4 +204,12 @@ func TestUnmarshalOriginRequestConfig(t *testing.T) {
176204
},
177205
}
178206
assert.Equal(t, ipRules, config.IPRules)
207+
208+
// validate that serializing and deserializing again matches the deserialization from raw string
209+
result, err := marshalFunc(config)
210+
require.NoError(t, err)
211+
err = unMarshalFunc(result, &config2)
212+
require.NoError(t, err)
213+
214+
require.Equal(t, config2, config)
179215
}

ingress/config.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,13 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
158158
ProxyAddress: defaultProxyAddress,
159159
}
160160
if c.ConnectTimeout != nil {
161-
out.ConnectTimeout = *c.ConnectTimeout
161+
out.ConnectTimeout = c.ConnectTimeout.Duration
162162
}
163163
if c.TLSTimeout != nil {
164-
out.TLSTimeout = *c.TLSTimeout
164+
out.TLSTimeout = c.TLSTimeout.Duration
165165
}
166166
if c.TCPKeepAlive != nil {
167-
out.TCPKeepAlive = *c.TCPKeepAlive
167+
out.TCPKeepAlive = c.TCPKeepAlive.Duration
168168
}
169169
if c.NoHappyEyeballs != nil {
170170
out.NoHappyEyeballs = *c.NoHappyEyeballs
@@ -173,7 +173,7 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
173173
out.KeepAliveConnections = *c.KeepAliveConnections
174174
}
175175
if c.KeepAliveTimeout != nil {
176-
out.KeepAliveTimeout = *c.KeepAliveTimeout
176+
out.KeepAliveTimeout = c.KeepAliveTimeout.Duration
177177
}
178178
if c.HTTPHostHeader != nil {
179179
out.HTTPHostHeader = *c.HTTPHostHeader
@@ -257,13 +257,13 @@ type OriginRequestConfig struct {
257257

258258
func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) {
259259
if val := overrides.ConnectTimeout; val != nil {
260-
defaults.ConnectTimeout = *val
260+
defaults.ConnectTimeout = val.Duration
261261
}
262262
}
263263

264264
func (defaults *OriginRequestConfig) setTLSTimeout(overrides config.OriginRequestConfig) {
265265
if val := overrides.TLSTimeout; val != nil {
266-
defaults.TLSTimeout = *val
266+
defaults.TLSTimeout = val.Duration
267267
}
268268
}
269269

@@ -281,13 +281,13 @@ func (defaults *OriginRequestConfig) setKeepAliveConnections(overrides config.Or
281281

282282
func (defaults *OriginRequestConfig) setKeepAliveTimeout(overrides config.OriginRequestConfig) {
283283
if val := overrides.KeepAliveTimeout; val != nil {
284-
defaults.KeepAliveTimeout = *val
284+
defaults.KeepAliveTimeout = val.Duration
285285
}
286286
}
287287

288288
func (defaults *OriginRequestConfig) setTCPKeepAlive(overrides config.OriginRequestConfig) {
289289
if val := overrides.TCPKeepAlive; val != nil {
290-
defaults.TCPKeepAlive = *val
290+
defaults.TCPKeepAlive = val.Duration
291291
}
292292
}
293293

ingress/config_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,12 @@ ingress:
191191
rawConfig := []byte(`
192192
{
193193
"originRequest": {
194-
"connectTimeout": 60000000000,
195-
"tlsTimeout": 1000000000,
194+
"connectTimeout": 60,
195+
"tlsTimeout": 1,
196196
"noHappyEyeballs": true,
197-
"tcpKeepAlive": 1000000000,
197+
"tcpKeepAlive": 1,
198198
"keepAliveConnections": 1,
199-
"keepAliveTimeout": 1000000000,
199+
"keepAliveTimeout": 1,
200200
"httpHostHeader": "abc",
201201
"originServerName": "a1",
202202
"caPool": "/tmp/path0",
@@ -228,12 +228,12 @@ ingress:
228228
"hostname": "*",
229229
"service": "https://localhost:8001",
230230
"originRequest": {
231-
"connectTimeout": 120000000000,
232-
"tlsTimeout": 2000000000,
231+
"connectTimeout": 120,
232+
"tlsTimeout": 2,
233233
"noHappyEyeballs": false,
234-
"tcpKeepAlive": 2000000000,
234+
"tcpKeepAlive": 2,
235235
"keepAliveConnections": 2,
236-
"keepAliveTimeout": 2000000000,
236+
"keepAliveTimeout": 2,
237237
"httpHostHeader": "def",
238238
"originServerName": "b2",
239239
"caPool": "/tmp/path1",
@@ -360,12 +360,12 @@ ingress:
360360
"hostname": "*",
361361
"service": "https://localhost:8001",
362362
"originRequest": {
363-
"connectTimeout": 120000000000,
364-
"tlsTimeout": 2000000000,
363+
"connectTimeout": 120,
364+
"tlsTimeout": 2,
365365
"noHappyEyeballs": false,
366-
"tcpKeepAlive": 2000000000,
366+
"tcpKeepAlive": 2,
367367
"keepAliveConnections": 2,
368-
"keepAliveTimeout": 2000000000,
368+
"keepAliveTimeout": 2,
369369
"httpHostHeader": "def",
370370
"originServerName": "b2",
371371
"caPool": "/tmp/path1",

orchestration/orchestrator_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func TestUpdateConfiguration(t *testing.T) {
5858
{
5959
"unknown_field": "not_deserialized",
6060
"originRequest": {
61-
"connectTimeout": 90000000000,
61+
"connectTimeout": 90,
6262
"noHappyEyeballs": true
6363
},
6464
"ingress": [
@@ -68,15 +68,15 @@ func TestUpdateConfiguration(t *testing.T) {
6868
"service": "http://192.16.19.1:443",
6969
"originRequest": {
7070
"noTLSVerify": true,
71-
"connectTimeout": 10000000000
71+
"connectTimeout": 10
7272
}
7373
},
7474
{
7575
"hostname": "jira.tunnel.org",
7676
"service": "http://172.32.20.6:80",
7777
"originRequest": {
7878
"noTLSVerify": true,
79-
"connectTimeout": 30000000000
79+
"connectTimeout": 30
8080
}
8181
},
8282
{
@@ -192,7 +192,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
192192
configJSONV1 = []byte(fmt.Sprintf(`
193193
{
194194
"originRequest": {
195-
"connectTimeout": 90000000000,
195+
"connectTimeout": 90,
196196
"noHappyEyeballs": true
197197
},
198198
"ingress": [
@@ -201,7 +201,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
201201
"service": "%s",
202202
"originRequest": {
203203
"httpHostHeader": "%s",
204-
"connectTimeout": 10000000000
204+
"connectTimeout": 10
205205
}
206206
},
207207
{

0 commit comments

Comments
 (0)