Browse Source

Fix: json.Reader: fill output bytes as much as possible

Fix JSON parsing state machine so Read([]byte) can accept byte array of any length.

This fixes a infinite read with io.ReadFull().
Huang-Huang Bao 3 years ago
parent
commit
f0236233f3
2 changed files with 29 additions and 12 deletions
  1. 27 11
      infra/conf/json/reader.go
  2. 2 1
      infra/conf/json/reader_test.go

+ 27 - 11
infra/conf/json/reader.go

@@ -27,18 +27,29 @@ const (
 type Reader struct {
 	io.Reader
 
-	state State
-	br    *buf.BufferedReader
+	state   State
+	pending []byte
+	br      *buf.BufferedReader
 }
 
-// Read implements io.Reader.Read(). Buffer must be at least 3 bytes.
+// Read implements io.Reader.Read().
 func (v *Reader) Read(b []byte) (int, error) {
 	if v.br == nil {
 		v.br = &buf.BufferedReader{Reader: buf.NewReader(v.Reader)}
 	}
 
 	p := b[:0]
-	for len(p) < len(b)-2 {
+	for len(p) < len(b) {
+		if len(v.pending) > 0 {
+			max := len(b) - len(p)
+			if max > len(v.pending) {
+				max = len(v.pending)
+			}
+			p = append(p, v.pending[:max]...)
+			v.pending = v.pending[max:]
+			continue
+		}
+
 		x, err := v.br.ReadByte()
 		if err != nil {
 			if len(p) == 0 {
@@ -57,6 +68,7 @@ func (v *Reader) Read(b []byte) (int, error) {
 				p = append(p, x)
 			case '\\':
 				v.state = StateEscape
+				p = append(p, x)
 			case '#':
 				v.state = StateComment
 			case '/':
@@ -65,7 +77,7 @@ func (v *Reader) Read(b []byte) (int, error) {
 				p = append(p, x)
 			}
 		case StateEscape:
-			p = append(p, '\\', x)
+			p = append(p, x)
 			v.state = StateContent
 		case StateDoubleQuote:
 			switch x {
@@ -74,11 +86,12 @@ func (v *Reader) Read(b []byte) (int, error) {
 				p = append(p, x)
 			case '\\':
 				v.state = StateDoubleQuoteEscape
+				p = append(p, x)
 			default:
 				p = append(p, x)
 			}
 		case StateDoubleQuoteEscape:
-			p = append(p, '\\', x)
+			p = append(p, x)
 			v.state = StateDoubleQuote
 		case StateSingleQuote:
 			switch x {
@@ -87,16 +100,17 @@ func (v *Reader) Read(b []byte) (int, error) {
 				p = append(p, x)
 			case '\\':
 				v.state = StateSingleQuoteEscape
+				p = append(p, x)
 			default:
 				p = append(p, x)
 			}
 		case StateSingleQuoteEscape:
-			p = append(p, '\\', x)
+			p = append(p, x)
 			v.state = StateSingleQuote
 		case StateComment:
 			if x == '\n' {
 				v.state = StateContent
-				p = append(p, '\n')
+				p = append(p, x)
 			}
 		case StateSlash:
 			switch x {
@@ -105,14 +119,16 @@ func (v *Reader) Read(b []byte) (int, error) {
 			case '*':
 				v.state = StateMultilineComment
 			default:
-				p = append(p, '/', x)
+				v.state = StateContent
+				v.pending = append(v.pending, x)
+				p = append(p, '/')
 			}
 		case StateMultilineComment:
 			switch x {
 			case '*':
 				v.state = StateMultilineCommentStar
 			case '\n':
-				p = append(p, '\n')
+				p = append(p, x)
 			}
 		case StateMultilineCommentStar:
 			switch x {
@@ -121,7 +137,7 @@ func (v *Reader) Read(b []byte) (int, error) {
 			case '*':
 				// Stay
 			case '\n':
-				p = append(p, '\n')
+				p = append(p, x)
 			default:
 				v.state = StateMultilineComment
 			}

+ 2 - 1
infra/conf/json/reader_test.go

@@ -62,7 +62,7 @@ func TestReader1(t *testing.T) {
 		output string
 	}
 
-	bufLen := 8
+	bufLen := 1
 
 	data := []dataStruct{
 		{"loooooooooooooooooooooooooooooooooooooooog", "loooooooooooooooooooooooooooooooooooooooog"},
@@ -70,6 +70,7 @@ func TestReader1(t *testing.T) {
 		{`{"t": "\/test"}`, `{"t": "\/test"}`},
 		{`"\// fake comment"`, `"\// fake comment"`},
 		{`"\/\/\/\/\/"`, `"\/\/\/\/\/"`},
+		{`/test/test`, `/test/test`},
 	}
 
 	for _, testCase := range data {