com.okta.sdk.impl.http.httpclient.HttpClientRequestFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of okta-sdk-httpclient Show documentation
Show all versions of okta-sdk-httpclient Show documentation
Moved to com.okta.commons:okta-http-httpclient
/*
* Copyright 2014 Stormpath, Inc.
* Modifications Copyright 2018 Okta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.okta.sdk.impl.http.httpclient;
import com.okta.commons.lang.Assert;
import com.okta.commons.lang.Strings;
import com.okta.sdk.http.HttpMethod;
import com.okta.sdk.impl.http.QueryString;
import com.okta.sdk.impl.http.Request;
import com.okta.sdk.impl.http.RestException;
import com.okta.sdk.impl.util.RequestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpVersion;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.BufferedHttpEntity;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import java.util.Map;
/**
* Responsible for creating Apache HttpClient 4 request objects.
*
* @since 0.5.0
*/
class HttpClientRequestFactory {
private final RequestConfig defaultRequestConfig;
HttpClientRequestFactory(RequestConfig defaultRequestConfig) {
Assert.notNull(defaultRequestConfig, "defaultRequestConfig");
this.defaultRequestConfig = defaultRequestConfig;
}
/**
* Creates an HttpClient method object based on the specified request and
* populates any parameters, headers, etc. from the original request.
*
* @param request The request to convert to an HttpClient method object.
* @param previousEntity The optional, previous HTTP entity to reuse in the new request.
* @return The converted HttpClient method object with any parameters,
* headers, etc. from the original request set.
*/
HttpRequestBase createHttpClientRequest(Request request, HttpEntity previousEntity) {
HttpMethod method = request.getMethod();
URI uri = getFullyQualifiedUri(request);
InputStream body = request.getBody();
long contentLength = request.getHeaders().getContentLength();
HttpRequestBase base;
switch (method) {
case DELETE:
base = new HttpDelete(uri);
break;
case GET:
base = new HttpGet(uri);
break;
case HEAD:
base = new HttpHead(uri);
break;
case POST:
base = new HttpPost(uri);
((HttpEntityEnclosingRequestBase)base).setEntity(new RepeatableInputStreamEntity(request));
break;
case PUT:
base = new HttpPut(uri);
// Enable 100-continue support for PUT operations, since this is where we're potentially uploading
// large amounts of data and want to find out as early as possible if an operation will fail. We
// don't want to do this for all operations since it will cause extra latency in the network
// interaction.
base.setConfig(RequestConfig.copy(defaultRequestConfig).setExpectContinueEnabled(true).build());
if (previousEntity != null) {
((HttpEntityEnclosingRequestBase)base).setEntity(previousEntity);
} else if (body != null) {
HttpEntity entity = new RepeatableInputStreamEntity(request);
if (contentLength < 0) {
entity = newBufferedHttpEntity(entity);
}
((HttpEntityEnclosingRequestBase)base).setEntity(entity);
}
break;
default:
throw new IllegalArgumentException("Unrecognized HttpMethod: " + method);
}
base.setProtocolVersion(HttpVersion.HTTP_1_1);
applyHeaders(base, request);
return base;
}
/**
* Configures the headers in the specified Apache HTTP request.
*/
private void applyHeaders(HttpRequestBase httpRequest, Request request) {
/*
* Apache HttpClient omits the port number in the Host header (even if
* we explicitly specify it) if it's the default port for the protocol
* in use. To ensure that we use the same Host header in the request and
* in the calculated string to sign (even if Apache HttpClient changed
* and started honoring our explicit host with endpoint), we follow this
* same behavior here and in the RequestAuthenticator.
*/
URI endpoint = request.getResourceUrl();
String hostHeader = endpoint.getHost();
if (!RequestUtils.isDefaultPort(endpoint)) {
hostHeader += ":" + endpoint.getPort();
}
httpRequest.addHeader("Host", hostHeader);
httpRequest.addHeader("Accept-Encoding", "gzip");
// Copy over any other headers already in our request
for (Map.Entry> entry : request.getHeaders().entrySet()) {
String key = entry.getKey();
List value = entry.getValue();
/*
* HttpClient4 fills in the Content-Length header and complains if
* it's already present, so we skip it here. We also skip the Host
* header to avoid sending it twice, which will interfere with some
* signing schemes.
*/
if (!"Content-Length".equalsIgnoreCase(key) && !"Host".equalsIgnoreCase(key)) {
String delimited = Strings.collectionToCommaDelimitedString(value);
httpRequest.addHeader(key, delimited);
}
}
}
private URI getFullyQualifiedUri(Request request) {
StringBuilder sb = new StringBuilder();
sb.append(request.getResourceUrl().normalize());
QueryString query = request.getQueryString();
if (query != null && !query.isEmpty()) {
sb.append("?").append(query.toString());
}
return URI.create(sb.toString());
}
/**
* Utility function for creating a new BufferedEntity and wrapping any errors
* as a RestException.
*
* @param entity The HTTP entity to wrap with a buffered HTTP entity.
* @return A new BufferedHttpEntity wrapping the specified entity.
*/
private HttpEntity newBufferedHttpEntity(HttpEntity entity) {
try {
return new BufferedHttpEntity(entity);
} catch (IOException e) {
throw new RestException("Unable to create HTTP entity: " + e.getMessage(), e);
}
}
}