com.comcast.drivethru.api.HTTPRequestManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of drive-thru Show documentation
Show all versions of drive-thru Show documentation
Drive-Thru is a helper utility for making HTTP requests
/**
* Copyright 2013 Comcast Cable Communications Management, LLC
*
* 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.
*/
/**
* @author Bobby Jap
*/
package com.comcast.drivethru.api;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.net.ssl.TrustManager;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
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.HttpOptions;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.comcast.drivethru.constants.ServerStatusCodes;
import com.comcast.drivethru.model.ResponseContainer;
public final class HTTPRequestManager
{
// PROPERTIES ----------------------------------------------------------------------------------------------------------
private static final Logger LOGGER = LoggerFactory.getLogger(HTTPRequestManager.class);
// HTTP Request properties
private String mUrl;
private byte[] mData;
private SSLConnectionSocketFactory mSocketFactory;
private String mContentType;
private Entry mAuth;
private METHOD mMethod;
private Map mHeaders;
private String[] mCookies;
private String mUserAgent;
// HTTP Request Types
public enum METHOD
{
GET,
POST,
PATCH,
OPTIONS,
DELETE,
PUT,
HEAD,
TRACE
}
// CONSTRUCTORS --------------------------------------------------------------------------------------------------------
/**
* Private constructor used by the Builder subclass to create and initialize the HTTPResposneManager object.
* @param builder Builder object that has all the needed parts to make a HTTP(S) request
*/
private HTTPRequestManager(Builder builder)
{
mUrl = builder.mUrl;
mData = builder.mData;
mSocketFactory = builder.mSocketFactory;
mContentType = builder.mContentType;
mAuth = builder.mAuth;
mMethod = builder.mMethod;
mHeaders = builder.mHeaders;
mUserAgent = builder.mUserAgent;
mCookies = builder.mCookies;
// verify that all required members have been set
if (mUrl == null) throw new IllegalStateException("URL is a required field");
if (mMethod == null) throw new IllegalStateException("Method is a required field");
}
// REQUEST GENERATION --------------------------------------------------------------------------------------------------
/**
* Creates a HttpClient object.
* @return client
*/
private CloseableHttpClient createHttpClient()
{
CloseableHttpClient client = null;
if (mSocketFactory != null && mUrl.startsWith("https"))
{
client = HttpClients.custom().setSSLSocketFactory(
mSocketFactory).build();
}
else
{
client = HttpClients.custom().build();
}
return client;
}
/**
* Configures HttpUrlConnection headers.
* This includes adding Cookies, Content-Type, User-Agent, Auth, and other headers
* @param message
*/
private void setHeaders(HttpUriRequest message)
{
// set cookies to connection (if needed)
if (mCookies != null)
{
for (String cookie : mCookies)
{
message.addHeader("Cookie", cookie);
}
}
// set content type
if ((mContentType != null) && !mContentType.isEmpty()) message.setHeader("Content-Type", mContentType);
// set user agent
if ((mUserAgent != null) && !mUserAgent.isEmpty()) message.setHeader("User-Agent", mUserAgent);
// add headers
for (String key : mHeaders.keySet())
{
message.setHeader(key, mHeaders.get(key));
}
// add auth header
if (mAuth != null) message.setHeader(mAuth.getKey(), mAuth.getValue());
}
/**
* Create the HTTP Method.
* @return the method
*/
private HttpUriRequest createMethod()
{
HttpUriRequest request = null;
switch (mMethod)
{
case GET:
request = new HttpGet(mUrl);
break;
case POST:
request = new HttpPost(mUrl);
break;
case PATCH:
request = new HttpPatch(mUrl);
break;
case OPTIONS:
request = new HttpOptions(mUrl);
break;
case DELETE:
request = new HttpDelete(mUrl);
break;
case PUT:
request = new HttpPut(mUrl);
break;
case HEAD:
request = new HttpHead(mUrl);
break;
case TRACE:
request = new HttpTrace(mUrl);
break;
default:
throw new UnsupportedOperationException("Unknown method: " + mMethod.toString());
}
return request;
}
// REQUEST DEPLOYMENT --------------------------------------------------------------------------------------------------
/**
* Send HTTP(S) Request.
* @return {@link ResponseContainer}
* @throws IOException When sending data to the server via HttpClient
*/
public ResponseContainer sendRequest() throws IOException
{
// create a http method based on provided method
HttpUriRequest httpMethod = createMethod();
// Create a HttpClient
CloseableHttpClient client = createHttpClient();
// set cookies
setHeaders(httpMethod);
ResponseContainer container = null;
if ((mData != null) && (mData.length > 0))
{
container = sendRequestWithData(client, httpMethod);
}
else
{
container = sendRequest(client, httpMethod);
}
return container;
}
/**
* Sends request with data and returns {@link ResponseContainer} object containing response's status code and body.
* @param client HttpClient use to send request
* @param request Http request object to be sent out
* @return {@link ResponseContainer} with response data
* @throws IOException When there's an error sending out request
*/
private ResponseContainer sendRequestWithData(CloseableHttpClient client, HttpUriRequest request) throws IOException
{
HttpEntityEnclosingRequestBase httpMethod = (HttpEntityEnclosingRequestBase) request;
HttpEntity entity = new ByteArrayEntity(mData);
httpMethod.setEntity(entity);
return sendRequest(client, httpMethod);
}
/**
* Sends request without data and returns {@link ResponseContainer} object containing response's status code and body.
* @param client HttpClient use to send request
* @param request Http request object to be sent out
* @return {@link ResponseContainer} with response data
* @throws IOException When there's an error sending out request
*/
private ResponseContainer sendRequest(CloseableHttpClient client, Object request) throws IOException
{
ResponseContainer responseContainer = null;
try
{
HttpResponse response = null;
LOGGER.info("Sending request to " + mUrl);
if (request instanceof HttpEntityEnclosingRequestBase)
{
response = client.execute((HttpEntityEnclosingRequestBase) request);
}
else
{
response = client.execute((HttpUriRequest) request);
}
int responseCode = response.getStatusLine().getStatusCode();
String responseText = EntityUtils.toString(response.getEntity());
Header[] headers = response.getAllHeaders();
String responseLog = "Response: " + responseCode + " - ";
if (responseText != null) responseLog += responseText;
if (responseCode == ServerStatusCodes.OK)
{
LOGGER.info(responseLog);
}
else
{
LOGGER.error(responseLog);
}
responseContainer = new ResponseContainer(responseCode, responseText, headers);
}
catch (IOException e)
{
throw new IOException("Connection failed. Request not sent");
}
finally
{
client.close();
}
return responseContainer;
}
/**
* Builder sub-class that is used to setup HTTPRequestHelper by providing necessary pieces.
* @author Dmitry Jerusalimsky
*/
public static class Builder
{
// PROPERTIES ----------------------------------------------------------------------------------------------------------
/**
* Default content type is: 'application/x-www-form-urlencoded'.
*/
private static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded";
/**
* Default request method: GET.
*/
private static final METHOD DEFAULT_METHOD = METHOD.GET;
/**
* Default encoding: UTF-8.
*/
private static final String DEFAULT_ENCODING = "UTF-8";
/**
* Default set of headers are: .
*
* - Accept-Language = en-US,en;q-0.5
* - accept-charset = UTF-8
*
*/
private static final Map DEFAULT_HEADERS;
static
{
DEFAULT_HEADERS = new HashMap<>();
DEFAULT_HEADERS.put("Accept-Language", "en-US,en;q=0.5");
DEFAULT_HEADERS.put("accept-charset", DEFAULT_ENCODING);
}
// HTTP Request properties
private String mContentType;
private Entry mAuth;
private String mUrl;
private METHOD mMethod;
private Map mHeaders;
private SSLConnectionSocketFactory mSocketFactory = null;
private byte[] mData;
private String mUserAgent;
private String[] mCookies;
// CONSTRUCTORS ----------------------------------------------------------------------------------------------------
/**
* Default constructor.
*/
public Builder()
{
mContentType = DEFAULT_CONTENT_TYPE;
mMethod = DEFAULT_METHOD;
mHeaders = DEFAULT_HEADERS;
}
// ACCESSORS AND MUTATORS ------------------------------------------------------------------------------------------
/**
* Sets request's User-Agent header.
* @param userAgent Desired User-Agent string
* @return {@link Builder} object
*/
public Builder userAgent(String userAgent)
{
mUserAgent = userAgent;
return this;
}
/**
* Sets request's Cookies header.
* @param cookies Array of cookies to be used by the request
* @return {@link Builder} object
*/
public Builder cookies(String[] cookies)
{
mCookies = cookies;
return this;
}
/**
* Sets request's data to be sent over.
* @param data Data to send to the server
* @return {@link Builder} object
*/
public Builder data(String data)
{
mData = data.getBytes();
return this;
}
/**
* Sets request's data to be sent over in byte format.
* @param data Data to send to the server
* @return {@link Builder} object
*/
public Builder data(byte[] data)
{
mData = data;
return this;
}
/**
* Sets request's url.
* @param url URL to send request to
* @return {@link Builder} object
*/
public Builder url(String url)
{
mUrl = url;
return this;
}
/**
* Sets request's authorization header.
* @param auth {@link Entry} object containing header name and it's value.
* @return {@link Builder} object
*/
public Builder auth(Entry auth)
{
mAuth = auth;
return this;
}
/**
* Sets request's Content-Type header.
* @param contentType Content-Type to be used. Default value is: application/x-www-form-urlencoded
* @return {@link Builder} object
*/
public Builder contentType(String contentType)
{
mContentType = contentType;
return this;
}
/**
* Sets request's method type (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE).
* @param method Method to be used by the request
* @return {@link Builder} object
* @Deprecated Please use {@link method(METHOD))} method
*/
public Builder method(String method)
{
if (method.equals("GET"))
{
mMethod = METHOD.GET;
}
else if (method.equals("POST"))
{
mMethod = METHOD.POST;
}
else if (method.equals("PATCH"))
{
mMethod = METHOD.PATCH;
}
else if (method.equals("OPTION"))
{
mMethod = METHOD.OPTIONS;
}
else if (method.equals("DELETE"))
{
mMethod = METHOD.DELETE;
}
else if (method.equals("PUT"))
{
mMethod = METHOD.PUT;
}
else if (method.equals("HEAD"))
{
mMethod = METHOD.HEAD;
}
else if (method.equals("TRACE"))
{
mMethod = METHOD.TRACE;
}
return this;
}
/**
* Sets request's method type.
* @param method Method type to use
* @return {@link Builder} object
*/
public Builder method(METHOD method)
{
mMethod = method;
return this;
}
/**
* Sets request's custom headers.
* @param headers Array of custom headers to be used by the request
* @return {@link Builder} object
*/
public Builder headers(Map headers)
{
mHeaders = headers;
return this;
}
/**
* Sets {@link TrustManager} array to allow for custom handling of SSL.
* @param socketFactory Array of {@link SSLConnectionSocketFactory} objects for SSL validation
* @return {@link Builder} object
*/
public Builder socketFactory(SSLConnectionSocketFactory socketFactory)
{
mSocketFactory = socketFactory;
return this;
}
/**
* Creates an instance of {@link HTTPRequestManager} class.
* @return A fully configured {@link HTTPRequestManager} object that is ready to send the request
*/
public HTTPRequestManager build()
{
return new HTTPRequestManager(this);
}
}
}