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 81cb9bc
Show file tree
Hide file tree
Showing 3 changed files with 39 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
10 changes: 7 additions & 3 deletions tunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"
"time"

"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -189,6 +190,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 +212,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
Enabled: true,
},
OriginRequest: OriginRequestConfig{
ConnectTimeout: DurationPtr(10),
ConnectTimeout: &TunnelDuration{timeout},
},
}}

Expand All @@ -233,7 +235,7 @@ func TestUpdateTunnelConfiguration(t *testing.T) {
Enabled: true,
},
OriginRequest: OriginRequestConfig{
ConnectTimeout: DurationPtr(10 * time.Second),
ConnectTimeout: &TunnelDuration{10},
},
},
})
Expand All @@ -254,6 +256,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,12 +278,13 @@ func TestGetTunnelConfiguration(t *testing.T) {
Enabled: true,
},
OriginRequest: OriginRequestConfig{
ConnectTimeout: DurationPtr(10),
ConnectTimeout: &TunnelDuration{timeout},
},
}}

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

spew.Dump(actual)
if assert.NoError(t, err) {
assert.Equal(t, want, actual)
}
Expand Down

0 comments on commit 81cb9bc

Please sign in to comment.