Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bufio.Scanner: token too long #161

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions fixtures/long.env

Large diffs are not rendered by default.

40 changes: 30 additions & 10 deletions godotenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,7 @@ func Read(filenames ...string) (envMap map[string]string, err error) {
// Parse reads an env file from io.Reader, returning a map of keys and values.
func Parse(r io.Reader) (envMap map[string]string, err error) {
envMap = make(map[string]string)

var lines []string
scanner := bufio.NewScanner(r)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}

if err = scanner.Err(); err != nil {
return
}
lines, err := ReadAllLines(r)

for _, fullLine := range lines {
if !isIgnoredLine(fullLine) {
Expand All @@ -124,6 +115,35 @@ func Parse(r io.Reader) (envMap map[string]string, err error) {
return
}

// ReadAllLines will convert the Reader into an array of strings
// one for each line in the original text.
func ReadAllLines(r io.Reader) (lines []string, err error) {
bf := bufio.NewReader(r)
linePart, isPrefix, err := bf.ReadLine()

for err == nil {
lineStr := string(linePart)

// handling of lines larger than the default buffer size
for err == nil && isPrefix == true {
linePart, isPrefix, err = bf.ReadLine()
if err != nil {
break
}
lineStr += string(linePart)
}

// `err == nil` is used to skip lines which caused an error
if isPrefix == false && err == nil {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition should supposedly allow skipping "invalid" lines.
But I am not sure if such a situation could even exist...
Perhaps I should just return immediately if/when an err is spotted?

lines = append(lines, lineStr)
}

linePart, isPrefix, err = bf.ReadLine()
}

return
}

//Unmarshal reads an env file from a string, returning a map of keys and values.
func Unmarshal(str string) (envMap map[string]string, err error) {
return Parse(strings.NewReader(str))
Expand Down
24 changes: 24 additions & 0 deletions godotenv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,30 @@ func TestReadPlainEnv(t *testing.T) {
}
}

func TestReadPlainEnvWithLongLine(t *testing.T) {
envFileName := "fixtures/long.env"
expectedBigValue := strings.Repeat("abcd", 20000)
expectedValues := map[string]string{
"BIG_VALUE": expectedBigValue,
"SMALL_VALUE": "abcd",
}

envMap, err := Read(envFileName)
if err != nil {
t.Error("Error reading file -> " + err.Error() )
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bug can be reproduced by reverting the change in godotenv.go and executing this new test.

}

if len(envMap) != len(expectedValues) {
t.Error("Didn't get the right size map back")
}

for key, value := range expectedValues {
if envMap[key] != value {
t.Error("Read got one of the keys wrong")
}
}
}

func TestParse(t *testing.T) {
envMap, err := Parse(bytes.NewReader([]byte("ONE=1\nTWO='2'\nTHREE = \"3\"")))
expectedValues := map[string]string{
Expand Down