Skip to content

Commit 48b6240

Browse files
committed
filesync: fix backward compatibility with encoding + and %
Signed-off-by: Tonis Tiigi <[email protected]>
1 parent 5230f20 commit 48b6240

File tree

1 file changed

+34
-33
lines changed

1 file changed

+34
-33
lines changed

session/filesync/filesync.go

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const (
2727
keyFollowPaths = "followpaths"
2828
keyDirName = "dir-name"
2929
keyExporterMetaPrefix = "exporter-md-"
30-
keyOptsEncoded = "opts-encoded"
3130
)
3231

3332
type 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 {
361347
func 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

Comments
 (0)