@@ -27,7 +27,6 @@ const (
27
27
keyFollowPaths = "followpaths"
28
28
keyDirName = "dir-name"
29
29
keyExporterMetaPrefix = "exporter-md-"
30
- keyOptsEncoded = "opts-encoded"
31
30
)
32
31
33
32
type fsSyncProvider struct {
@@ -86,17 +85,7 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retEr
86
85
}
87
86
88
87
opts , _ := metadata .FromIncomingContext (stream .Context ()) // if no metadata continue with empty object
89
-
90
- isDecoded := false
91
- if v , ok := opts [keyOptsEncoded ]; ok && len (v ) > 0 {
92
- if b , _ := strconv .ParseBool (v [0 ]); b {
93
- isDecoded = true
94
- }
95
- }
96
-
97
- if isDecoded {
98
- opts = decodeOpts (opts )
99
- }
88
+ opts = decodeOpts (opts )
100
89
101
90
dirName := ""
102
91
name , ok := opts [keyDirName ]
@@ -224,9 +213,6 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error {
224
213
225
214
var stream grpc.ClientStream
226
215
227
- // mark that we have encoded options so older versions with raw values can be detected on client side
228
- opts [keyOptsEncoded ] = []string {"1" }
229
-
230
216
opts = encodeOpts (opts )
231
217
232
218
ctx = metadata .NewOutgoingContext (ctx , opts )
@@ -361,11 +347,11 @@ func (e InvalidSessionError) Unwrap() error {
361
347
func encodeOpts (opts map [string ][]string ) map [string ][]string {
362
348
md := make (map [string ][]string , len (opts ))
363
349
for k , v := range opts {
364
- out := make ([]string , len (v ))
365
- for i , s := range v {
366
- out [i ] = encodeStringForHeader (s )
367
- }
350
+ out , encoded := encodeStringForHeader (v )
368
351
md [k ] = out
352
+ if encoded {
353
+ md [k + "-encoded" ] = []string {"1" }
354
+ }
369
355
}
370
356
return md
371
357
}
@@ -374,8 +360,18 @@ func decodeOpts(opts map[string][]string) map[string][]string {
374
360
md := make (map [string ][]string , len (opts ))
375
361
for k , v := range opts {
376
362
out := make ([]string , len (v ))
377
- for i , s := range v {
378
- out [i ], _ = url .QueryUnescape (s )
363
+ var isDecoded bool
364
+ if v , ok := opts [k + "-encoded" ]; ok && len (v ) > 0 {
365
+ if b , _ := strconv .ParseBool (v [0 ]); b {
366
+ isDecoded = true
367
+ }
368
+ }
369
+ if isDecoded {
370
+ for i , s := range v {
371
+ out [i ], _ = url .QueryUnescape (s )
372
+ }
373
+ } else {
374
+ copy (out , v )
379
375
}
380
376
md [k ] = out
381
377
}
@@ -384,18 +380,23 @@ func decodeOpts(opts map[string][]string) map[string][]string {
384
380
385
381
// encodeStringForHeader encodes a string value so it can be used in grpc header. This encoding
386
382
// is backwards compatible and avoids encoding ASCII characters.
387
- func encodeStringForHeader (input string ) string {
388
- var output strings.Builder
389
- for _ , runeVal := range input {
390
- // Only encode non-ASCII characters, and characters that have special
391
- // meaning during decoding.
392
- if runeVal > unicode .MaxASCII || runeVal == '%' || runeVal == '+' {
393
- // Encode each non-ASCII character individually.
394
- output .WriteString (url .QueryEscape (string (runeVal )))
395
- } else {
396
- // Directly append ASCII characters and '*' to the output.
397
- output .WriteRune (runeVal )
383
+ func encodeStringForHeader (inputs []string ) ([]string , bool ) {
384
+ var encode bool
385
+ for _ , input := range inputs {
386
+ for _ , runeVal := range input {
387
+ // Only encode non-ASCII characters, and characters that have special
388
+ // meaning during decoding.
389
+ if runeVal > unicode .MaxASCII {
390
+ encode = true
391
+ break
392
+ }
398
393
}
399
394
}
400
- return output .String ()
395
+ if ! encode {
396
+ return inputs , false
397
+ }
398
+ for i , input := range inputs {
399
+ inputs [i ] = url .QueryEscape (input )
400
+ }
401
+ return inputs , true
401
402
}
0 commit comments