Unit Testing for MultiPartEntityBuilder fails with org.apache.http.ContentTooLongException: Content length is unknown

443 views Asked by At

I have a class in my javax servlet application that serves as a proxy and creates multipart form data for POST requests. During regression tests (actual calls to the servlet) the application performs as expected, however, my unit test continues to fail. This is the line in my unit test that throws the exceptionString httpPostBody = IOUtils.toString(httpPost.getEntity().getContent()); It's specifically httpPost.getEntity().getContent().

Full Unit Test

@Test
public void testCreatePostRequest() throws IOException, ServletException, URISyntaxException, NullPointerException {
    URI newTargetUri = new URI(targetUriPost);
    cdsRequestBuilder = new CDSRequestBuilder();
    HttpPost httpPost = cdsRequestBuilder.createPostRequest(mockClientRequest, newTargetUri);

    // Parse the entity
    String httpPostBody = IOUtils.toString(httpPost.getEntity().getContent());

    // Perform evaluation
    Assert.assertNotNull(httpPost);
    Assert.assertEquals(httpPost.getHeaders(HttpHeaders.HOST)[0].toString(), ("Host: " + newTargetUri.getHost()));
    Assert.assertTrue(httpPost.getHeaders(HttpHeaders.CONTENT_LENGTH).length == 0);
    Assert.assertTrue(httpPostBody.contains("payload"));
    Assert.assertTrue(httpPostBody.contains("file"));
    Assert.assertTrue(httpPostBody.contains(testFileBinaryData));
}

Method that is tested calls this method

private HttpEntity convertInputStreamToMultiPart(Collection<Part> clientParts, String boundary) throws IOException {
    Part payload = null;
    Part file = null;

    for (Part part : clientParts) {
        if (part.getName().equalsIgnoreCase("payload")) {
            payload = part;
        } else if (part.getName().equalsIgnoreCase("file")) {
            file = part;
        }
    }

    if(payload == null) {
        logIt.error("Failed to extract payload - no payload found");
        throw new IllegalStateException("Payload is missing from client request.");
    }

    if(file == null || file.getSubmittedFileName().length() == 0) {
        logIt.error("Failed to extract file - no file found");
        throw new IllegalStateException("File is missing from client request.");
    }

    logIt.info("Payload is parsed from client: {}", IOUtils.toString(payload.getInputStream()));

    return MultipartEntityBuilder.create()
            .addTextBody("payload", IOUtils.toString(payload.getInputStream()))
            .addBinaryBody("file", file.getInputStream(), ContentType.parse("application/pdf"), file.getSubmittedFileName())
            .setContentType(ContentType.parse(MediaType.APPLICATION_FORM_URLENCODED))
            .setBoundary(boundary)
            .build();
}

This unit test passed fine previously when I was temporarily saving the file input stream to a File object and then using that to populate the binary body, but as a result of performance tests, I decided to simply pass in the file input stream itself. Note, that in my unit test, I mock the file input stream as such: Part mockFilePart = mock(Part.class);

when(mockFilePart.getName()).thenReturn("file");
    when(mockFilePart.getSubmittedFileName()).thenReturn(testFileName);
    when(mockFilePart.getInputStream()).thenReturn(IOUtils.toInputStream(testFileBinaryData));

Full Exception

org.apache.http.ContentTooLongException: Content length is unknown

at org.apache.http.entity.mime.MultipartFormEntity.getContent(MultipartFormEntity.java:101)
at gov.va.cds.test.CDSRequestBuilderTest.testCreatePostRequest(CDSRequestBuilderTest.java:117)
1

There are 1 answers

0
E_R On

FIXED! So since httpPost.getEntity().getContent() will always have an unknown content length before the request is finally sent, I looked at other ways to get the content and that was by using a ByteArrayOutputStream. I then do a couple conversions to get it back to a String to perform my assertions. The unit test now looks like this.

// Parse the entity - has to be done this way for the unit test b/c the content length isn't set until final request is made
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    httpPost.getEntity().writeTo(byteArrayOutputStream); // keeps us from calling getContent which will throw Content Length unknown
    String httpPostBody = IOUtils.toString(byteArrayOutputStream.toByteArray()); // now we can evaluate this
    byteArrayOutputStream.close();