Skip to content

Commit 4308342

Browse files
authored
Change parseTimeout to not handle non-second durations (grpc#1706)
1 parent be07790 commit 4308342

File tree

3 files changed

+113
-23
lines changed

3 files changed

+113
-23
lines changed

service_config.go

+44-17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ package grpc
2020

2121
import (
2222
"encoding/json"
23+
"fmt"
24+
"strconv"
25+
"strings"
2326
"time"
2427

2528
"google.golang.org/grpc/grpclog"
@@ -70,12 +73,48 @@ type ServiceConfig struct {
7073
Methods map[string]MethodConfig
7174
}
7275

73-
func parseTimeout(t *string) (*time.Duration, error) {
74-
if t == nil {
76+
func parseDuration(s *string) (*time.Duration, error) {
77+
if s == nil {
7578
return nil, nil
7679
}
77-
d, err := time.ParseDuration(*t)
78-
return &d, err
80+
if !strings.HasSuffix(*s, "s") {
81+
return nil, fmt.Errorf("malformed duration %q", *s)
82+
}
83+
ss := strings.SplitN((*s)[:len(*s)-1], ".", 3)
84+
if len(ss) > 2 {
85+
return nil, fmt.Errorf("malformed duration %q", *s)
86+
}
87+
// hasDigits is set if either the whole or fractional part of the number is
88+
// present, since both are optional but one is required.
89+
hasDigits := false
90+
var d time.Duration
91+
if len(ss[0]) > 0 {
92+
i, err := strconv.ParseInt(ss[0], 10, 32)
93+
if err != nil {
94+
return nil, fmt.Errorf("malformed duration %q: %v", *s, err)
95+
}
96+
d = time.Duration(i) * time.Second
97+
hasDigits = true
98+
}
99+
if len(ss) == 2 && len(ss[1]) > 0 {
100+
if len(ss[1]) > 9 {
101+
return nil, fmt.Errorf("malformed duration %q", *s)
102+
}
103+
f, err := strconv.ParseInt(ss[1], 10, 64)
104+
if err != nil {
105+
return nil, fmt.Errorf("malformed duration %q: %v", *s, err)
106+
}
107+
for i := 9; i > len(ss[1]); i-- {
108+
f *= 10
109+
}
110+
d += time.Duration(f)
111+
hasDigits = true
112+
}
113+
if !hasDigits {
114+
return nil, fmt.Errorf("malformed duration %q", *s)
115+
}
116+
117+
return &d, nil
79118
}
80119

81120
type jsonName struct {
@@ -128,7 +167,7 @@ func parseServiceConfig(js string) (ServiceConfig, error) {
128167
if m.Name == nil {
129168
continue
130169
}
131-
d, err := parseTimeout(m.Timeout)
170+
d, err := parseDuration(m.Timeout)
132171
if err != nil {
133172
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
134173
return ServiceConfig{}, err
@@ -182,18 +221,6 @@ func getMaxSize(mcMax, doptMax *int, defaultVal int) *int {
182221
return doptMax
183222
}
184223

185-
func newBool(b bool) *bool {
186-
return &b
187-
}
188-
189224
func newInt(b int) *int {
190225
return &b
191226
}
192-
193-
func newDuration(b time.Duration) *time.Duration {
194-
return &b
195-
}
196-
197-
func newString(b string) *string {
198-
return &b
199-
}

service_config_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
package grpc
2020

2121
import (
22+
"fmt"
23+
"math"
2224
"reflect"
2325
"testing"
2426
"time"
@@ -321,3 +323,64 @@ func TestPraseMsgSize(t *testing.T) {
321323
}
322324
}
323325
}
326+
327+
func TestParseDuration(t *testing.T) {
328+
testCases := []struct {
329+
s *string
330+
want *time.Duration
331+
err bool
332+
}{
333+
{s: nil, want: nil},
334+
{s: newString("1s"), want: newDuration(time.Second)},
335+
{s: newString("-1s"), want: newDuration(-time.Second)},
336+
{s: newString("1.1s"), want: newDuration(1100 * time.Millisecond)},
337+
{s: newString("1.s"), want: newDuration(time.Second)},
338+
{s: newString("1.0s"), want: newDuration(time.Second)},
339+
{s: newString(".002s"), want: newDuration(2 * time.Millisecond)},
340+
{s: newString(".002000s"), want: newDuration(2 * time.Millisecond)},
341+
{s: newString("0.003s"), want: newDuration(3 * time.Millisecond)},
342+
{s: newString("0.000004s"), want: newDuration(4 * time.Microsecond)},
343+
{s: newString("5000.000000009s"), want: newDuration(5000*time.Second + 9*time.Nanosecond)},
344+
{s: newString("4999.999999999s"), want: newDuration(5000*time.Second - time.Nanosecond)},
345+
{s: newString("1"), err: true},
346+
{s: newString("s"), err: true},
347+
{s: newString(".s"), err: true},
348+
{s: newString("1 s"), err: true},
349+
{s: newString(" 1s"), err: true},
350+
{s: newString("1ms"), err: true},
351+
{s: newString("1.1.1s"), err: true},
352+
{s: newString("Xs"), err: true},
353+
{s: newString("as"), err: true},
354+
{s: newString(".0000000001s"), err: true},
355+
{s: newString(fmt.Sprint(math.MaxInt32) + "s"), want: newDuration(math.MaxInt32 * time.Second)},
356+
{s: newString(fmt.Sprint(int64(math.MaxInt32)+1) + "s"), err: true},
357+
}
358+
for _, tc := range testCases {
359+
got, err := parseDuration(tc.s)
360+
if tc.err != (err != nil) ||
361+
(got == nil) != (tc.want == nil) ||
362+
(got != nil && *got != *tc.want) {
363+
wantErr := "<nil>"
364+
if tc.err {
365+
wantErr = "<non-nil error>"
366+
}
367+
s := "<nil>"
368+
if tc.s != nil {
369+
s = `&"` + *tc.s + `"`
370+
}
371+
t.Errorf("parseDuration(%v) = %v, %v; want %v, %v", s, got, err, tc.want, wantErr)
372+
}
373+
}
374+
}
375+
376+
func newBool(b bool) *bool {
377+
return &b
378+
}
379+
380+
func newDuration(b time.Duration) *time.Duration {
381+
return &b
382+
}
383+
384+
func newString(b string) *string {
385+
return &b
386+
}

test/end2end_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -1304,7 +1304,7 @@ func TestGetMethodConfig(t *testing.T) {
13041304
}
13051305
],
13061306
"waitForReady": true,
1307-
"timeout": "1ms"
1307+
"timeout": ".001s"
13081308
},
13091309
{
13101310
"name": [
@@ -1343,7 +1343,7 @@ func TestGetMethodConfig(t *testing.T) {
13431343
}
13441344
],
13451345
"waitForReady": true,
1346-
"timeout": "1ms"
1346+
"timeout": ".001s"
13471347
},
13481348
{
13491349
"name": [
@@ -1393,7 +1393,7 @@ func TestServiceConfigWaitForReady(t *testing.T) {
13931393
}
13941394
],
13951395
"waitForReady": false,
1396-
"timeout": "1ms"
1396+
"timeout": ".001s"
13971397
}
13981398
]
13991399
}`)
@@ -1433,7 +1433,7 @@ func TestServiceConfigWaitForReady(t *testing.T) {
14331433
}
14341434
],
14351435
"waitForReady": true,
1436-
"timeout": "1ms"
1436+
"timeout": ".001s"
14371437
}
14381438
]
14391439
}`)
@@ -1478,7 +1478,7 @@ func TestServiceConfigTimeout(t *testing.T) {
14781478
}
14791479
],
14801480
"waitForReady": true,
1481-
"timeout": "1h"
1481+
"timeout": "3600s"
14821482
}
14831483
]
14841484
}`)
@@ -1523,7 +1523,7 @@ func TestServiceConfigTimeout(t *testing.T) {
15231523
}
15241524
],
15251525
"waitForReady": true,
1526-
"timeout": "1ns"
1526+
"timeout": ".000000001s"
15271527
}
15281528
]
15291529
}`)

0 commit comments

Comments
 (0)