Skip to content

Commit 9cbfcc5

Browse files
committed
rework code to scan for header name
1 parent 3bb2985 commit 9cbfcc5

File tree

1 file changed

+40
-27
lines changed

1 file changed

+40
-27
lines changed

http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -165,37 +165,50 @@ private[engine] final class HttpHeaderParser private (
165165
}
166166

167167
private def parseRawHeader(input: ByteString, lineStart: Int, cursor: Int, nodeIx: Int): Int = {
168-
val colonIx = scanHeaderNameAndReturnIndexOfColon(input, lineStart, lineStart + 1 + maxHeaderNameLength)(cursor)
169-
val headerName = asciiString(input, lineStart, colonIx)
170-
try {
171-
val valueParser = new RawHeaderValueParser(headerName, maxHeaderValueLength,
172-
headerValueCacheLimit(headerName), log, illegalResponseHeaderValueProcessingMode)
173-
insert(input, valueParser)(cursor, colonIx + 1, nodeIx, colonIx)
174-
parseHeaderLine(input, lineStart)(cursor, nodeIx)
175-
} catch {
176-
case OutOfTrieSpaceException => // if we cannot insert we drop back to simply creating new header instances
177-
val (headerValue, endIx) = scanHeaderValue(this, input, colonIx + 1, colonIx + maxHeaderValueLength + 3,
178-
log, settings.illegalResponseHeaderValueProcessingMode)()
179-
resultHeader = RawHeader(headerName, headerValue.trim)
180-
endIx
168+
val colonIx = input.indexOf(':', cursor, lineStart + 1 + maxHeaderNameLength)
169+
if (colonIx == -1) {
170+
fail(s"HTTP header name exceeds the configured limit of $maxHeaderNameLength characters",
171+
StatusCodes.RequestHeaderFieldsTooLarge)
172+
} else {
173+
val headerName = scanAsciiString(input, lineStart, colonIx)
174+
try {
175+
val valueParser = new RawHeaderValueParser(headerName, maxHeaderValueLength,
176+
headerValueCacheLimit(headerName), log, illegalResponseHeaderValueProcessingMode)
177+
insert(input, valueParser)(cursor, colonIx + 1, nodeIx, colonIx)
178+
parseHeaderLine(input, lineStart)(cursor, nodeIx)
179+
} catch {
180+
case OutOfTrieSpaceException => // if we cannot insert we drop back to simply creating new header instances
181+
val (headerValue, endIx) = scanHeaderValue(this, input, colonIx + 1, colonIx + maxHeaderValueLength + 3,
182+
log, settings.illegalResponseHeaderValueProcessingMode)()
183+
resultHeader = RawHeader(headerName, headerValue.trim)
184+
endIx
185+
}
181186
}
182187
}
183188

184-
@tailrec private def scanHeaderNameAndReturnIndexOfColon(input: ByteString, start: Int, limit: Int)(ix: Int): Int =
185-
if (ix < limit)
186-
(byteChar(input, ix), settings.illegalResponseHeaderNameProcessingMode) match {
187-
case (':', _) => ix
188-
case (c, _) if tchar(c) => scanHeaderNameAndReturnIndexOfColon(input, start, limit)(ix + 1)
189-
case (c, IllegalResponseHeaderNameProcessingMode.Error) =>
190-
fail(s"Illegal character '${escape(c)}' in header name")
191-
case (c, IllegalResponseHeaderNameProcessingMode.Warn) =>
192-
log.warning(s"Header key contains illegal character '${escape(c)}'")
193-
scanHeaderNameAndReturnIndexOfColon(input, start, limit)(ix + 1)
194-
case (c, IllegalResponseHeaderNameProcessingMode.Ignore) =>
195-
scanHeaderNameAndReturnIndexOfColon(input, start, limit)(ix + 1)
189+
// similar to asciiString function but it checks for illegal characters
190+
private def scanAsciiString(input: ByteString, start: Int, end: Int): String = {
191+
@tailrec def build(ix: Int = start, sb: JStringBuilder = new JStringBuilder(end - start)): String =
192+
if (ix == end) {
193+
sb.toString
194+
} else {
195+
val c = input(ix).toChar
196+
if (tchar(c)) {
197+
build(ix + 1, sb.append(c))
198+
} else {
199+
settings.illegalResponseHeaderNameProcessingMode match {
200+
case IllegalResponseHeaderNameProcessingMode.Error =>
201+
fail(s"Illegal character '${escape(c)}' in header name")
202+
case IllegalResponseHeaderNameProcessingMode.Warn =>
203+
log.warning(s"Header key contains illegal character '${escape(c)}'")
204+
build(ix + 1, sb.append(c))
205+
case IllegalResponseHeaderNameProcessingMode.Ignore =>
206+
build(ix + 1, sb.append(c))
207+
}
208+
}
196209
}
197-
else fail(s"HTTP header name exceeds the configured limit of ${limit - start - 1} characters",
198-
StatusCodes.RequestHeaderFieldsTooLarge)
210+
if (start == end) "" else build()
211+
}
199212

200213
@tailrec
201214
private def parseHeaderValue(input: ByteString, valueStart: Int, branch: ValueBranch)(cursor: Int = valueStart,

0 commit comments

Comments
 (0)