chore(natpmp): initialRetry -> initialConnectionDuration

This commit is contained in:
Quentin McGaw
2023-07-06 06:50:17 +00:00
parent f31a846cda
commit 075a1e2a80
6 changed files with 93 additions and 93 deletions

View File

@@ -18,7 +18,7 @@ func Test_Client_ExternalAddress(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
ctx context.Context ctx context.Context
gateway netip.Addr gateway netip.Addr
initialRetry time.Duration initialConnDuration time.Duration
exchanges []udpExchange exchanges []udpExchange
durationSinceStartOfEpoch time.Duration durationSinceStartOfEpoch time.Duration
externalIPv4Address netip.Addr externalIPv4Address netip.Addr
@@ -26,16 +26,16 @@ func Test_Client_ExternalAddress(t *testing.T) {
errMessage string errMessage string
}{ }{
"failure": { "failure": {
ctx: canceledCtx, ctx: canceledCtx,
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
initialRetry: time.Millisecond, initialConnDuration: time.Millisecond,
err: context.Canceled, err: context.Canceled,
errMessage: "executing remote procedure call: reading from udp connection: context canceled", errMessage: "executing remote procedure call: reading from udp connection: context canceled",
}, },
"success": { "success": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
initialRetry: time.Millisecond, initialConnDuration: time.Millisecond,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0, 0}, request: []byte{0, 0},
response: []byte{0x0, 0x80, 0x0, 0x0, 0x0, 0x13, 0xf2, 0x4f, 0x49, 0x8c, 0x36, 0x9a}, response: []byte{0x0, 0x80, 0x0, 0x0, 0x0, 0x13, 0xf2, 0x4f, 0x49, 0x8c, 0x36, 0x9a},
@@ -53,9 +53,9 @@ func Test_Client_ExternalAddress(t *testing.T) {
remoteAddress := launchUDPServer(t, testCase.exchanges) remoteAddress := launchUDPServer(t, testCase.exchanges)
client := Client{ client := Client{
serverPort: uint16(remoteAddress.Port), serverPort: uint16(remoteAddress.Port),
initialRetry: testCase.initialRetry, initialConnectionDuration: testCase.initialConnDuration,
maxRetries: 1, maxRetries: 1,
} }
durationSinceStartOfEpoch, externalIPv4Address, err := durationSinceStartOfEpoch, externalIPv4Address, err :=

View File

@@ -6,9 +6,9 @@ import (
// Client is a NAT-PMP protocol client. // Client is a NAT-PMP protocol client.
type Client struct { type Client struct {
serverPort uint16 serverPort uint16
initialRetry time.Duration initialConnectionDuration time.Duration
maxRetries uint maxRetries uint
} }
// New creates a new NAT-PMP client. // New creates a new NAT-PMP client.
@@ -16,11 +16,11 @@ func New() (client *Client) {
const natpmpPort = 5351 const natpmpPort = 5351
// Parameters described in https://www.ietf.org/rfc/rfc6886.html#section-3.1 // Parameters described in https://www.ietf.org/rfc/rfc6886.html#section-3.1
const initialRetry = 250 * time.Millisecond const initialConnectionDuration = 250 * time.Millisecond
const maxTries = 9 // 64 seconds const maxTries = 9 // 64 seconds
return &Client{ return &Client{
serverPort: natpmpPort, serverPort: natpmpPort,
initialRetry: initialRetry, initialConnectionDuration: initialConnectionDuration,
maxRetries: maxTries, maxRetries: maxTries,
} }
} }

View File

@@ -11,9 +11,9 @@ func Test_New(t *testing.T) {
t.Parallel() t.Parallel()
expectedClient := &Client{ expectedClient := &Client{
serverPort: 5351, serverPort: 5351,
initialRetry: 250 * time.Millisecond, initialConnectionDuration: 250 * time.Millisecond,
maxRetries: 9, maxRetries: 9,
} }
client := New() client := New()
assert.Equal(t, expectedClient, client) assert.Equal(t, expectedClient, client)

View File

@@ -19,7 +19,7 @@ func Test_Client_AddPortMapping(t *testing.T) {
internalPort uint16 internalPort uint16
requestedExternalPort uint16 requestedExternalPort uint16
lifetime time.Duration lifetime time.Duration
initialRetry time.Duration initialConnDuration time.Duration
exchanges []udpExchange exchanges []udpExchange
durationSinceStartOfEpoch time.Duration durationSinceStartOfEpoch time.Duration
assignedInternalPort uint16 assignedInternalPort uint16
@@ -46,7 +46,7 @@ func Test_Client_AddPortMapping(t *testing.T) {
internalPort: 123, internalPort: 123,
requestedExternalPort: 456, requestedExternalPort: 456,
lifetime: 1200 * time.Second, lifetime: 1200 * time.Second,
initialRetry: time.Millisecond, initialConnDuration: time.Millisecond,
exchanges: []udpExchange{{close: true}}, exchanges: []udpExchange{{close: true}},
err: ErrConnectionTimeout, err: ErrConnectionTimeout,
errMessage: "executing remote procedure call: connection timeout: after 1ms", errMessage: "executing remote procedure call: connection timeout: after 1ms",
@@ -58,7 +58,7 @@ func Test_Client_AddPortMapping(t *testing.T) {
internalPort: 123, internalPort: 123,
requestedExternalPort: 456, requestedExternalPort: 456,
lifetime: 1200 * time.Second, lifetime: 1200 * time.Second,
initialRetry: time.Second, initialConnDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x1, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x1, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
response: []byte{0x0, 0x81, 0x0, 0x0, 0x0, 0x13, 0xfe, 0xff, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, response: []byte{0x0, 0x81, 0x0, 0x0, 0x0, 0x13, 0xfe, 0xff, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
@@ -75,7 +75,7 @@ func Test_Client_AddPortMapping(t *testing.T) {
internalPort: 123, internalPort: 123,
requestedExternalPort: 456, requestedExternalPort: 456,
lifetime: 1200 * time.Second, lifetime: 1200 * time.Second,
initialRetry: time.Second, initialConnDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
response: []byte{0x0, 0x82, 0x0, 0x0, 0x0, 0x14, 0x3, 0x21, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, response: []byte{0x0, 0x82, 0x0, 0x0, 0x0, 0x14, 0x3, 0x21, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
@@ -86,11 +86,11 @@ func Test_Client_AddPortMapping(t *testing.T) {
assignedLifetime: 0x4b0 * time.Second, assignedLifetime: 0x4b0 * time.Second,
}, },
"remove_udp": { "remove_udp": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
protocol: "udp", protocol: "udp",
internalPort: 123, internalPort: 123,
initialRetry: time.Second, initialConnDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x1, 0x0, 0x0, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, request: []byte{0x0, 0x1, 0x0, 0x0, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
response: []byte{0x0, 0x81, 0x0, 0x0, 0x0, 0x14, 0x3, 0xd5, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, response: []byte{0x0, 0x81, 0x0, 0x0, 0x0, 0x14, 0x3, 0xd5, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
@@ -99,11 +99,11 @@ func Test_Client_AddPortMapping(t *testing.T) {
assignedInternalPort: 0x7b, assignedInternalPort: 0x7b,
}, },
"remove_tcp": { "remove_tcp": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
protocol: "tcp", protocol: "tcp",
internalPort: 123, internalPort: 123,
initialRetry: time.Second, initialConnDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
response: []byte{0x0, 0x82, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, response: []byte{0x0, 0x82, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
@@ -121,9 +121,9 @@ func Test_Client_AddPortMapping(t *testing.T) {
remoteAddress := launchUDPServer(t, testCase.exchanges) remoteAddress := launchUDPServer(t, testCase.exchanges)
client := Client{ client := Client{
serverPort: uint16(remoteAddress.Port), serverPort: uint16(remoteAddress.Port),
initialRetry: testCase.initialRetry, initialConnectionDuration: testCase.initialConnDuration,
maxRetries: 1, maxRetries: 1,
} }
durationSinceStartOfEpoch, assignedInternalPort, durationSinceStartOfEpoch, assignedInternalPort,

View File

@@ -61,15 +61,15 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr,
const maxResponseSize = 16 const maxResponseSize = 16
response = make([]byte, maxResponseSize) response = make([]byte, maxResponseSize)
// Retry duration doubles on every network error // Connection duration doubles on every network error
// Note it does not double if the source IP mismatches the gateway IP. // Note it does not double if the source IP mismatches the gateway IP.
retryDuration := c.initialRetry connectionDuration := c.initialConnectionDuration
var totalRetryDuration time.Duration var totalRetryDuration time.Duration
var retryCount uint var retryCount uint
for retryCount = 0; retryCount < c.maxRetries; retryCount++ { for retryCount = 0; retryCount < c.maxRetries; retryCount++ {
deadline := time.Now().Add(retryDuration) deadline := time.Now().Add(connectionDuration)
err = connection.SetDeadline(deadline) err = connection.SetDeadline(deadline)
if err != nil { if err != nil {
return nil, fmt.Errorf("setting connection deadline: %w", err) return nil, fmt.Errorf("setting connection deadline: %w", err)
@@ -87,8 +87,8 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr,
} }
var netErr net.Error var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() { if errors.As(err, &netErr) && netErr.Timeout() {
totalRetryDuration += retryDuration totalRetryDuration += connectionDuration
retryDuration *= 2 connectionDuration *= 2
continue continue
} }
return nil, fmt.Errorf("reading from udp connection: %w", err) return nil, fmt.Errorf("reading from udp connection: %w", err)

View File

@@ -13,15 +13,15 @@ func Test_Client_rpc(t *testing.T) {
t.Parallel() t.Parallel()
testCases := map[string]struct { testCases := map[string]struct {
ctx context.Context ctx context.Context
gateway netip.Addr gateway netip.Addr
request []byte request []byte
responseSize uint responseSize uint
initialRetry time.Duration initialConnectionDuration time.Duration
exchanges []udpExchange exchanges []udpExchange
expectedResponse []byte expectedResponse []byte
err error err error
errMessage string errMessage string
}{ }{
"gateway_ip_unspecified": { "gateway_ip_unspecified": {
gateway: netip.IPv6Unspecified(), gateway: netip.IPv6Unspecified(),
@@ -30,10 +30,10 @@ func Test_Client_rpc(t *testing.T) {
errMessage: "gateway IP is unspecified", errMessage: "gateway IP is unspecified",
}, },
"request_too_small": { "request_too_small": {
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0}, request: []byte{0},
initialRetry: time.Second, initialConnectionDuration: time.Second,
err: ErrRequestSizeTooSmall, err: ErrRequestSizeTooSmall,
errMessage: `checking request: message size is too small: ` + errMessage: `checking request: message size is too small: ` +
`need at least 2 bytes and got 1 byte\(s\)`, `need at least 2 bytes and got 1 byte\(s\)`,
}, },
@@ -46,10 +46,10 @@ func Test_Client_rpc(t *testing.T) {
`i/o timeout`, `i/o timeout`,
}, },
"call_error": { "call_error": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0, 1}, request: []byte{0, 1},
initialRetry: time.Millisecond, initialConnectionDuration: time.Millisecond,
exchanges: []udpExchange{ exchanges: []udpExchange{
{request: []byte{0, 1}, close: true}, {request: []byte{0, 1}, close: true},
}, },
@@ -57,10 +57,10 @@ func Test_Client_rpc(t *testing.T) {
errMessage: "connection timeout: after 1ms", errMessage: "connection timeout: after 1ms",
}, },
"response_too_small": { "response_too_small": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0, 0}, request: []byte{0, 0},
initialRetry: time.Second, initialConnectionDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0, 0}, request: []byte{0, 0},
response: []byte{1}, response: []byte{1},
@@ -70,11 +70,11 @@ func Test_Client_rpc(t *testing.T) {
`need at least 4 bytes and got 1 byte\(s\)`, `need at least 4 bytes and got 1 byte\(s\)`,
}, },
"unexpected_response_size": { "unexpected_response_size": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
responseSize: 5, responseSize: 5,
initialRetry: time.Second, initialConnectionDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
response: []byte{0, 1, 2, 3}, // size 4 response: []byte{0, 1, 2, 3}, // size 4
@@ -84,11 +84,11 @@ func Test_Client_rpc(t *testing.T) {
`expected 5 bytes and got 4 byte\(s\)`, `expected 5 bytes and got 4 byte\(s\)`,
}, },
"unknown_protocol_version": { "unknown_protocol_version": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
responseSize: 16, responseSize: 16,
initialRetry: time.Second, initialConnectionDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
response: []byte{0x1, 0x82, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, response: []byte{0x1, 0x82, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
@@ -97,11 +97,11 @@ func Test_Client_rpc(t *testing.T) {
errMessage: "checking response: protocol version is unknown: 1", errMessage: "checking response: protocol version is unknown: 1",
}, },
"unexpected_operation_code": { "unexpected_operation_code": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
responseSize: 16, responseSize: 16,
initialRetry: time.Second, initialConnectionDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
response: []byte{0x0, 0x88, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, response: []byte{0x0, 0x88, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
@@ -110,11 +110,11 @@ func Test_Client_rpc(t *testing.T) {
errMessage: "checking response: operation code is unexpected: expected 0x82 and got 0x88", errMessage: "checking response: operation code is unexpected: expected 0x82 and got 0x88",
}, },
"failure_result_code": { "failure_result_code": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
responseSize: 16, responseSize: 16,
initialRetry: time.Second, initialConnectionDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
response: []byte{0x0, 0x82, 0x0, 0x11, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, response: []byte{0x0, 0x82, 0x0, 0x11, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
@@ -123,11 +123,11 @@ func Test_Client_rpc(t *testing.T) {
errMessage: "checking response: result code: result code is unknown: 17", errMessage: "checking response: result code: result code is unknown: 17",
}, },
"success": { "success": {
ctx: context.Background(), ctx: context.Background(),
gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
responseSize: 16, responseSize: 16,
initialRetry: time.Second, initialConnectionDuration: time.Second,
exchanges: []udpExchange{{ exchanges: []udpExchange{{
request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0},
response: []byte{0x0, 0x82, 0x0, 0x0, 0x0, 0x0, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, response: []byte{0x0, 0x82, 0x0, 0x0, 0x0, 0x0, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
@@ -144,9 +144,9 @@ func Test_Client_rpc(t *testing.T) {
remoteAddress := launchUDPServer(t, testCase.exchanges) remoteAddress := launchUDPServer(t, testCase.exchanges)
client := Client{ client := Client{
serverPort: uint16(remoteAddress.Port), serverPort: uint16(remoteAddress.Port),
initialRetry: testCase.initialRetry, initialConnectionDuration: testCase.initialConnectionDuration,
maxRetries: 1, maxRetries: 1,
} }
response, err := client.rpc(testCase.ctx, testCase.gateway, response, err := client.rpc(testCase.ctx, testCase.gateway,