Skip to content

Commit

Permalink
Merge pull request #1 from CandisIO/unquoted-param-issue
Browse files Browse the repository at this point in the history
Unquoted param issue
  • Loading branch information
Pritoj authored Mar 25, 2021
2 parents 6fabded + f9892ce commit 429d4f1
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 2 deletions.
85 changes: 84 additions & 1 deletion header.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ func fixUnquotedSpecials(s string) string {

clean := strings.Builder{}
clean.WriteString(s[:idx+1])
s = s[idx+1:]
s = fixUnquotedValueWithSpaces(s[idx+1:], ';')

for len(s) > 0 {
var consumed string
Expand Down Expand Up @@ -615,3 +615,86 @@ func fixUnescapedQuotes(hvalue string) string {
func whiteSpaceRune(r rune) bool {
return r == ' ' || r == '\t' || r == '\r' || r == '\n'
}

// gets a string like: x-unix-mode=0644; name=File name with spaces.pdf; some-param=da da da
// returns a string like: x-unix-mode=0644; name="File name with spaces.pdf"
// A Bit of explanation on terminology
// attr is the key
// value is the value
// param refers to the combination of "attr=value" separated by a separator
func fixUnquotedValueWithSpaces(s string, sep byte) string {
// The clean string that we will return
clean := strings.Builder{}
// This is either attr or value depending on where we are at in a
// Content-Type param list
const (
attrMode = iota
valueMode
)
mode := attrMode
attr := strings.Builder{}
value := strings.Builder{}
insideQuotes := false
spaceEncountered := false

resetForNextParam := func() {
attr.Reset()
value.Reset()
insideQuotes = false
spaceEncountered = false
mode = attrMode
}

writeCleanParam := func() {
clean.WriteString(attr.String())
if spaceEncountered {
clean.WriteByte('"')
}
clean.WriteString(value.String())
if spaceEncountered {
clean.WriteByte('"')
}
}

for len(s) > 0 {
// fmt.Printf("\ns -> %s\nmode -> %s\n attr-> %s\n value-> %s\n insideQuotes->%t\n spaceEncountered-> %t\n\n==========\n\n", s, mode, attr.String(), value.String(), insideQuotes, spaceEncountered)
switch mode {
case attrMode:
if s[0] == '=' {
mode = valueMode
}
attr.WriteByte(s[0])
s = s[1:]
case valueMode:
// If we encounter an end, reset the state
if len(s) == 1 || s[0] == '\n' || s[0] == '\t' || ((s[0] == '"' || s[0] == ';') && !insideQuotes) {
if len(s) == 1 && s[0] != ';' {
value.WriteString(s)
}
writeCleanParam()
if len(s) > 1 || s[0] == ';' {
clean.WriteByte(s[0])
}
s = s[1:]
resetForNextParam()
break
}

if s[0] == '"' {
insideQuotes = true
}

if s[0] == ' ' && !insideQuotes {
spaceEncountered = true
}

value.WriteByte(s[0])
s = s[1:]
}

}
if attr.Len() > 0 {
clean.WriteString(attr.String())
}
return clean.String()
}
58 changes: 57 additions & 1 deletion header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func TestFixUnquotedSpecials(t *testing.T) {
},
{
input: "application/octet-stream; param1= value1",
want: "application/octet-stream; param1= value1",
want: "application/octet-stream; param1=\" value1\"",
},
{
input: "application/octet-stream; param1=\tvalue1",
Expand Down Expand Up @@ -799,6 +799,12 @@ func TestParseMediaType(t *testing.T) {
mtype: "text/html",
params: map[string]string{"name": "index;a.html", "hash": "8675309"},
},
{
label: "unquoted with spaces",
input: "application/pdf; x-unix-mode=0644; name=File name with spaces.pdf",
mtype: "application/pdf",
params: map[string]string{"x-unix-mode": "0644", "name": "File name with spaces.pdf"},
},
}
for _, tc := range testCases {
t.Run(tc.label, func(t *testing.T) {
Expand Down Expand Up @@ -826,3 +832,53 @@ func TestParseMediaType(t *testing.T) {
})
}
}

func TestFixUnquotedValueWithSpaces(t *testing.T) {
testCases := []struct {
label string // Test case label.
input string // Content type to parse.
want string // Expected media type returned.
}{
{
label: "base case",
input: "x-unix-mode=0644; name=File name with spaces.pdf",
want: "x-unix-mode=0644; name=\"File name with spaces.pdf\"",
},
// We are making a conscious choice here to assume that if there is an unquoted string
// then the semicolon at the end is the param separator and not part of the value
{
label: "semi-colon at the end",
input: "x-unix-mode=0644; name=File name with spaces.pdf;",
want: "x-unix-mode=0644; name=\"File name with spaces.pdf\";",
},
{
label: "Non param ending",
input: "x-unix-mode=0644; name=File name with spaces.pdf; some-random string",
want: "x-unix-mode=0644; name=\"File name with spaces.pdf\"; some-random string",
},
{
label: "Other white space chars",
input: "x-unix-mode=0644;\n\tname=File name with spaces.pdf;\n\tanother=some param\n\t",
want: "x-unix-mode=0644;\n\tname=\"File name with spaces.pdf\";\n\tanother=\"some param\"\n\t",
}, {
label: "Don't touch already quoted values",
input: "x-unix-mode=0644;\n\tname=\"File name with spaces.pdf\";\n\tanother=some param\n\t",
want: "x-unix-mode=0644;\n\tname=\"File name with spaces.pdf\";\n\tanother=\"some param\"\n\t",
},

{
label: "Quoted separator mid string",
input: "x-unix-mode=0644;\n\tname=\"File name;with quoted separator.pdf\";\n\tanother=some param\n\t",
want: "x-unix-mode=0644;\n\tname=\"File name;with quoted separator.pdf\";\n\tanother=\"some param\"\n\t",
},
}
for _, tc := range testCases {
t.Run(tc.label, func(t *testing.T) {
got := fixUnquotedValueWithSpaces(tc.input, ';')
if got != tc.want {
t.Errorf("\nWanted:\t%q\nGot:\t%q", tc.want, got)
}
})
}

}

0 comments on commit 429d4f1

Please sign in to comment.