From efe2a4f1474a98c3aba35d11c18fe45e6fa1e429 Mon Sep 17 00:00:00 2001 From: "Danil Yarantsev (Yardanico)" Date: Fri, 17 Dec 2021 04:52:14 +0300 Subject: [PATCH 1/4] Fix #19261 --- lib/pure/httpclient.nim | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 33da6ef6c4f29..322b5cefe0a8d 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -805,6 +805,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, var parsedStatus = false var linei = 0 var fullyRead = false + var prevHeader = "" var line = "" result.headers = newHttpHeaders() while true: @@ -840,13 +841,23 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, else: # Parse headers var name = "" - var le = parseUntil(line, name, ':', linei) - if le <= 0: httpError("invalid headers") - inc(linei, le) - if line[linei] != ':': httpError("invalid headers") - inc(linei) # Skip : + var leadingSpaces = skipWhitespace(line, linei) + inc(linei, leadingSpaces) + if leadingSpaces == 0: + var le = parseUntil(line, name, ':', linei) + if le <= 0: httpError("invalid headers") + prevHeader = name + inc(linei, le) + if line[linei] != ':': httpError("invalid headers") + inc(linei) # Skip : + + result.headers.add(name, line[linei .. ^1].strip()) + # If there are spaces before the header name, it's actually a value + # that should be appended to the previous header, see bug #19261 + # Also, if there was no header before this, we just ignore the line + elif prevHeader != "": + result.headers.table.mgetOrPut(prevHeader, @[""])[^1].add line.strip() - result.headers.add(name, line[linei .. ^1].strip()) if result.headers.len > headerLimit: httpError("too many headers") From 80bdfce356393b9b904985936eeeb209ccde209b Mon Sep 17 00:00:00 2001 From: "Danil Yarantsev (Yardanico)" Date: Fri, 17 Dec 2021 04:54:52 +0300 Subject: [PATCH 2/4] No need for mgetOrPut --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 322b5cefe0a8d..a1d5638e3affb 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -856,7 +856,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, # that should be appended to the previous header, see bug #19261 # Also, if there was no header before this, we just ignore the line elif prevHeader != "": - result.headers.table.mgetOrPut(prevHeader, @[""])[^1].add line.strip() + result.headers.table[prevHeader][^1].add line.strip() if result.headers.len > headerLimit: httpError("too many headers") From 227cf4f60d93fee3896161380e6b9022bd75f1f7 Mon Sep 17 00:00:00 2001 From: "Danil Yarantsev (Yardanico)" Date: Fri, 17 Dec 2021 05:16:02 +0300 Subject: [PATCH 3/4] Follow the casing for headers, error out on index out of bounds --- lib/pure/httpclient.nim | 6 ++---- lib/pure/httpcore.nim | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index a1d5638e3affb..afb9bc03ea06c 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -842,21 +842,19 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, # Parse headers var name = "" var leadingSpaces = skipWhitespace(line, linei) - inc(linei, leadingSpaces) if leadingSpaces == 0: var le = parseUntil(line, name, ':', linei) - if le <= 0: httpError("invalid headers") + if le <= 0 or le >= line.len: httpError("invalid headers") prevHeader = name inc(linei, le) if line[linei] != ':': httpError("invalid headers") inc(linei) # Skip : - result.headers.add(name, line[linei .. ^1].strip()) # If there are spaces before the header name, it's actually a value # that should be appended to the previous header, see bug #19261 # Also, if there was no header before this, we just ignore the line elif prevHeader != "": - result.headers.table[prevHeader][^1].add line.strip() + result.headers.table[result.headers.toCaseInsensitive(prevHeader)][^1].add line.strip() if result.headers.len > headerLimit: httpError("too many headers") diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index fdd5926f72e8a..1f444c52daa0d 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -126,7 +126,7 @@ func toTitleCase(s: string): string = result[i] = if upper: toUpperAscii(s[i]) else: toLowerAscii(s[i]) upper = s[i] == '-' -func toCaseInsensitive(headers: HttpHeaders, s: string): string {.inline.} = +func toCaseInsensitive*(headers: HttpHeaders, s: string): string {.inline.} = return if headers.isTitleCase: toTitleCase(s) else: toLowerAscii(s) func newHttpHeaders*(titleCase=false): HttpHeaders = From f67ed12bb06e8ba55def2b671ded55ada21a83a2 Mon Sep 17 00:00:00 2001 From: "Danil Yarantsev (Yardanico)" Date: Sat, 25 Dec 2021 01:30:02 +0300 Subject: [PATCH 4/4] Ignore non-header lines like browsers do --- lib/pure/httpclient.nim | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index afb9bc03ea06c..eea77f47fb076 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -842,19 +842,22 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, # Parse headers var name = "" var leadingSpaces = skipWhitespace(line, linei) - if leadingSpaces == 0: - var le = parseUntil(line, name, ':', linei) - if le <= 0 or le >= line.len: httpError("invalid headers") - prevHeader = name - inc(linei, le) - if line[linei] != ':': httpError("invalid headers") - inc(linei) # Skip : - result.headers.add(name, line[linei .. ^1].strip()) - # If there are spaces before the header name, it's actually a value - # that should be appended to the previous header, see bug #19261 - # Also, if there was no header before this, we just ignore the line - elif prevHeader != "": - result.headers.table[result.headers.toCaseInsensitive(prevHeader)][^1].add line.strip() + var le = parseUntil(line, name, ':', linei) + # We only check lines that have `:` in them and don't have + # leading whitespace, so non-header lines are just ignored + if le < line.len: + if leadingSpaces == 0 and le < line.len: + if le <= 0: httpError("invalid headers") + prevHeader = name + inc(linei, le) + if line[linei] != ':': httpError("invalid headers") + inc(linei) # Skip : + result.headers.add(name, line[linei .. ^1].strip()) + # If there are spaces before the header name, it's actually a value + # that should be appended to the previous header, see bug #19261 + # Also, if there was no header before this, we just ignore the line + elif prevHeader != "": + result.headers.table[result.headers.toCaseInsensitive(prevHeader)][^1].add line.strip() if result.headers.len > headerLimit: httpError("too many headers")