Skip to content

Commit 1ec6286

Browse files
committed
Changes to move dependency from Apache HttpClient to OkHttp in httpcore module
1 parent eef4fe9 commit 1ec6286

File tree

8 files changed

+149
-133
lines changed

8 files changed

+149
-133
lines changed

build.gradle

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ repositories {
2323
mavenCentral()
2424
}
2525

26-
def platformDependency = "org.apache.httpcomponents:httpclient:4.5.6"
27-
if ( project.hasProperty("platform") && project.platform == "android" ) {
28-
platformDependency = "org.apache.httpcomponents:httpclient-android:4.3.5"
29-
}
30-
3126
dependencies {
3227
// This dependency is exported to consumers, that is to say found on their compile classpath.
3328
api 'org.apache.commons:commons-math3:3.6.1'
@@ -38,7 +33,7 @@ dependencies {
3833
// Use JUnit test framework
3934
testImplementation 'junit:junit:4.12'
4035

41-
api platformDependency
36+
api 'com.squareup.okhttp3:okhttp:3.12.1'
4237

4338
// https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple
4439
compile group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1'

src/main/java/com/microsoft/graph/httpcore/AuthenticationHandler.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@
22

33
import java.io.IOException;
44

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;
5+
import okhttp3.Interceptor;
6+
import okhttp3.Request;
7+
import okhttp3.Response;
98

10-
public class AuthenticationHandler implements HttpRequestInterceptor {
9+
public class AuthenticationHandler implements Interceptor {
1110

1211
private IAuthenticationProvider authProvider;
1312

1413
public AuthenticationHandler(IAuthenticationProvider authProvider) {
1514
this.authProvider = authProvider;
1615
}
17-
16+
1817
@Override
19-
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
20-
authProvider.authenticateRequest(request);
18+
public Response intercept(Chain chain) throws IOException {
19+
Request originalRequest = chain.request();
20+
Request authenticatedRequest = authProvider.authenticateRequest(originalRequest);
21+
return chain.proceed(authenticatedRequest);
2122
}
2223

2324
}
Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package com.microsoft.graph.httpcore;
22

3-
import org.apache.http.client.config.RequestConfig;
4-
import org.apache.http.impl.client.CloseableHttpClient;
5-
import org.apache.http.impl.client.HttpClientBuilder;
3+
import okhttp3.OkHttpClient;
4+
import okhttp3.OkHttpClient.Builder;
65

76
public class HttpClients {
87
private HttpClients() {
@@ -11,31 +10,21 @@ private HttpClients() {
1110

1211
/**
1312
* Creates builder object for construction of custom
14-
* {@link CloseableHttpClient} instances.
13+
* {@link OkHttpClient} instances.
1514
*/
16-
public static HttpClientBuilder custom() {
17-
return HttpClientBuilder.create();
15+
public static Builder custom() {
16+
return new OkHttpClient.Builder();
1817
}
1918

2019
/**
21-
* Creates {@link CloseableHttpClient} instance with default
20+
* Creates {@link OkHttpClient} instance with default
2221
* configuration and provided authProvider
2322
*/
24-
public static CloseableHttpClient createDefault(IAuthenticationProvider auth) {
25-
RequestConfig config = RequestConfig.custom().setMaxRedirects(5).build();
23+
public static OkHttpClient createDefault(IAuthenticationProvider auth) {
2624

27-
return HttpClientBuilder.create().addInterceptorFirst(new AuthenticationHandler(auth))
28-
.setRedirectStrategy(new RedirectHandler())
29-
.setServiceUnavailableRetryStrategy(new RetryHandler())
30-
.setDefaultRequestConfig(config)
25+
return new OkHttpClient.Builder().addInterceptor(new AuthenticationHandler(auth))
26+
.addInterceptor(new RetryHandler())
27+
.addInterceptor(new RedirectHandler())
3128
.build();
3229
}
33-
34-
/**
35-
* Creates {@link CloseableHttpClient} instance with default
36-
* configuration based on system properties.
37-
*/
38-
public static CloseableHttpClient createSystem() {
39-
return HttpClientBuilder.create().useSystemProperties().build();
40-
}
4130
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.microsoft.graph.httpcore;
22

3-
import org.apache.http.HttpRequest;
3+
import okhttp3.Request;
44

55
public interface IAuthenticationProvider {
66
/**
77
* Authenticates the request
88
*
99
* @param request the request to authenticate
1010
*/
11-
void authenticateRequest(HttpRequest request);
11+
Request authenticateRequest(Request request);
1212
}
Lines changed: 84 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,101 @@
11
package com.microsoft.graph.httpcore;
22

3-
import java.net.URI;
4-
import java.net.URISyntaxException;
3+
import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
4+
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
5+
import static java.net.HttpURLConnection.HTTP_SEE_OTHER;
6+
import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT;
7+
import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
58

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;
9+
import java.io.IOException;
10+
import java.net.ProtocolException;
1811

19-
public class RedirectHandler extends DefaultRedirectStrategy{
12+
import okhttp3.HttpUrl;
13+
import okhttp3.Interceptor;
14+
import okhttp3.Request;
15+
import okhttp3.RequestBody;
16+
import okhttp3.Response;
17+
import okhttp3.internal.http.HttpMethod;
18+
19+
public class RedirectHandler implements Interceptor{
2020

2121
public static final RedirectHandler INSTANCE = new RedirectHandler();
22+
final int maxRedirect = 5;
2223

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");
24+
public boolean isRedirected(Request request, Response response, int redirectCount) throws IOException {
25+
26+
if(redirectCount > maxRedirect) return false;
27+
28+
final String locationHeader = response.header("location");
3329
if(locationHeader == null)
3430
return false;
3531

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-
statusCode == 308)
32+
final int statusCode = response.code();
33+
if(statusCode == HTTP_PERM_REDIRECT ||
34+
statusCode == HTTP_MOVED_PERM ||
35+
statusCode == HTTP_TEMP_REDIRECT ||
36+
statusCode == HTTP_SEE_OTHER ||
37+
statusCode == HTTP_MOVED_TEMP)
4138
return true;
4239

4340
return false;
4441
}
4542

46-
@Override
47-
public HttpUriRequest getRedirect(
48-
final HttpRequest request,
49-
final HttpResponse response,
50-
final HttpContext context) throws ProtocolException {
51-
final URI uri = getLocationURI(request, response, context);
52-
try {
53-
final URI requestURI = new URI(request.getRequestLine().getUri());
54-
if(!uri.getHost().equalsIgnoreCase(requestURI.getHost()) ||
55-
!uri.getScheme().equalsIgnoreCase(requestURI.getScheme()))
56-
request.removeHeaders("Authorization");
57-
}
58-
catch (final URISyntaxException ex) {
59-
throw new ProtocolException(ex.getMessage(), ex);
60-
}
61-
62-
final int status = response.getStatusLine().getStatusCode();
63-
if(status == HttpStatus.SC_SEE_OTHER)
64-
return new HttpGet(uri);
65-
return RequestBuilder.copy(request).setUri(uri).build();
43+
public Request getRedirect(
44+
final Request request,
45+
final Response userResponse) throws ProtocolException {
46+
String location = userResponse.header("Location");
47+
if (location == null) return null;
48+
HttpUrl url = userResponse.request().url().resolve(location);
49+
// Don't follow redirects to unsupported protocols.
50+
if (url == null) return null;
51+
52+
// Most redirects don't include a request body.
53+
Request.Builder requestBuilder = userResponse.request().newBuilder();
54+
final String method = userResponse.request().method();
55+
56+
if (HttpMethod.permitsRequestBody(method)) {
57+
final boolean maintainBody = HttpMethod.redirectsWithBody(method);
58+
if (HttpMethod.redirectsToGet(method)) {
59+
requestBuilder.method("GET", null);
60+
} else {
61+
RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
62+
requestBuilder.method(method, requestBody);
63+
}
64+
if (!maintainBody) {
65+
requestBuilder.removeHeader("Transfer-Encoding");
66+
requestBuilder.removeHeader("Content-Length");
67+
requestBuilder.removeHeader("Content-Type");
68+
}
69+
}
70+
71+
// When redirecting across hosts, drop all authentication headers. This
72+
// is potentially annoying to the application layer since they have no
73+
// way to retain them.
74+
boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
75+
boolean sameHost = url.host().equals(userResponse.request().url().host());
76+
if (!sameScheme || !sameHost) {
77+
requestBuilder.removeHeader("Authorization");
78+
}
79+
80+
return requestBuilder.url(url).build();
6681
}
82+
83+
@Override
84+
public Response intercept(Chain chain) throws IOException {
85+
Request request = chain.request();
86+
Response response = null;
87+
int redirectCount = 1;
88+
while(true) {
89+
response = chain.proceed(request);
90+
boolean shouldRedirect = isRedirected(request, response, redirectCount);
91+
if(!shouldRedirect) break;
92+
93+
Request followup = getRedirect(request, response);
94+
if(followup == null) break;
95+
request = followup;
96+
97+
redirectCount++;
98+
}
99+
return response;
100+
}
67101
}
Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
package com.microsoft.graph.httpcore;
22

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;
3+
import java.io.IOException;
154

16-
import com.microsoft.graph.httpcore.middlewareoption.MiddlewareType;
175
import com.microsoft.graph.httpcore.middlewareoption.RetryOptions;
186

19-
public class RetryHandler implements ServiceUnavailableRetryStrategy{
7+
import okhttp3.Interceptor;
8+
import okhttp3.Request;
9+
import okhttp3.Response;
10+
11+
public class RetryHandler implements Interceptor{
2012

2113
/**
2214
* Maximum number of allowed retries if the server responds with a HTTP code
@@ -47,32 +39,30 @@ public RetryHandler() {
4739
this(null);
4840
}
4941

50-
@Override
51-
public boolean retryRequest(HttpResponse response, int executionCount, HttpContext context) {
42+
public boolean retryRequest(Response response, int executionCount, Request request) {
5243

53-
RetryOptions retryOption = (RetryOptions)context.getAttribute(MiddlewareType.RETRY.toString());
44+
RetryOptions retryOption = request.tag(RetryOptions.class);
5445
if(retryOption != null) {
55-
return retryOption.shouldRetry().shouldRetry(response, executionCount, context);
46+
return retryOption.shouldRetry().shouldRetry(response, executionCount, request);
5647
}
5748
if(mRetryOption != null) {
58-
return mRetryOption.shouldRetry().shouldRetry(response, executionCount, context);
49+
return mRetryOption.shouldRetry().shouldRetry(response, executionCount, request);
5950
}
6051

6152
boolean shouldRetry = false;
62-
int statusCode = response.getStatusLine().getStatusCode();
63-
shouldRetry = (executionCount < maxRetries) && checkStatus(statusCode) && isBuffered(response, context);
53+
int statusCode = response.code();
54+
shouldRetry = (executionCount < maxRetries) && checkStatus(statusCode) && isBuffered(response, request);
6455

6556
if(shouldRetry) {
66-
Header header = response.getFirstHeader(RETRY_AFTER);
67-
if(header != null)
68-
retryInterval = Long.parseLong(header.getValue());
57+
String retryAfterHeader = response.header(RETRY_AFTER);
58+
if(retryAfterHeader != null)
59+
retryInterval = Long.parseLong(retryAfterHeader);
6960
else
7061
retryInterval = (long)Math.pow(2.0, (double)executionCount) * DELAY_MILLISECONDS;
7162
}
7263
return shouldRetry;
7364
}
7465

75-
@Override
7666
public long getRetryInterval() {
7767
return retryInterval;
7868
}
@@ -84,27 +74,34 @@ private boolean checkStatus(int statusCode) {
8474
return false;
8575
}
8676

87-
private boolean isBuffered(HttpResponse response, HttpContext context) {
88-
HttpRequest request = (HttpRequest)context.getAttribute( HttpCoreContext.HTTP_REQUEST);
89-
String methodName = request.getRequestLine().getMethod();
77+
private boolean isBuffered(Response response, Request request) {
78+
String methodName = request.method();
9079

91-
boolean isHTTPMethodPutPatchOrPost = methodName.equalsIgnoreCase(HttpPost.METHOD_NAME) ||
92-
methodName.equalsIgnoreCase(HttpPut.METHOD_NAME) ||
93-
methodName.equalsIgnoreCase(HttpPatch.METHOD_NAME);
80+
boolean isHTTPMethodPutPatchOrPost = methodName.equalsIgnoreCase("POST") ||
81+
methodName.equalsIgnoreCase("PUT") ||
82+
methodName.equalsIgnoreCase("PATCH");
9483

95-
Header transferEncoding = response.getFirstHeader(TRANSFER_ENCODING);
84+
//Header transferEncoding = response.getFirstHeader(TRANSFER_ENCODING);
85+
String transferEncoding = response.header(TRANSFER_ENCODING);
9686
boolean isTransferEncodingChunked = (transferEncoding != null) &&
97-
transferEncoding.getValue().equalsIgnoreCase("chunked");
98-
99-
HttpEntity entity = null;
100-
if(request instanceof HttpEntityEnclosingRequestBase) {
101-
HttpEntityEnclosingRequestBase httprequest = (HttpEntityEnclosingRequestBase)request;
102-
entity = httprequest.getEntity();
103-
}
87+
transferEncoding.equalsIgnoreCase("chunked");
10488

105-
if(entity != null && isHTTPMethodPutPatchOrPost && isTransferEncodingChunked)
89+
if(request.body() != null && isHTTPMethodPutPatchOrPost && isTransferEncodingChunked)
10690
return false;
10791
return true;
10892
}
10993

94+
@Override
95+
public Response intercept(Chain chain) throws IOException {
96+
Request request = chain.request();
97+
98+
Response response = chain.proceed(request);
99+
int executionCount = 0;
100+
while(retryRequest(response, executionCount, request)) {
101+
executionCount++;
102+
response = chain.proceed(request);
103+
}
104+
return response;
105+
}
106+
110107
}

0 commit comments

Comments
 (0)