1

I'm using the most recent apache http:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient-osgi</artifactId>
    <version>4.5.6</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore-osgi</artifactId>
    <version>4.4.10</version>
</dependency>

I have the following operation:

public void store(InputStream input) throws IOException {
    HttpClientBuilder builder = HttpClientBuilder.create();
    if (StringUtils.isNotBlank(username)) {
      CredentialsProvider provider = new BasicCredentialsProvider();
      UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username.trim(), StringUtils.trimToEmpty(password));
      provider.setCredentials(AuthScope.ANY, credentials);
      builder.setDefaultCredentialsProvider(provider);
    }
    HttpClient client = builder.build();
    HttpPost post  = new HttpPost(uri);
    post.setEntity(new InputStreamEntity(input));
    HttpResponse response = client.execute(post);
}   

Until basic auth was active, everything was working fine, however, after adding basic auth I get the following error:

Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity. at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:226) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) ... 6 more

I've found a following bug report: https://github.com/http-builder-ng/http-builder-ng/issues/10, however it is assigned to another problem.

What is causing the error? How to use basic auth with apache httpclient? I have no idea what is 'repeatable HTTP request', from what I know all the client need to set is Authorization header. Is it possible that I've misconfigured something on the server so that it requires 'repeatable' HTTP request?

8
  • Just a wild guess: is your server responding with an authentication challenge and your client library tries to obey but somehow fails on that? edit: it would be helpful to have the HTTP communication going on. Any chance to activiate some verbose log? Commented Aug 27, 2018 at 8:15
  • @UniversE this is basic auth, there is no authentication challenge. Either you set Authorization header, or you get 401. Commented Aug 27, 2018 at 8:25
  • Most probably the InputStreamEntity is the problem because this might not be repeatable. I do not know, though, what to do in this case. So just a hint ... Commented Aug 27, 2018 at 8:28
  • OK I've checked the source code, there seems to be a big misdesign in apache library, they set authorization headers only after they receive 401 from the server, which is absurd because they know they have to use basic anyway... Commented Aug 27, 2018 at 8:59
  • @9ilsdx9rvj0lo HTTP 401 is the authentication challenge in the sense that it MUST contain a WWW-Authenticate header. See RFC-7235 if you want to learn more. Now when you initially send valid credentials, the server simply accepts. But if the server (for some reason) does not accept, it sends 401. Back to my wild guess: your client sends the requests, server does not accept and responds with 401, the client intends to retry the request and fails due to the above error. Edit: This would match your recent observations. Commented Aug 27, 2018 at 9:03

1 Answer 1

1

I do not agree with the OP's own solution, because it is somehow very hackish and circumvents the Credential Mechanism of the library.

There are several entity types for HTTP Entities, as documented here. So now that you know, you cannot use a repeatable entity in your scenario, how about using a self-contained one or a wrapper which uses a buffer.

You can achieve this with a one-liner. Without having tried it out, I think the correct solution is:

post.setEntity(new BufferedHttpEntity(new InputStreamEntity(input)));
Sign up to request clarification or add additional context in comments.

7 Comments

Have you checked BufferedHttpEntity? It looks like it would copy all input stream into memory, which would be absolutely inacceptable. The controversial line is: entity.writeTo(out) in constructor
That is the purpose of a buffer, yes, but how can that be inacceptable? Large file upload? Reading again from the source and requiring it to resend would be way more inacceptable, wouldn't it? Keep in mind, that in your solution you tricked the library to not send another request in one special case. What about other cases? E.g. if you get 407 because some proxy is in the way? But I agree completely that the library should provide another way to force it using some credentials right from the beginning.
Your code has crashed with OutOfMemoryError already in my JUnit test... The uploads can have hundreds of megabytes, it must be processed using streams...
@9ilsdx9rvj0lo okay.. now I have more understanding about your use case. What prevents you from using a MultipartEntityBuilder to create your HttpEntity?
The fact that it's not multipart... the protocol is until now very simple and supports sending everything from 1 byte up to gigabytes... It's simply a blob storage, used in intranet, where network connection is stable (otherwise I'd go in direction of torrent or sth similar).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.