Skip to content

Commit

Permalink
tunnel: introduce custom Duration for time fields
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jacobbednarz committed Jun 8, 2023
1 parent bddcc22 commit 2aff559
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .changelog/1303.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:breaking-change
tunnel: swap `ConnectTimeout`, `TLSTimeout`, `TCPKeepAlive` and `KeepAliveTimeout` to `TunnelDuration` instead of `time.Duration`
```
33 changes: 29 additions & 4 deletions tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,34 @@ import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
)

// A TunnelDuration is a Duration that has custom serialization for JSON.
// JSON in Javascript assumes that int fields are 32 bits and Duration fields
// are deserialized assuming that numbers are in nanoseconds, which in 32bit
// integers limits to just 2 seconds. This type assumes that when
// serializing/deserializing from JSON, that the number is in seconds, while it
// maintains the YAML serde assumptions.
type TunnelDuration struct {
time.Duration
}

func (s TunnelDuration) MarshalJSON() ([]byte, error) {
return json.Marshal(s.Duration.Seconds())
}

func (s *TunnelDuration) UnmarshalJSON(data []byte) error {
seconds, err := strconv.ParseInt(string(data), 10, 64)
if err != nil {
return err
}

s.Duration = time.Duration(seconds * int64(time.Second))
return nil
}

// ErrMissingTunnelID is for when a required tunnel ID is missing from the
// parameters.
var ErrMissingTunnelID = errors.New("required missing tunnel ID")
Expand Down Expand Up @@ -118,17 +143,17 @@ type UnvalidatedIngressRule struct {
// config.
type OriginRequestConfig struct {
// HTTP proxy timeout for establishing a new connection
ConnectTimeout *time.Duration `json:"connectTimeout,omitempty"`
ConnectTimeout *TunnelDuration `json:"connectTimeout,omitempty"`
// HTTP proxy timeout for completing a TLS handshake
TLSTimeout *time.Duration `json:"tlsTimeout,omitempty"`
TLSTimeout *TunnelDuration `json:"tlsTimeout,omitempty"`
// HTTP proxy TCP keepalive duration
TCPKeepAlive *time.Duration `json:"tcpKeepAlive,omitempty"`
TCPKeepAlive *TunnelDuration `json:"tcpKeepAlive,omitempty"`
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
NoHappyEyeballs *bool `json:"noHappyEyeballs,omitempty"`
// HTTP proxy maximum keepalive connection pool size
KeepAliveConnections *int `json:"keepAliveConnections,omitempty"`
// HTTP proxy timeout for closing an idle connection
KeepAliveTimeout *time.Duration `json:"keepAliveTimeout,omitempty"`
KeepAliveTimeout *TunnelDuration `json:"keepAliveTimeout,omitempty"`
// Sets the HTTP Host header for the local webserver.
HTTPHostHeader *string `json:"httpHostHeader,omitempty"`
// Hostname on the origin server certificate.
Expand Down
8 changes: 5 additions & 3 deletions tunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
fmt.Fprint(w, loadFixture("tunnel", "configuration"))
}

timeout, _ := time.ParseDuration("10s")
mux.HandleFunc(fmt.Sprintf("/accounts/%s/cfd_tunnel/%s/configurations", testAccountID, testTunnelID), handler)
want := TunnelConfigurationResult{
TunnelID: testTunnelID,
Expand All @@ -210,7 +211,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
Enabled: true,
},
OriginRequest: OriginRequestConfig{
ConnectTimeout: DurationPtr(10),
ConnectTimeout: &TunnelDuration{timeout},
},
}}

Expand All @@ -233,7 +234,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
Enabled: true,
},
OriginRequest: OriginRequestConfig{
ConnectTimeout: DurationPtr(10 * time.Second),
ConnectTimeout: &TunnelDuration{10},
},
},
})
Expand All @@ -254,6 +255,7 @@ func TestGetTunnelConfiguration(t *testing.T) {
fmt.Fprint(w, loadFixture("tunnel", "configuration"))
}

timeout, _ := time.ParseDuration("10s")
mux.HandleFunc(fmt.Sprintf("/accounts/%s/cfd_tunnel/%s/configurations", testAccountID, testTunnelID), handler)
want := TunnelConfigurationResult{
TunnelID: testTunnelID,
Expand All @@ -275,7 +277,7 @@ func TestGetTunnelConfiguration(t *testing.T) {
Enabled: true,
},
OriginRequest: OriginRequestConfig{
ConnectTimeout: DurationPtr(10),
ConnectTimeout: &TunnelDuration{timeout},
},
}}

Expand Down

0 comments on commit 2aff559

Please sign in to comment.