Search code examples
javahttpnetwork-programmingmultipartform-dataapache-httpclient-5.x

Apache HttpComponents Async Client Setting Request Body Issue


I'm learning examples of latest apache hc async client. The response streaming feature is very useful and exactly what I need(such as calculating latency when each chunk arrived, which seems lack in CloseableHttpClient).

But having trouble setting the request body.

Take the AsyncClientHttpExchangeStreaming as example, I can run this example, but when trying to add some body to request, like following, I find it not work(httpbin response is very clear, and data or json field is empty).

      String bodyString = "{\"k\":\"v\"}";
      final SimpleHttpRequest request = SimpleRequestBuilder
        .post()
        .setHttpHost(target)
        .setPath("/post")
        .setBody(bodyString, ContentType.APPLICATION_JSON)
        .build();

Then I noticed client.execute(new BasicRequestProducer(request, null), and tried to replace null with new BasicAsyncEntityProducer(bodyString, ContentType.APPLICATION_JSON);, this time httpbin tells me it received the bodyString(is this walkaround correct and why need?).

When I tried posting multi part form, like above walkaround, the server reports errors such as 'content-length missing' or 'malformed request':

    final StringBody inputBody = new StringBody(inputJson, ContentType.APPLICATION_JSON);
    final StringBody parametersBody = new StringBody(parametersJson, ContentType.APPLICATION_JSON);

    final HttpEntity reqEntity = MultipartEntityBuilder.create()
        .addPart("Input", inputBody)
        .addPart("Parameters", parametersBody)
        .build();
    //above reqEntity can successful request my server in sync client
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    reqEntity.writeTo(baos);
    byte[] bytes = baos.toByteArray();
    BasicAsyncEntityProducer multiPartProducer = new BasicAsyncEntityProducer(
        bytes,
        ContentType.MULTIPART_FORM_DATA
    );

I searched a lot and someone says the HttpEntity created by MultipartEntityBuilder cannot be used along with async client. I'm very disappointed and frustrated. Could anyone suggest the correct way in apache hc, or rather other libraries instead?


Solution

  • Found solution and answer myself, hope helpful for some newcomers like me. Still welcome more elgant solution.

    • Cause of the issue

      HttpEntity created by MultipartEntityBuilder is wrapped into BasicAsyncEntityProducer as bytes, library sends the bytes as they are, and http post header missing boundary information.

    • Solution

      Get the Content-Type header including the boundery info then update it into request headers.

    //get the `Content-Type` header including the boundery info
    //value is like:
    //Content-Type: multipart/form-data; charset=ISO-8859-1; boundary=fLC0K1nKdSI-Q9vzFpU2WleMTXyWbOBp6
    String contentType = reqEntity.getContentType();
    final SimpleHttpRequest request = SimpleRequestBuilder
    //...omit other methods
    .setHeader("Content-Type", contentType)