diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BodyPartParser.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BodyPartParser.scala index f03b0794a2..44673bf8e5 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BodyPartParser.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BodyPartParser.scala @@ -31,8 +31,8 @@ private[http] final class BodyPartParser(defaultContentType: ContentType, require(boundary.nonEmpty, "'boundary' parameter of multipart Content-Type must be non-empty") require(boundary.charAt(boundary.length - 1) != ' ', "'boundary' parameter of multipart Content-Type must not end with a space char") - require(boundaryCharNoSpace matchesAll boundary, - s"'boundary' parameter of multipart Content-Type contains illegal character '${boundaryCharNoSpace.firstMismatch(boundary).get}'") + require(boundaryChar matchesAll boundary, + s"'boundary' parameter of multipart Content-Type contains illegal character '${boundaryChar.firstMismatch(boundary).get}'") sealed trait StateResult // phantom type for ensuring soundness of our parsing method setup @@ -252,7 +252,7 @@ private[http] final class BodyPartParser(defaultContentType: ContentType, private[http] object BodyPartParser { // http://tools.ietf.org/html/rfc2046#section-5.1.1 - val boundaryCharNoSpace = CharPredicate.Digit ++ CharPredicate.Alpha ++ "'()+_,-./:=?" + val boundaryChar = CharPredicate.Digit ++ CharPredicate.Alpha ++ "'()+_,-./:=? " private object BoundaryHeader extends HttpHeader { def name = "" diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallersSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallersSpec.scala index 1b6d295601..e1953f2758 100644 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallersSpec.scala +++ b/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallersSpec.scala @@ -16,7 +16,7 @@ import akka.stream.scaladsl._ import akka.http.scaladsl.model._ import akka.http.scaladsl.util.FastFuture._ import akka.http.impl.util._ -import headers._ +import akka.http.scaladsl.model.headers._ import MediaTypes._ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAfterAll with ScalatestUtils { @@ -144,6 +144,12 @@ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAf Multipart.General.BodyPart.Strict(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "first part, implicitly typed")), Multipart.General.BodyPart.Strict(HttpEntity(`application/octet-stream`, "second part, explicitly typed"))) } + "a boundary with spaces" in { + Unmarshal(HttpEntity(`multipart/mixed` withBoundary "simple boundary", + """--simple boundary + |--simple boundary--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( + Multipart.General.BodyPart.Strict(HttpEntity.empty(ContentTypes.`text/plain(UTF-8)`))) + } } "multipartGeneralUnmarshaller should reject illegal multipart content with" - { @@ -198,6 +204,18 @@ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAf .to[Multipart.General] .flatMap(_ toStrict 1.second).failed, 1.second).getMessage shouldEqual "Illegal character ' ' in header name" } + "a boundary with a trailing space" in { + Await.result( + Unmarshal(HttpEntity(`multipart/mixed` withBoundary "simple boundary ", ByteString.empty)) + .to[Multipart.General].failed, 1.second).getMessage shouldEqual + "requirement failed: 'boundary' parameter of multipart Content-Type must not end with a space char" + } + "a boundary with an illegal character" in { + Await.result( + Unmarshal(HttpEntity(`multipart/mixed` withBoundary "simple&boundary", ByteString.empty)) + .to[Multipart.General].failed, 1.second).getMessage shouldEqual + "requirement failed: 'boundary' parameter of multipart Content-Type contains illegal character '&'" + } } "multipartByteRangesUnmarshaller should correctly unmarshal multipart/byteranges content with two different parts" in { @@ -262,6 +280,7 @@ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAf RawHeader("Content-Additional-2", "really-anything")))) // verifies order of headers is preserved } // TODO: reactivate after multipart/form-data unmarshalling integrity verification is implemented + // see https://github.com/akka/akka/issues/18908 // // "reject illegal multipart content" in { // val Left(MalformedContent(msg, _)) = HttpEntity(`multipart/form-data` withBoundary "XYZABC", "--noboundary--").as[MultipartFormData]