Skip to content

Commit 0e8c72d

Browse files
committed
Adding HttpClient and middleware classes
1 parent f9acadc commit 0e8c72d

File tree

5 files changed

+254
-0
lines changed

5 files changed

+254
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.microsoft.graph.httpcore;
2+
3+
import java.io.IOException;
4+
5+
import org.apache.http.HttpException;
6+
import org.apache.http.HttpRequest;
7+
import org.apache.http.HttpRequestInterceptor;
8+
import org.apache.http.protocol.HttpContext;
9+
10+
public class AuthenticationHandler implements HttpRequestInterceptor {
11+
12+
private IAuthenticationProvider authProvider;
13+
14+
public AuthenticationHandler(IAuthenticationProvider authProvider) {
15+
this.authProvider = authProvider;
16+
}
17+
18+
@Override
19+
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
20+
// TODO Auto-generated method stub
21+
authProvider.authenticateRequest(request);
22+
}
23+
24+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.microsoft.graph.httpcore;
2+
3+
import org.apache.http.client.config.RequestConfig;
4+
import org.apache.http.impl.client.CloseableHttpClient;
5+
import org.apache.http.impl.client.HttpClientBuilder;
6+
7+
public class HttpClients {
8+
private HttpClients() {
9+
super();
10+
}
11+
12+
/**
13+
* Creates builder object for construction of custom
14+
* {@link CloseableHttpClient} instances.
15+
*/
16+
public static HttpClientBuilder custom() {
17+
return HttpClientBuilder.create();
18+
}
19+
20+
/**
21+
* Creates {@link CloseableHttpClient} instance with default
22+
* configuration.
23+
*/
24+
public static CloseableHttpClient createDefault() {
25+
//return HttpClientBuilder.create().build();
26+
27+
RequestConfig config = RequestConfig.custom().setMaxRedirects(5).build();
28+
29+
IAuthenticationProvider auth = null;
30+
return HttpClientBuilder.create().addInterceptorFirst(new AuthenticationHandler(null))
31+
.setRedirectStrategy(new RedirectHandler())
32+
.setServiceUnavailableRetryStrategy(new RetryHandler())
33+
.setDefaultRequestConfig(config)
34+
.build();
35+
}
36+
37+
/**
38+
* Creates {@link CloseableHttpClient} instance with default
39+
* configuration based on system properties.
40+
*/
41+
public static CloseableHttpClient createSystem() {
42+
return HttpClientBuilder.create().useSystemProperties().build();
43+
}
44+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.microsoft.graph.httpcore;
2+
3+
import org.apache.http.HttpRequest;
4+
5+
public interface IAuthenticationProvider {
6+
/**
7+
* Authenticates the request
8+
*
9+
* @param request the request to authenticate
10+
*/
11+
void authenticateRequest(final HttpRequest request);
12+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.microsoft.graph.httpcore;
2+
3+
import java.net.URI;
4+
import java.net.URISyntaxException;
5+
6+
import org.apache.http.Header;
7+
import org.apache.http.HttpRequest;
8+
import org.apache.http.HttpResponse;
9+
import org.apache.http.HttpStatus;
10+
import org.apache.http.ProtocolException;
11+
import org.apache.http.client.methods.HttpGet;
12+
import org.apache.http.client.methods.HttpHead;
13+
import org.apache.http.client.methods.HttpUriRequest;
14+
import org.apache.http.client.methods.RequestBuilder;
15+
import org.apache.http.impl.client.DefaultRedirectStrategy;
16+
import org.apache.http.protocol.HttpContext;
17+
import org.apache.http.util.Args;
18+
19+
public class RedirectHandler extends DefaultRedirectStrategy{
20+
21+
public static final RedirectHandler INSTANCE = new RedirectHandler();
22+
23+
@Override
24+
public boolean isRedirected(
25+
final HttpRequest request,
26+
final HttpResponse response,
27+
final HttpContext context) throws ProtocolException {
28+
Args.notNull(request, "HTTP request");
29+
Args.notNull(response, "HTTP response");
30+
31+
final int statusCode = response.getStatusLine().getStatusCode();
32+
final Header locationHeader = response.getFirstHeader("location");
33+
if(locationHeader == null)
34+
return false;
35+
36+
if(statusCode == HttpStatus.SC_MOVED_TEMPORARILY ||
37+
statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
38+
statusCode == HttpStatus.SC_TEMPORARY_REDIRECT ||
39+
statusCode == HttpStatus.SC_SEE_OTHER)
40+
return true;
41+
42+
return false;
43+
}
44+
45+
@Override
46+
public HttpUriRequest getRedirect(
47+
final HttpRequest request,
48+
final HttpResponse response,
49+
final HttpContext context) throws ProtocolException {
50+
final URI uri = getLocationURI(request, response, context);
51+
final String method = request.getRequestLine().getMethod();
52+
if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) {
53+
return new HttpHead(uri);
54+
} else if (method.equalsIgnoreCase(HttpGet.METHOD_NAME))
55+
return new HttpGet(uri);
56+
else {
57+
final int status = response.getStatusLine().getStatusCode();
58+
if(status != HttpStatus.SC_SEE_OTHER) {
59+
try {
60+
final URI requestURI = new URI(request.getRequestLine().getUri());
61+
if(!uri.getHost().equalsIgnoreCase(requestURI.getHost())) {
62+
request.removeHeaders("Authorization");
63+
}
64+
return RequestBuilder.copy(request).setUri(uri).build();
65+
}
66+
catch (final URISyntaxException ex) {
67+
throw new ProtocolException(ex.getMessage(), ex);
68+
}
69+
}
70+
return new HttpGet(uri);
71+
}
72+
}
73+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.microsoft.graph.httpcore;
2+
3+
import org.apache.http.Header;
4+
import org.apache.http.HttpEntity;
5+
import org.apache.http.HttpRequest;
6+
import org.apache.http.HttpResponse;
7+
import org.apache.http.client.ServiceUnavailableRetryStrategy;
8+
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
9+
import org.apache.http.client.methods.HttpPatch;
10+
import org.apache.http.client.methods.HttpPost;
11+
import org.apache.http.client.methods.HttpPut;
12+
import org.apache.http.protocol.HttpContext;
13+
import org.apache.http.protocol.HttpCoreContext;
14+
import org.apache.http.util.Args;
15+
16+
public class RetryHandler implements ServiceUnavailableRetryStrategy{
17+
18+
/**
19+
* Maximum number of allowed retries if the server responds with a HTTP code
20+
* in our retry code list. Default value is 1.
21+
*/
22+
private final int maxRetries;
23+
24+
/**
25+
* Retry interval between subsequent requests, in milliseconds. Default
26+
* value is 1 second.
27+
*/
28+
private long retryInterval;
29+
private final int DELAY_SECONDS = 10;
30+
private final String RETRY_AFTER = "Retry-After";
31+
private final String TRANSFER_ENCODING = "Transfer-Encoding";
32+
33+
private final int MSClientErrorCodeTooManyRequests = 429;
34+
private final int MSClientErrorCodeServiceUnavailable = 503;
35+
private final int MSClientErrorCodeGatewayTimeout = 504;
36+
37+
public RetryHandler(final int maxRetries, final int retryInterval) {
38+
super();
39+
Args.positive(maxRetries, "Max retries");
40+
Args.positive(retryInterval, "Retry interval");
41+
this.maxRetries = maxRetries;
42+
this.retryInterval = retryInterval;
43+
}
44+
45+
public RetryHandler() {
46+
this(1, 1000);
47+
}
48+
49+
@Override
50+
public boolean retryRequest(HttpResponse response, int executionCount, HttpContext context) {
51+
boolean shouldRetry = false;
52+
int statusCode = response.getStatusLine().getStatusCode();
53+
shouldRetry = (executionCount < maxRetries) && checkStatus(statusCode) && isBuffered(response, context);
54+
55+
if(shouldRetry) {
56+
Header header = response.getFirstHeader(RETRY_AFTER);
57+
if(header != null)
58+
retryInterval = Long.parseLong(header.getValue());
59+
else
60+
retryInterval = (long)Math.pow(2.0, (double)executionCount) * DELAY_SECONDS;
61+
}
62+
return shouldRetry;
63+
}
64+
65+
@Override
66+
public long getRetryInterval() {
67+
// TODO Auto-generated method stub
68+
return retryInterval;
69+
}
70+
71+
private boolean checkStatus(int statusCode) {
72+
if (statusCode == MSClientErrorCodeTooManyRequests || statusCode == MSClientErrorCodeServiceUnavailable
73+
|| statusCode == MSClientErrorCodeGatewayTimeout)
74+
return true;
75+
return false;
76+
}
77+
78+
private boolean isBuffered(HttpResponse response, HttpContext context) {
79+
HttpRequest request = (HttpRequest)context.getAttribute( HttpCoreContext.HTTP_REQUEST);
80+
String methodName = request.getRequestLine().getMethod();
81+
82+
boolean isHTTPMethodPutPatchOrPost = methodName.equalsIgnoreCase(HttpPost.METHOD_NAME) ||
83+
methodName.equalsIgnoreCase(HttpPut.METHOD_NAME) ||
84+
methodName.equalsIgnoreCase(HttpPatch.METHOD_NAME);
85+
86+
Header transferEncoding = response.getFirstHeader(TRANSFER_ENCODING);
87+
boolean isTransferEncodingChunked = (transferEncoding != null) &&
88+
transferEncoding.getValue().equalsIgnoreCase("chunked");
89+
90+
HttpEntity entity = null;
91+
if(request instanceof HttpEntityEnclosingRequestBase) {
92+
HttpEntityEnclosingRequestBase httprequest = (HttpEntityEnclosingRequestBase)request;
93+
entity = httprequest.getEntity();
94+
}
95+
96+
if(entity != null && isHTTPMethodPutPatchOrPost && isTransferEncodingChunked)
97+
return false;
98+
return true;
99+
}
100+
101+
}

0 commit comments

Comments
 (0)