Skip to content

Commit

Permalink
Merge pull request #776 from pinguinson/multipart-fix
Browse files Browse the repository at this point in the history
HttpClientBackend: fixed binary subparts' headers of a multipart request
  • Loading branch information
adamw authored Nov 30, 2020
2 parents b263b84 + 0ff2be6 commit 7b754b5
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 16 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ val scalaTest = libraryDependencies ++= Seq("freespec", "funsuite", "flatspec",
val zioVersion = "1.0.3"
val zioInteropRsVersion = "1.3.0.7-2"

val sttpModelVersion = "1.2.0-RC5"
val sttpModelVersion = "1.2.0-RC6"
val sttpSharedVersion = "1.0.0-RC8"

val logback = "ch.qos.logback" % "logback-classic" % "1.2.3"
Expand Down
12 changes: 12 additions & 0 deletions core/src/test/scala/sttp/client3/testing/HttpTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,18 @@ trait HttpTest[F[_]]
req.send(backend).toFuture().map { resp => resp.body should be("p1=v1 (f1), p2=v2 (f2)") }
}

"send a multipart message with binary data and filename" in {
val binaryPart = {
multipart("p1", "v1".getBytes)
.fileName("f1")
}
val req = mp.multipartBody(binaryPart)
req.send(backend).toFuture().map { resp =>
resp.body should include("f1")
resp.body should include("v1")
}
}

if (supportsCustomMultipartContentType) {
"send a multipart message with custom content type" in {
val req = basicRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,30 @@ private[httpclient] trait BodyToHttpClient[F[_], S] {
private def multipartBody[T](parts: Seq[Part[RequestBody[_]]]) = {
val multipartBuilder = new MultiPartBodyPublisher()
parts.foreach { p =>
val allHeaders = p.headers :+ Header(HeaderNames.ContentDisposition, p.contentDispositionHeaderValue)
val allHeaders = Header(HeaderNames.ContentDisposition, p.contentDispositionHeaderValue) +: p.headers
val partHeaders = allHeaders.map(h => h.name -> h.value).toMap.asJava
def fileName = p.fileName.getOrElse("")
def contentType = p.contentType.getOrElse(MediaType.ApplicationOctetStream.toString())
p.body match {
case NoBody => // ignore
case FileBody(f, _) => multipartBuilder.addPart(p.name, f.toFile.toPath, partHeaders)
case StringBody(b, _, _) => multipartBuilder.addPart(p.name, b, partHeaders)
case ByteArrayBody(b, _) =>
multipartBuilder.addPart(p.name, supplier(new ByteArrayInputStream(b)), fileName, contentType)
multipartBuilder.addPart(p.name, supplier(new ByteArrayInputStream(b)), partHeaders)
case ByteBufferBody(b, _) =>
if ((b: Buffer).isReadOnly())
multipartBuilder.addPart(p.name, supplier(new ByteBufferBackedInputStream(b)), fileName, contentType)
else multipartBuilder.addPart(p.name, supplier(new ByteArrayInputStream(b.array())), fileName, contentType)
case InputStreamBody(b, _) => multipartBuilder.addPart(p.name, supplier(b), fileName, contentType)
multipartBuilder.addPart(p.name, supplier(new ByteBufferBackedInputStream(b)), partHeaders)
else
multipartBuilder.addPart(p.name, supplier(new ByteArrayInputStream(b.array())), partHeaders)
case InputStreamBody(b, _) => multipartBuilder.addPart(p.name, supplier(b), partHeaders)
case StreamBody(_) => throw new IllegalArgumentException("Streaming multipart bodies are not supported")
case MultipartBody(_) => throwNestedMultipartNotAllowed
}
}
multipartBuilder
}

private def supplier[T](t: => T) =
new Supplier[T] {
override def get(): T = t
private def supplier(t: => InputStream) =
new Supplier[InputStream] {
override def get(): InputStream = t
}

// https://stackoverflow.com/a/6603018/362531
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,12 @@ public MultiPartBodyPublisher addPart(String name, Path value, Map<String, Strin
return this;
}

public MultiPartBodyPublisher addPart(String name, Supplier<InputStream> value, String filename, String contentType) {
public MultiPartBodyPublisher addPart(String name, Supplier<InputStream> value, Map<String, String> headers) {
PartsSpecification newPart = new PartsSpecification();
newPart.type = PartsSpecification.TYPE.STREAM;
newPart.name = name;
newPart.stream = value;
newPart.filename = filename;
newPart.contentType = contentType;
newPart.headers = headers;
partsSpecificationList.add(newPart);
return this;
}
Expand All @@ -77,8 +76,6 @@ public enum TYPE {
String value;
Path path;
Supplier<InputStream> stream;
String filename;
String contentType;
Map<String, String> headers = new HashMap<>();
}

Expand Down

0 comments on commit 7b754b5

Please sign in to comment.