Skip to content

Commit

Permalink
Fix CIO headers parsing
Browse files Browse the repository at this point in the history
    Closes #853
  • Loading branch information
e5l committed Jan 16, 2019
1 parent 2306a2d commit 4625b71
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 19 deletions.
Expand Up @@ -29,7 +29,10 @@ internal class CurlClientEngine(override val config: CurlClientEngineConfig) : H
val responseData = curlProcessor.executeRequest(curlRequest)

val response = with(responseData) {
val headers = parseHeaders(ByteReadChannel(headersBytes))
val headerBytes = ByteReadChannel(headersBytes).apply {
readUTF8Line()
}
val headers = parseHeaders(headerBytes)

val body = writer(coroutineContext) {
channel.writeFully(bodyBytes)
Expand Down
18 changes: 7 additions & 11 deletions ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/HttpParser.kt
Expand Up @@ -107,21 +107,17 @@ internal suspend fun parseHeaders(
if (range.start == range.end) break

val nameStart = range.start
val nameEnd = findColonOrSpace(builder, range)
val nameEnd = findLetterBeforeColon(builder, range) + 1

if (nameEnd <= 0) {
val header = builder.substring(nameStart, builder.length)
throw ParserException("No colon in HTTP header in $header in builder: \n$builder")
}

val nameHash = builder.hashCodeLowerCase(nameStart, nameEnd)
range.start = nameEnd

skipSpacesAndColon(builder, range)
if (range.start == range.end) {
throw ParserException(
"No HTTP header value provided for name ${builder.substring(
nameStart,
nameEnd
)}: \n$builder"
)
}

// TODO check for trailing spaces in HTTP spec

val valueStart = range.start
val valueEnd = range.end
Expand Down
Expand Up @@ -60,15 +60,17 @@ internal fun findSpaceOrEnd(text: CharSequence, range: MutableRange): Int {
return idx
}

internal fun findColonOrSpace(text: CharSequence, range: MutableRange): Int {
var idx = range.start
internal fun findLetterBeforeColon(text: CharSequence, range: MutableRange): Int {
var index = range.start
var lastCharIndex = index
val end = range.end

while (idx < end) {
val ch = text[idx]
if (ch == ' ' || ch == ':') return idx
idx++
while (index < end) {
val ch = text[index]
if (ch == ':') return lastCharIndex
if (ch != ' ') lastCharIndex = index
index++
}

return idx
return -1
}
Expand Up @@ -209,4 +209,46 @@ class HeadersTest {
request.release()
}
}

@Test
fun testEmptyHeaderValue() = runBlocking {
ch.writeStringUtf8("Host:\r\n\r\n")
val headers = parseHeaders(ch, builder)!!
assertEquals("", headers["Host"]?.toString())

headers.release()
}

@Test
fun testNoColon(): Unit = runBlocking<Unit> {
ch.writeStringUtf8("Host\r\n\r\n")

assertFails {
runBlocking {
parseHeaders(ch, builder)
}
}
}

@Test
fun testBlankHeaderValue() = runBlocking {
ch.writeStringUtf8("Host: \r\n\r\n")
val headers = parseHeaders(ch, builder)!!
assertEquals("", headers["Host"]?.toString())

headers.release()
}


@Test
fun testWrongHeader() = runBlocking<Unit> {
ch.writeStringUtf8("Hello world\r\n\r\n")

assertFails {
runBlocking {
parseHeaders(ch, builder)
}
}
}

}

0 comments on commit 4625b71

Please sign in to comment.