Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for multipart body from an input stream #34

Merged
merged 2 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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#getInputStream()} ()}
* @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));
}
}
Loading