Get The Most Out of HttpClient
Get The Most Out of HttpClient
2. Basic POST 2
9. Conclusion 11
3. Conclusion 15
Table of Contents
3: HttpClient Timeout
1. Overview 17
7. Hard Timeout 23
9. Conclusion 26
Table of Contents
5. Conclusion 32
2. The SSLPeerUnverifiedException 35
7. Conclusion 40
Table of Contents
5. Conclusion 47
5. Conclusion 55
1: Posting with HttpClient
6
1. Overview
In this chapter – we’ll POST with the HttpClient 4 – using first authorization,
then the fluent HttpClient API.
1
2. Basic POST
First, let’s go over a simple example and send a POST request using
HttpClient.
1. @Test
2. public void whenPostRequestUsingHttpClient_thenCorrect()
3. throws ClientProtocolException, IOException {
4. CloseableHttpClient client = HttpClients.createDefault();
5. HttpPost httpPost = new HttpPost(“http://www.example.com”);
6.
7. List<NameValuePair> params = new ArrayList<NameValuePair>();
8. params.add(new BasicNameValuePair(“username”, “John”));
9. params.add(new BasicNameValuePair(“password”, “pass”));
10. httpPost.setEntity(new UrlEncodedFormEntity(params));
11.
12. CloseableHttpResponse response = client.execute(httpPost);
13. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
14. client.close();
15. }
2
3. POST with Authorization
Next, let’s see how to do a POST with Authentication credentials using the
HttpClient.
1. @Test
2. public void whenPostRequestWithAuthorizationUsingHttpClient_thenCorrect()
3. throws ClientProtocolException, IOException, AuthenticationException {
4. CloseableHttpClient client = HttpClients.createDefault();
5. HttpPost httpPost = new HttpPost(“http://www.example.com”);
6.
7. httpPost.setEntity(new StringEntity(“test post”));
8. UsernamePasswordCredentials creds
9. = new UsernamePasswordCredentials(“John”, “pass”);
10. httpPost.addHeader(new BasicScheme().authenticate(creds, httpPost, null));
11.
12. CloseableHttpResponse response = client.execute(httpPost);
13. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
14. client.close();
15. }
3
4. POST with JSON
Now – let’s see how to send a POST request with a JSON body using the
HttpClient.
1. @Test
2. public void whenPostJsonUsingHttpClient_thenCorrect()
3. throws ClientProtocolException, IOException {
4. CloseableHttpClient client = HttpClients.createDefault();
5. HttpPost httpPost = new HttpPost(“http://www.example.com”);
6.
7. String json = “{“id”:1,”name”:”John”}”;
8. StringEntity entity = new StringEntity(json);
9. httpPost.setEntity(entity);
10. httpPost.setHeader(“Accept”, “application/json”);
11. httpPost.setHeader(“Content-type”, “application/json”);
12.
13. CloseableHttpResponse response = client.execute(httpPost);
14. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
15. client.close();
16. }
Note how we’re using the StringEntity to set the body of the request.
4
5. POST with the HttpClient Fluent API
1. @Test
2. public void whenPostFormUsingHttpClientFluentAPI_thenCorrect()
3. throws ClientProtocolException, IOException {
4. HttpResponse response = Request.Post(“http://www.example.com”).bodyForm(
5. Form.form().add(“username”, “John”).add(“password”, “pass”).build())
6. .execute().returnResponse();
7.
8. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
9. }
5
6. POST Multipart Request
1. @Test
2. public void whenSendMultipartRequestUsingHttpClient_thenCorrect()
3. throws ClientProtocolException, IOException {
4. CloseableHttpClient client = HttpClients.createDefault();
5. HttpPost httpPost = new HttpPost(“http://www.example.com”);
6.
7. MultipartEntityBuilder builder = MultipartEntityBuilder.create();
8. builder.addTextBody(“username”, “John”);
9. builder.addTextBody(“password”, “pass”);
10. builder.addBinaryBody(“file”, new File(“test.txt”), ContentType.
11. APPLICATION_OCTET_STREAM, “file.ext”);
12.
13. HttpEntity multipart = builder.build();
14. httpPost.setEntity(multipart);
15.
16. CloseableHttpResponse response = client.execute(httpPost);
17. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
18. client.close();
19. }
6
7. Upload a File using HttpClient
1. @Test
2. public void whenUploadFileUsingHttpClient_thenCorrect() throws
3. ClientProtocolException, IOException {
4. CloseableHttpClient client = HttpClients.createDefault();
5. HttpPost httpPost = new HttpPost(“http://www.example.com”);
6.
7. MultipartEntityBuilder builder = MultipartEntityBuilder.create();
8. builder.addBinaryBody(“file”, new File(“test.txt”), ContentType.
9. APPLICATION_OCTET_STREAM, “file.ext”);
10. HttpEntity multipart = builder.build();
11. httpPost.setEntity(multipart);
12.
13. CloseableHttpResponse response = client.execute(httpPost);
14. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
15. client.close();
16. }
7
8. Get File Upload Progress
Finally – let’s see how to get the progress of File upload using HttpClient.
1. @Test
2. public void whenGetUploadFileProgressUsingHttpClient_thenCorrect()
3. throws ClientProtocolException, IOException {
4. CloseableHttpClient client = HttpClients.createDefault();
5. HttpPost httpPost = new HttpPost(“http://www.example.com”);
6.
7. MultipartEntityBuilder builder = MultipartEntityBuilder.create();
8. builder.addBinaryBody(“file”, new File(“test.txt”), ContentType.
9. APPLICATION_OCTET_STREAM, “file.ext”);
10. HttpEntity multipart = builder.build();
11.
12. ProgressEntityWrapper.ProgressListener pListener =
13. percentage -> assertFalse(Float.compare(percentage, 100) > 0);
14. httpPost.setEntity(new ProgressEntityWrapper(multipart, pListener));
15.
16. CloseableHttpResponse response = client.execute(httpPost);
17. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
18. client.close();
19. }
8
And here’s our extended version of HttpEntityWrapper
“ProgressEntityWrapper“:
9
Note that:
10
9. Conclusion
In this chapter, we illustrated the most common ways to send POST HTTP
Requests with the Apache HttpClient 4.
The implementation of all these examples and code snippets can be found
in the github project.
11
2: HttpClient 4 – Get the Status Code
12
1. Overview
In this very quick chapter, we’ll see how to get and validate the Status
Code of the HTTP Response using HttpClient 4.
13
2. Retrieve the Status Code from the Http Response
1. response.getStatusLine().getStatusCode()
Using this, we can validate that the code we receive from the server is
indeed correct:
1. @Test
2. public void givenGetRequestExecuted_whenAnalyzingTheResponse_
3. thenCorrectStatusCode()
4. throws ClientProtocolException, IOException {
5. HttpClient client = HttpClientBuilder.create().build();
6. HttpResponse response = client.execute(new HttpGet(SAMPLE_URL));
7. int statusCode = response.getStatusLine().getStatusCode();
8. assertThat(statusCode, equalTo(HttpStatus.SC_OK));
9. }
Notice that we’re using the predefined Status Codes also available in the
library via org.apache.http.HttpStatus.
14
3. Conclusion
This very simple example shows how to retrieve and work with Status
Codes with the Apache HttpClient 4.
The implementation of all these examples and code snippets can be found
in my github project.
15
3: HttpClient Timeout
16
1. Overview
This chapter will show how to configure a timeout with the Apache
HttpClient 4.
17
2. Configure Timeouts via raw String Parameters
18
3. Configure Timeouts via the API
The more important of these parameters – namely the first two – can also
be set via a more type-safe API:
19
4. Configure Timeouts using the new 4.3. Builder
The fluent, builder API introduced in 4.3 provides the right way to set
timeouts at a high level:
1. int timeout = 5;
2. RequestConfig config = RequestConfig.custom()
3. .setConnectTimeout(timeout * 1000)
4. .setConnectionRequestTimeout(timeout * 1000)
5. .setSocketTimeout(timeout * 1000).build();
6. CloseableHttpClient client =
7. HttpClientBuilder.create().setDefaultRequestConfig(config).build();
20
5. Timeout Properties Explained
The first two parameters – the connection and socket timeouts – are the
most important. However, setting a timeout for obtaining a connection is
definitely important in high load scenarios, which is why the third parameter
shouldn’t be ignored.
21
6. Using the HttpClient
After configuring it, we can now use the client to perform HTTP requests:
With the previously defined client, the connection to the host will time
out in 5 seconds. Also, if the connection is established but no data is
received, the timeout will also be 5 additional seconds.
22
7. Hard Timeout
For example, the download of a potentially large file fits into this category.
In this case, the connection may be successfully established, data may be
consistently coming through, but we still need to ensure that the operation
doesn’t go over some specific time threshold.
23
8. Timeout and DNS Round Robin–Something to Be Aware Of
It’s quite common that some larger domains will be using a DNS round robin
configuration – essentially having the same domain mapped to multiple
IP addresses. This introduces a new challenge for a timeout against such
a domain, simply because of the way HttpClient will try to connect to that
domain that times out:
So, as you can see – the overall operation will not time out when we
expect it to. Instead – it will time out when all the possible routes have
timed out. What’s more – this will happen completely transparently for the
client (unless you have your log configured at the DEBUG level).
Here’s a simple example you can run and replicate this issue:
1. int timeout = 3;
2. RequestConfig config = RequestConfig.custom().
3. setConnectTimeout(timeout * 1000).
4. setConnectionRequestTimeout(timeout * 1000).
5. setSocketTimeout(timeout * 1000).build();
6. CloseableHttpClient client = HttpClientBuilder.create()
7. .setDefaultRequestConfig(config).build();
8.
9. HttpGet request = new HttpGet(“http://www.google.com:81”);
10. response = client.execute(request);
24
You will notice the retrying logic with a DEBUG log level:
25
9. Conclusion
26
4: Custom HTTP Header with the HttpClient
27
1. Overview
In this chapter, we’ll look at how to set a custom header with the HttpClient.
28
2. Set Header on Request – 4.3 and above
29
3. Set Header on Request – Before 4.3
In versions pre 4.3 of HttpClient, we can set any custom header on a request
with a simple setHeader call on the request:
30
4. Set Default Header on the Client
Instead of setting the Header on each and every request, we can also
configure it as a default header on the Client itself:
This is extremely helpful when the header needs to be the same for all
requests – such as a custom application header.
31
5. Conclusion
This chapter illustrated how to add an HTTP header to one or all requests
sent via the Apache HttpClient.
The implementation of all these examples and code snippets can be found
in the GitHub project.
32
5: HttpClient with SSL
33
1. Overview
This chapter will show how to configure the Apache HttpClient 4 with
“Accept All” SSL support. The goal is simple – consume HTTPS URLs which
do not have valid certificates.
34
2. The SSLPeerUnverifiedException
35
3. Configure SSL – Accept All (HttpClient < 4.3)
Let’s now configure the HTTP client to trust all certificate chains regardless
of their validity:
1. @Test
2. public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk()
3. throws GeneralSecurityException {
4. HttpComponentsClientHttpRequestFactory requestFactory = new
5. HttpComponentsClientHttpRequestFactory();
6. CloseableHttpClient httpClient = (CloseableHttpClient) requestFactory.
7. getHttpClient();
8.
9. TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
10. SSLSocketFactory sf = new SSLSocketFactory(acceptingTrustStrategy, ALLOW_
11. ALL_HOSTNAME_VERIFIER);
12. httpClient.getConnectionManager().getSchemeRegistry().register(new
13. Scheme(“https”, 8443, sf));
14.
15. ResponseEntity<String> response = new RestTemplate(requestFactory).
16. exchange(urlOverHttps, HttpMethod.GET, null, String.class);
17. assertThat(response.getStatusCode().value(), equalTo(200));
18. }
36
4. Configure SSL – Accept All (HttpClient 4.4 and above)
1. @Test
2. public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk()
3. throws GeneralSecurityException {
4. TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
5. SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null,
6. acceptingTrustStrategy).build();
7. SSLConnectionSocketFactory sslsf = new
8. SSLConnectionSocketFactory(sslContext,
9. NoopHostnameVerifier.INSTANCE);
10.
11. Registry<ConnectionSocketFactory> socketFactoryRegistry =
12. RegistryBuilder.<ConnectionSocketFactory> create()
13. .register(“https”, sslsf)
14. .register(“http”, new PlainConnectionSocketFactory())
15. .build();
16.
17. BasicHttpClientConnectionManager connectionManager =
18. new BasicHttpClientConnectionManager(socketFactoryRegistry);
19. CloseableHttpClient httpClient = HttpClients.custom().
20. setSSLSocketFactory(sslsf)
21. .setConnectionManager(connectionManager).build();
22.
23. HttpComponentsClientHttpRequestFactory requestFactory =
24. new HttpComponentsClientHttpRequestFactory(httpClient);
25. ResponseEntity<String> response = new RestTemplate(requestFactory)
26. .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
27. assertThat(response.getStatusCode().value(), equalTo(200));
28. }
37
5. The Spring RestTemplate with SSL (HttpClient < 4.3)
Now that we have seen how to configure a raw HttpClient with SSL support,
let’s take a look at a higher level client – the Spring RestTemplate.
With no SSL configured, the following test fails as expected:
1. @Test(expected = ResourceAccessException.class)
2. public void whenHttpsUrlIsConsumed_thenException() {
3. String urlOverHttps
4. = “https://localhost:8443/httpclient-simple/api/bars/1”;
5. ResponseEntity<String> response
6. = new RestTemplate().exchange(urlOverHttps, HttpMethod.GET, null,
7. String.class);
8. assertThat(response.getStatusCode().value(), equalTo(200));
9. }
1. @Test
2. public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_
3. thenException()
4. throws GeneralSecurityException {
5. HttpComponentsClientHttpRequestFactory requestFactory
6. = new HttpComponentsClientHttpRequestFactory();
7. DefaultHttpClient httpClient
8. = (DefaultHttpClient) requestFactory.getHttpClient();
9. TrustStrategy acceptingTrustStrategy = (cert, authType) -> true
10. SSLSocketFactory sf = new SSLSocketFactory(
11. acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER);
12. httpClient.getConnectionManager().getSchemeRegistry()
13. .register(new Scheme(“https”, 8443, sf));
14.
15. String urlOverHttps
16. = “https://localhost:8443/httpclient-simple/api/bars/1”;
17. ResponseEntity<String> response = new RestTemplate(requestFactory).
18. exchange(urlOverHttps, HttpMethod.GET, null, String.class);
19. assertThat(response.getStatusCode().value(), equalTo(200));
20. }
As we can see, this is very similar to the way we configured SSL for the
raw HttpClient – we configure the request factory with SSL support and
then we instantiate the template passing this preconfigured factory.
38
6. The Spring RestTemplate with SSL (HttpClient 4.4)
1. @Test
2. public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_
3. thenCorrect()
4. throws ClientProtocolException, IOException {
5. CloseableHttpClient httpClient
6. = HttpClients.custom()
7. .setSSLHostnameVerifier(new NoopHostnameVerifier())
8. .build();
9. HttpComponentsClientHttpRequestFactory requestFactory
6. = new HttpComponentsClientHttpRequestFactory();
7. requestFactory.setHttpClient(httpClient);
8.
9. ResponseEntity<String> response
6. = new RestTemplate(requestFactory).exchange(
7. urlOverHttps, HttpMethod.GET, null, String.class);
8. assertThat(response.getStatusCode().value(), equalTo(200));
9. }
39
7. Conclusion
40
6: HttpClient 4 – Send Custom Cookie
41
1. Overview
This chapter will focus on how to send a Custom Cookie using the Apache
HttpClient 4.
42
2. Configure Cookie Management on the HttpClient
In the newer HttpClient 4.3, we’ll leverage the fluent builder API responsible
with both constructing and configuring the client.
First, we’ll need to create a cookie store and set up our sample cookie in
the store:
Then, we can set up this cookie store on the HttpClient using the
setDefaultCookieStore() method and send the request:
1. @Test
2. public void whenSettingCookiesOnTheHttpClient_thenCookieSentCorrectly()
3. throws ClientProtocolException, IOException {
4. BasicCookieStore cookieStore = new BasicCookieStore();
5. BasicClientCookie cookie = new BasicClientCookie(“JSESSIONID”, “1234”);
6. cookie.setDomain(“.github.com”);
7. cookie.setPath(“/”);
8. cookieStore.addCookie(cookie);
9. HttpClient client = HttpClientBuilder.create().
10. setDefaultCookieStore(cookieStore).build();
11.
12. final HttpGet request = new HttpGet(“http://www.github.com”);
13.
14. response = client.execute(request);
15. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
16. }
43
A very important element is the domain being set on the cookie – without
setting the proper domain, the client will not send the cookie at all!
With older versions of the HttpClient (before 4.3) – the cookie store was set
directly on the HttpClient:
1. @Test
2. public void givenUsingDeprecatedApi_whenSettingCookiesOnTheHttpClient_
3. thenCorrect()
4. throws ClientProtocolException, IOException {
5. BasicCookieStore cookieStore = new BasicCookieStore();
6. BasicClientCookie cookie = new BasicClientCookie(“JSESSIONID”, “1234”);
7. cookie.setDomain(“.github.com”);
8. cookie.setPath(“/”);
9. cookieStore.addCookie(cookie);
10. DefaultHttpClient client = new DefaultHttpClient();
11. client.setCookieStore(cookieStore);
12.
13. HttpGet request = new HttpGet(“http://www.github.com”);
14.
15. response = client.execute(request);
16. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
17. }
Other than the way the client is built, there’s no other difference from the
previous example.
44
3. Set the Cookie on the Request
If setting the cookie on the entire HttpClient is not an option, we can configure
requests with the cookie individually using the HttpContext class:
1. @Test
2. public void whenSettingCookiesOnTheRequest_thenCookieSentCorrectly()
3. throws ClientProtocolException, IOException {
4. BasicCookieStore cookieStore = new BasicCookieStore();
5. BasicClientCookie cookie = new BasicClientCookie(“JSESSIONID”, “1234”);
6. cookie.setDomain(“.github.com”);
7. cookie.setPath(“/”);
8. cookieStore.addCookie(cookie);
9. instance = HttpClientBuilder.create().build();
10.
11. HttpGet request = new HttpGet(“http://www.github.com”);
12.
13. HttpContext localContext = new BasicHttpContext();
14. localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
15. // localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
16. // before 4.3
17. response = instance.execute(request, localContext);
18.
19. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
16. }
45
4. Set the Cookie on the Low Level Request
A low level alternative of setting the cookie on the HTTP Request would be
setting it as a raw Header:
1. @Test
2. public void whenSettingCookiesOnARequest_thenCorrect()
3. throws ClientProtocolException, IOException {
4. instance = HttpClientBuilder.create().build();
5. HttpGet request = new HttpGet(“http://www.github.com”);
6. request.setHeader(“Cookie”, “JSESSIONID=1234”);
7.
8. response = instance.execute(request);
9.
10. assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
11. }
This is of course much more error-prone than working with the built
in cookie support. For example, notice that we’re no longer setting the
domain in this case – which is not correct.
46
5. Conclusion
This chapter illustrated how to work with the HttpClient to send a custom,
user controlled Cookie.
Note that this is not the same as letting the HttpClient deal with the cookies
set by a server. Instead, it’s controlling the client side manually at a low
level.
The implementation of all these examples and code snippets can be found
in my GitHub project.
47
7: HttpClient Basic Authentication
48
1. Overview
49
2. Basic Authentication with the API
50
The entire Client-Server communication is now clear:
51
3. Preemptive Basic Authentication
Now we can use the client with the new context and send the
pre-authentication request:
52
Let’s look at the logs:
1. [main] DEBUG ... - Re-using cached ‘basic’ auth scheme for http://
2. localhost:8082
3. [main] DEBUG ... - Executing request GET /spring-security-rest-basic-auth/
4. api/foos/1 HTTP/1.1
5. [main] DEBUG ... >> GET /spring-security-rest-basic-auth/api/foos/1
6. HTTP/1.1
7. [main] DEBUG ... >> Host: localhost:8082
8. [main] DEBUG ... >> Authorization: Basic dXNlcjE6dXNlcjFQYXNz
9. [main] DEBUG ... << HTTP/1.1 200 OK
10. [main] DEBUG ... - Authentication succeeded
53
4. Basic Auth with Raw HTTP Headers
So, instead of going through the rather complex previous example to set it
up, we can take control of this header and construct it by hand:
So, even though there is no auth cache, Basic Authentication still works
correctly and we receive 200 OK.
54
5. Conclusion
This chapter illustrated various ways to set up and use basic authentication
with the Apache HttpClient 4.
V3.1
55