@@ -27,7 +27,6 @@ const (
2727 keyFollowPaths = "followpaths"
2828 keyDirName = "dir-name"
2929 keyExporterMetaPrefix = "exporter-md-"
30- keyOptsEncoded = "opts-encoded"
3130)
3231
3332type fsSyncProvider struct {
@@ -86,17 +85,7 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retEr
8685 }
8786
8887 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 )
10089
10190 dirName := ""
10291 name , ok := opts [keyDirName ]
@@ -224,9 +213,6 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error {
224213
225214 var stream grpc.ClientStream
226215
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-
230216 opts = encodeOpts (opts )
231217
232218 ctx = metadata .NewOutgoingContext (ctx , opts )
@@ -361,11 +347,11 @@ func (e InvalidSessionError) Unwrap() error {
361347func encodeOpts (opts map [string ][]string ) map [string ][]string {
362348 md := make (map [string ][]string , len (opts ))
363349 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 )
368351 md [k ] = out
352+ if encoded {
353+ md [k + "-encoded" ] = []string {"1" }
354+ }
369355 }
370356 return md
371357}
@@ -374,8 +360,18 @@ func decodeOpts(opts map[string][]string) map[string][]string {
374360 md := make (map [string ][]string , len (opts ))
375361 for k , v := range opts {
376362 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 )
379375 }
380376 md [k ] = out
381377 }
@@ -384,18 +380,23 @@ func decodeOpts(opts map[string][]string) map[string][]string {
384380
385381// encodeStringForHeader encodes a string value so it can be used in grpc header. This encoding
386382// 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+ }
398393 }
399394 }
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
401402}
0 commit comments