@@ -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