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