I am trying to upload a file by the POST
method with the Apache HttpClient library.
I used the example code for the preemptive basic authentification here:
package ahcs;
// many imports, press ctrl-o in eclipse
public class App {
static final String url = "http://127.0.0.1:64738/test/";
static final String content = "test\nfile\ndata";
static final String httpUser = "testuser";
static final String httpPasswd = "testPassword";
static final String fileUploadFieldName = "uploadData";
static final String fileName = "upload.dat";
public static void main(String[] args) {
System.err.println("Uploading to URL " + url);
CloseableHttpClient httpclient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(url);
httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
MultipartEntityBuilder mpEntityBuilder =
MultipartEntityBuilder.create();
mpEntityBuilder.setMode(HttpMultipartMode.RFC6532);
mpEntityBuilder.addBinaryBody(fileUploadFieldName,
content.getBytes(), ContentType.DEFAULT_BINARY, fileName);
httpPost.setEntity(mpEntityBuilder.build());
System.err.println("executing request " + httpPost.getRequestLine());
HttpEntity resEntity = null;
try {
// Really simple HTTP Authentification, grat Apache
HttpHost httpHost = URIUtils.extractHost(new URI(url));
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(httpUser, httpPasswd));
AuthCache authCache = new BasicAuthCache();
authCache.put(httpHost, new BasicScheme());
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
HttpResponse response = httpclient.execute(httpPost);
resEntity = response.getEntity();
System.err.println(response.getStatusLine().toString());
if (resEntity != null) {
System.err.println(EntityUtils.toString(resEntity));
}
int status = response.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new HttpResponseException(status,
"Upload error! (" + status + ")");
}
EntityUtils.consume(resEntity);
httpclient.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Unfortunately, it doesn't do what I want. The request what the apache httpclient gives, is this (I got this by listening from the command line with an nc -p 64738 -l
command):
POST /test/ HTTP/1.1
Content-Length: 249
Content-Type: multipart/form-data; boundary=PIrvSJ07MLxTV2rC4d-5ZfoL3CvJFJdJqO4i
Host: 127.0.0.1:64738
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.4 (Java/1.8.0_151)
Accept-Encoding: gzip,deflate
--PIrvSJ07MLxTV2rC4d-5ZfoL3CvJFJdJqO4i
Content-Disposition: form-data; name="uploadData"; filename="upload.dat"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
test
file
data
--PIrvSJ07MLxTV2rC4d-5ZfoL3CvJFJdJqO4i--
As we can see, everything is okay, except that the authentification header is simply missing.
Why is it so? What is the bug?
According to RFC7617 you need only one header "Authorization" with values "Basic " + login:passord in Base64 encoding to successefuly pass Basic authorization.
Your code is correct, except one thing - when you call httpPost.execute you are not pass execution context, and AuthCache and CredentialsProvider wasn't used at all.
package ahcs;
// many imports, press ctrl-o in eclipse
public class App {
static final String url = "http://127.0.0.1:64738/test/";
static final String content = "test\nfile\ndata";
static final String httpUser = "testuser";
static final String httpPasswd = "testPassword";
static final String fileUploadFieldName = "uploadData";
static final String fileName = "upload.dat";
public static void main(String[] args) {
System.err.println("Uploading to URL " + url);
CloseableHttpClient httpclient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(url);
httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
MultipartEntityBuilder mpEntityBuilder =
MultipartEntityBuilder.create();
mpEntityBuilder.setMode(HttpMultipartMode.RFC6532);
mpEntityBuilder.addBinaryBody(fileUploadFieldName,
content.getBytes(), ContentType.DEFAULT_BINARY, fileName);
httpPost.setEntity(mpEntityBuilder.build());
System.err.println("executing request " + httpPost.getRequestLine());
HttpEntity resEntity = null;
try {
// Really simple HTTP Authentification, grat Apache
HttpHost httpHost = URIUtils.extractHost(new URI(url));
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(httpUser, httpPasswd));
AuthCache authCache = new BasicAuthCache();
authCache.put(httpHost, new BasicScheme());
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
// context was missed
HttpResponse response = httpclient.execute(httpPost, context);
resEntity = response.getEntity();
System.err.println(response.getStatusLine().toString());
if (resEntity != null) {
System.err.println(EntityUtils.toString(resEntity));
}
int status = response.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new HttpResponseException(status,
"Upload error! (" + status + ")");
}
EntityUtils.consume(resEntity);
httpclient.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
But for Basic Auth using this API may be a bit verbose, it was designed to support many different authorization schemes.
If you know what charset server will use to decode Authorization header (suppose it UTF-8), you can write one-liner:
httpPost.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString((httpUser + ':' + httpPasswd).getBytes("UTF-8")));