Skip to content

Commit 81cb9bc

Browse files
committed
tunnel: introduce custom Duration for time fields
All Tunnel timeout values are based on seconds however, there isn't a great way to do this in a flexible way with Go due to `time.Duration` not having marshal/unmarshal support in Go 1[1]. Instead, we introduce a custom `TunnelDuration` value here and ensure it always converts durations into seconds without impacting other users of `time.Duration`. [1]: golang/go#10275
1 parent bddcc22 commit 81cb9bc

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

.changelog/1303.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:breaking-change
2+
tunnel: swap `ConnectTimeout`, `TLSTimeout`, `TCPKeepAlive` and `KeepAliveTimeout` to `TunnelDuration` instead of `time.Duration`
3+
```

tunnel.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,34 @@ import (
66
"errors"
77
"fmt"
88
"net/http"
9+
"strconv"
910
"time"
1011
)
1112

13+
// A TunnelDuration is a Duration that has custom serialization for JSON.
14+
// JSON in Javascript assumes that int fields are 32 bits and Duration fields
15+
// are deserialized assuming that numbers are in nanoseconds, which in 32bit
16+
// integers limits to just 2 seconds. This type assumes that when
17+
// serializing/deserializing from JSON, that the number is in seconds, while it
18+
// maintains the YAML serde assumptions.
19+
type TunnelDuration struct {
20+
time.Duration
21+
}
22+
23+
func (s TunnelDuration) MarshalJSON() ([]byte, error) {
24+
return json.Marshal(s.Duration.Seconds())
25+
}
26+
27+
func (s *TunnelDuration) UnmarshalJSON(data []byte) error {
28+
seconds, err := strconv.ParseInt(string(data), 10, 64)
29+
if err != nil {
30+
return err
31+
}
32+
33+
s.Duration = time.Duration(seconds * int64(time.Second))
34+
return nil
35+
}
36+
1237
// ErrMissingTunnelID is for when a required tunnel ID is missing from the
1338
// parameters.
1439
var ErrMissingTunnelID = errors.New("required missing tunnel ID")
@@ -118,17 +143,17 @@ type UnvalidatedIngressRule struct {
118143
// config.
119144
type OriginRequestConfig struct {
120145
// HTTP proxy timeout for establishing a new connection
121-
ConnectTimeout *time.Duration `json:"connectTimeout,omitempty"`
146+
ConnectTimeout *TunnelDuration `json:"connectTimeout,omitempty"`
122147
// HTTP proxy timeout for completing a TLS handshake
123-
TLSTimeout *time.Duration `json:"tlsTimeout,omitempty"`
148+
TLSTimeout *TunnelDuration `json:"tlsTimeout,omitempty"`
124149
// HTTP proxy TCP keepalive duration
125-
TCPKeepAlive *time.Duration `json:"tcpKeepAlive,omitempty"`
150+
TCPKeepAlive *TunnelDuration `json:"tcpKeepAlive,omitempty"`
126151
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
127152
NoHappyEyeballs *bool `json:"noHappyEyeballs,omitempty"`
128153
// HTTP proxy maximum keepalive connection pool size
129154
KeepAliveConnections *int `json:"keepAliveConnections,omitempty"`
130155
// HTTP proxy timeout for closing an idle connection
131-
KeepAliveTimeout *time.Duration `json:"keepAliveTimeout,omitempty"`
156+
KeepAliveTimeout *TunnelDuration `json:"keepAliveTimeout,omitempty"`
132157
// Sets the HTTP Host header for the local webserver.
133158
HTTPHostHeader *string `json:"httpHostHeader,omitempty"`
134159
// Hostname on the origin server certificate.

tunnel_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99
"time"
1010

11+
"github.com/davecgh/go-spew/spew"
1112
"github.com/stretchr/testify/assert"
1213
)
1314

@@ -189,6 +190,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
189190
fmt.Fprint(w, loadFixture("tunnel", "configuration"))
190191
}
191192

193+
timeout, _ := time.ParseDuration("10s")
192194
mux.HandleFunc(fmt.Sprintf("/accounts/%s/cfd_tunnel/%s/configurations", testAccountID, testTunnelID), handler)
193195
want := TunnelConfigurationResult{
194196
TunnelID: testTunnelID,
@@ -210,7 +212,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
210212
Enabled: true,
211213
},
212214
OriginRequest: OriginRequestConfig{
213-
ConnectTimeout: DurationPtr(10),
215+
ConnectTimeout: &TunnelDuration{timeout},
214216
},
215217
}}
216218

@@ -233,7 +235,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
233235
Enabled: true,
234236
},
235237
OriginRequest: OriginRequestConfig{
236-
ConnectTimeout: DurationPtr(10 * time.Second),
238+
ConnectTimeout: &TunnelDuration{10},
237239
},
238240
},
239241
})
@@ -254,6 +256,7 @@ func TestGetTunnelConfiguration(t *testing.T) {
254256
fmt.Fprint(w, loadFixture("tunnel", "configuration"))
255257
}
256258

259+
timeout, _ := time.ParseDuration("10s")
257260
mux.HandleFunc(fmt.Sprintf("/accounts/%s/cfd_tunnel/%s/configurations", testAccountID, testTunnelID), handler)
258261
want := TunnelConfigurationResult{
259262
TunnelID: testTunnelID,
@@ -275,12 +278,13 @@ func TestGetTunnelConfiguration(t *testing.T) {
275278
Enabled: true,
276279
},
277280
OriginRequest: OriginRequestConfig{
278-
ConnectTimeout: DurationPtr(10),
281+
ConnectTimeout: &TunnelDuration{timeout},
279282
},
280283
}}
281284

282285
actual, err := client.GetTunnelConfiguration(context.Background(), AccountIdentifier(testAccountID), testTunnelID)
283286

287+
spew.Dump(actual)
284288
if assert.NoError(t, err) {
285289
assert.Equal(t, want, actual)
286290
}

0 commit comments

Comments
 (0)