6
6
"fmt"
7
7
"net"
8
8
"net/netip"
9
+ "sort"
10
+ "strings"
9
11
"time"
10
12
)
11
13
@@ -65,9 +67,8 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr,
65
67
// Note it does not double if the source IP mismatches the gateway IP.
66
68
connectionDuration := c .initialConnectionDuration
67
69
68
- var totalRetryDuration time.Duration
69
-
70
70
var retryCount uint
71
+ var failedAttempts []string
71
72
for retryCount = 0 ; retryCount < c .maxRetries ; retryCount ++ {
72
73
deadline := time .Now ().Add (connectionDuration )
73
74
err = connection .SetDeadline (deadline )
@@ -87,8 +88,8 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr,
87
88
}
88
89
var netErr net.Error
89
90
if errors .As (err , & netErr ) && netErr .Timeout () {
90
- totalRetryDuration += connectionDuration
91
91
connectionDuration *= 2
92
+ failedAttempts = append (failedAttempts , netErr .Error ())
92
93
continue
93
94
}
94
95
return nil , fmt .Errorf ("reading from udp connection: %w" , err )
@@ -98,6 +99,9 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr,
98
99
// Upon receiving a response packet, the client MUST check the source IP
99
100
// address, and silently discard the packet if the address is not the
100
101
// address of the gateway to which the request was sent.
102
+ failedAttempts = append (failedAttempts ,
103
+ fmt .Sprintf ("received response from %s instead of gateway IP %s" ,
104
+ receivedRemoteAddress .IP , gatewayAddress .IP ))
101
105
continue
102
106
}
103
107
@@ -106,8 +110,8 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr,
106
110
}
107
111
108
112
if retryCount == c .maxRetries {
109
- return nil , fmt .Errorf ("%w: after %s" ,
110
- ErrConnectionTimeout , totalRetryDuration )
113
+ return nil , fmt .Errorf ("%w: failed attempts: %s" ,
114
+ ErrConnectionTimeout , dedupFailedAttempts ( failedAttempts ) )
111
115
}
112
116
113
117
// Opcodes between 0 and 127 are client requests. Opcodes from 128 to
@@ -121,3 +125,50 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr,
121
125
122
126
return response , nil
123
127
}
128
+
129
+ func dedupFailedAttempts (failedAttempts []string ) (errorMessage string ) {
130
+ type data struct {
131
+ message string
132
+ indices []int
133
+ }
134
+ messageToData := make (map [string ]data , len (failedAttempts ))
135
+ for i , message := range failedAttempts {
136
+ metadata , ok := messageToData [message ]
137
+ if ! ok {
138
+ metadata .message = message
139
+ }
140
+ metadata .indices = append (metadata .indices , i )
141
+ sort .Slice (metadata .indices , func (i , j int ) bool {
142
+ return metadata .indices [i ] < metadata .indices [j ]
143
+ })
144
+ messageToData [message ] = metadata
145
+ }
146
+
147
+ // Sort by first index
148
+ dataSlice := make ([]data , 0 , len (messageToData ))
149
+ for _ , metadata := range messageToData {
150
+ dataSlice = append (dataSlice , metadata )
151
+ }
152
+ sort .Slice (dataSlice , func (i , j int ) bool {
153
+ return dataSlice [i ].indices [0 ] < dataSlice [j ].indices [0 ]
154
+ })
155
+
156
+ dedupedFailedAttempts := make ([]string , 0 , len (dataSlice ))
157
+ for _ , data := range dataSlice {
158
+ newMessage := fmt .Sprintf ("%s (%s)" , data .message ,
159
+ indicesToTryString (data .indices ))
160
+ dedupedFailedAttempts = append (dedupedFailedAttempts , newMessage )
161
+ }
162
+ return strings .Join (dedupedFailedAttempts , "; " )
163
+ }
164
+
165
+ func indicesToTryString (indices []int ) string {
166
+ if len (indices ) == 1 {
167
+ return fmt .Sprintf ("try %d" , indices [0 ]+ 1 )
168
+ }
169
+ tries := make ([]string , len (indices ))
170
+ for i , index := range indices {
171
+ tries [i ] = fmt .Sprintf ("%d" , index + 1 )
172
+ }
173
+ return fmt .Sprintf ("tries %s" , strings .Join (tries , ", " ))
174
+ }
0 commit comments