diff --git a/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/HttpParser.kt b/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/HttpParser.kt index b814fa249c4..780612cd696 100644 --- a/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/HttpParser.kt +++ b/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/HttpParser.kt @@ -306,7 +306,16 @@ internal fun parseHeaderValue(text: CharArrayBuilder, range: MutableRange) { } private fun noColonFound(text: CharSequence, range: MutableRange): Nothing { - throw ParserException("No colon in HTTP header in ${text.substring(range.start, range.end)} in builder: \n$text") + val headerText = text.substring(range.start, range.end) + + // Check if this might be a multipart boundary line (starts with -- and might end with --) + if (headerText.startsWith("--") && (headerText.endsWith("--") || headerText.isEmpty())) { + // This is likely a multipart boundary line, not a header + // We'll throw a specific exception that can be caught and handled by the multipart parser + throw ParserException("Multipart boundary detected: $headerText") + } + + throw ParserException("No colon in HTTP header in $headerText in builder: \n$text") } private fun characterIsNotAllowed(text: CharSequence, ch: Char): Nothing = diff --git a/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/Multipart.kt b/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/Multipart.kt index e74c580a04b..d7370941129 100644 --- a/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/Multipart.kt +++ b/ktor-http/ktor-http-cio/common/src/io/ktor/http/cio/Multipart.kt @@ -111,8 +111,18 @@ private suspend fun parsePartHeadersImpl(input: ByteReadChannel): HttpHeadersMap val builder = CharArrayBuilder() try { - return parseHeaders(input, builder) - ?: throw EOFException("Failed to parse multipart headers: unexpected end of stream") + try { + return parseHeaders(input, builder) + ?: throw EOFException("Failed to parse multipart headers: unexpected end of stream") + } catch (e: ParserException) { + // Check if this is our special case for multipart boundary + if (e.message?.startsWith("Multipart boundary detected:") == true) { + // This is a boundary line, not a header + // Return empty headers to signal that this is not a valid part + return HttpHeadersMap(builder) + } + throw e + } } catch (t: Throwable) { builder.release() throw t