Skip to content

Commit

Permalink
Added support for multipart body from an input stream
Browse files Browse the repository at this point in the history
  • Loading branch information
shaunstorey committed Nov 7, 2023
1 parent 769c431 commit b10f504
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.co.autotrader.traverson.http;

import java.io.InputStream;
import java.util.Arrays;

/**
Expand Down Expand Up @@ -41,6 +42,7 @@ public String getContentType() {
public static class BodyPart {
private final String name;
private final byte[] data;
private final InputStream inputStream;
private final String contentType;
private final String filename;

Expand All @@ -56,6 +58,22 @@ public BodyPart(String name, byte[] data, String contentType, String filename) {
this.data = Arrays.copyOf(data, data.length);
this.contentType = contentType;
this.filename = filename;
this.inputStream = null;
}

/**
* Constructs a BodyPart
* @param name see {@link BodyPart#getName()}
* @param inputStream see {@link BodyPart#getData()}
* @param contentType see {@link BodyPart#getContentType()}
* @param filename see {@link BodyPart#getFilename()}
*/
public BodyPart(String name, InputStream inputStream, String contentType, String filename) {
this.name = name;
this.inputStream = inputStream;
this.contentType = contentType;
this.filename = filename;
this.data = null;
}

/**
Expand All @@ -70,7 +88,18 @@ public String getName() {
* @return the raw data for this body part
*/
public byte[] getData() {
return Arrays.copyOf(data, data.length);
if (data == null) {
return null;
} else {
return Arrays.copyOf(data, data.length);
}
}

/**
* @return the input stream for this body part
*/
public InputStream getInputStream() {
return inputStream;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
import org.mockito.junit.jupiter.MockitoExtension;
import uk.co.autotrader.traverson.http.SimpleMultipartBody;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(MockitoExtension.class)
class BodyPartTest {

@Test
void init_GivenValues_SetsProperties() {
SimpleMultipartBody.BodyPart multipart = new SimpleMultipartBody.BodyPart("name", "data".getBytes(StandardCharsets.UTF_8), "contentType", "filename");
Expand All @@ -19,5 +22,17 @@ void init_GivenValues_SetsProperties() {
assertThat(multipart.getData()).isEqualTo("data".getBytes(StandardCharsets.UTF_8));
assertThat(multipart.getContentType()).isEqualTo("contentType");
assertThat(multipart.getFilename()).isEqualTo("filename");
assertThat(multipart.getInputStream()).isNull();
}

@Test
void init_GivenValuesWithInputStream_SetsProperties() throws IOException {
SimpleMultipartBody.BodyPart multipart = new SimpleMultipartBody.BodyPart("name", new ByteArrayInputStream("data".getBytes(StandardCharsets.UTF_8)), "contentType", "filename");

assertThat(multipart.getName()).isEqualTo("name");
assertThat(multipart.getData()).isNull();
assertThat(multipart.getContentType()).isEqualTo("contentType");
assertThat(multipart.getFilename()).isEqualTo("filename");
assertThat(multipart.getInputStream().readAllBytes()).isEqualTo("data".getBytes(StandardCharsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ class MultipartEntityConverter implements HttpEntityConverter {
public HttpEntity toEntity(Body body) {
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
for (SimpleMultipartBody.BodyPart bodyPart : ((SimpleMultipartBody) body).getContent()) {
multipartEntityBuilder.addBinaryBody(bodyPart.getName(), bodyPart.getData(), ContentType.create(bodyPart.getContentType()), bodyPart.getFilename());

if (bodyPart.getData() != null) {
multipartEntityBuilder.addBinaryBody(bodyPart.getName(), bodyPart.getData(), ContentType.create(bodyPart.getContentType()), bodyPart.getFilename());
} else {
multipartEntityBuilder.addBinaryBody(bodyPart.getName(), bodyPart.getInputStream(), ContentType.create(bodyPart.getContentType()), bodyPart.getFilename());
}
}
return multipartEntityBuilder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.junit.jupiter.api.Test;
import uk.co.autotrader.traverson.Traverson;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -106,6 +107,26 @@ void requestBody_MultipartBodyIsSerializedAndPostedCorrectly() {
assertThat(response.getStatusCode()).isEqualTo(202);
}

@Test
void requestBody_MultipartBodyFromInputStreamIsSerializedAndPostedCorrectly() {
byte[] data = new byte[]{0x00, 0x01, 0x02};
InputStream inputStream = new ByteArrayInputStream(data);
wireMockServer.stubFor(post("/records")
.withMultipartRequestBody(aMultipart()
.withName("my-body-part")
.withHeader("Content-Type", equalTo("application/octet-stream"))
.withBody(binaryEqualTo(data))
)
.willReturn(WireMock.status(202)));
SimpleMultipartBody.BodyPart bodyPart = new SimpleMultipartBody.BodyPart("my-body-part", inputStream, "application/octet-stream", "my-file");
SimpleMultipartBody multipartBody = new SimpleMultipartBody(bodyPart);
Response<JSONObject> response = traverson.from("http://localhost:8089/records")
.post(multipartBody);

wireMockServer.verify(1, postRequestedFor(urlEqualTo("/records")));
assertThat(response.getStatusCode()).isEqualTo(202);
}

@Test
void basicAuthentication_ReactsToUnauthorizedStatusAndAuthenticateHeader() {
wireMockServer.stubFor(get("/restricted-area")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import uk.co.autotrader.traverson.http.SimpleMultipartBody;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -38,4 +39,20 @@ void toEntity_GivenTextBody_ReturnsStringEntity() throws Exception {
.matches(Pattern.compile(".*Content-Type: contenttype.*", Pattern.DOTALL | Pattern.MULTILINE))
.matches(Pattern.compile(".*data.*", Pattern.DOTALL | Pattern.MULTILINE));
}

@Test
void toEntity_GivenTextBodyFromInputStream_ReturnsStringEntity() throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

HttpEntity entity = converter.toEntity(new SimpleMultipartBody(new SimpleMultipartBody.BodyPart("file", new ByteArrayInputStream("data".getBytes(StandardCharsets.UTF_8)), "contentType", "filename")));

assertThat(entity.getContentType()).matches(Pattern.compile("multipart/form-data; charset=ISO-8859-1; boundary=.*"));
entity.writeTo(outputStream);
String content = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
assertThat(content)
.matches(Pattern.compile(".*name=\"file\".*", Pattern.DOTALL | Pattern.MULTILINE))
.matches(Pattern.compile(".*filename=\"filename\".*", Pattern.DOTALL | Pattern.MULTILINE))
.matches(Pattern.compile(".*Content-Type: contenttype.*", Pattern.DOTALL | Pattern.MULTILINE))
.matches(Pattern.compile(".*data.*", Pattern.DOTALL | Pattern.MULTILINE));
}
}

0 comments on commit b10f504

Please sign in to comment.