/* * Copyright 2014, The Sporting Exchange Limited * Copyright 2015, Simon Matić Langford * * 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.betfair.cougar.client; import com.betfair.cougar.CougarVersion; import com.betfair.cougar.api.ExecutionContext; import com.betfair.cougar.client.api.ContextEmitter; import com.betfair.cougar.core.api.ev.TimeConstraints; import com.betfair.cougar.marshalling.api.databinding.Marshaller; import org.apache.http.Header; import org.apache.http.message.BasicHeader; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.apache.http.HttpHeaders.*; /** * {@code CougarRequestFactory} describes contract and base behaviour for creating http requests on client side. * Particular implementations are to be used within each actual transport. */ public abstract class CougarRequestFactory<HR> { protected static final String UTF8 = "utf-8"; // todo: base this on app name as well? static final String USER_AGENT_HEADER = "Cougar Client " + CougarVersion.getVersion(); private final ContextEmitter<HR,List<Header>> contextEmission; private volatile boolean gzipCompressionEnabled; public CougarRequestFactory(ContextEmitter<HR,List<Header>> contextEmission) { this.contextEmission = contextEmission; } public HR create(final String uri, final String httpMethod, final Message message, final Marshaller marshaller, final String contentType, final ClientCallContext ctx, final TimeConstraints timeConstraints) { final HR httpRequest = createRequest(httpMethod, uri); if ("POST".equalsIgnoreCase(httpMethod)) { addPostEntity(httpRequest, createPostEntity(message, marshaller), contentType); } List<Header> coreHeaders = constructRequestHeaders(message, contentType, timeConstraints); contextEmission.emit(ctx, httpRequest, coreHeaders); addHeaders(httpRequest, coreHeaders); return httpRequest; } /** * Add the specified headers to the request. * * @param httpRequest the request * @param headers the headers to add. */ protected abstract void addHeaders(HR httpRequest, List<Header> headers); /** * Adds the specified entity to the request as a POST body, with the specified content-type. * * @param httpRequest the request * @param postEntity the entity * @param contentType the content type */ protected abstract void addPostEntity(HR httpRequest, String postEntity, String contentType); /** * Make a HTTP request object suitable for connecting to the specified URL with the given method. * * @param httpMethod the method * @param uri the uri * @return a HTTP request object * @throws UnsupportedOperationException if the method isn't supported */ protected abstract HR createRequest(String httpMethod, String uri); private String createPostEntity(final Message message, final Marshaller marshaller) { try { final ByteArrayOutputStream os = new ByteArrayOutputStream(); marshaller.marshall(os, message.getRequestBodyMap(), UTF8, true); return os.toString(UTF8); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } private List<Header> constructRequestHeaders(final Message message, final String contentType, TimeConstraints timeConstraints) { final List<Header> result = new ArrayList<Header>(); result.add(new BasicHeader(ACCEPT, contentType)); if (gzipCompressionEnabled) { result.add(new BasicHeader(ACCEPT_ENCODING, "gzip")); } result.add(new BasicHeader(USER_AGENT, USER_AGENT_HEADER)); if (timeConstraints.getTimeRemaining() != null) { result.add(new BasicHeader("X-RequestTimeout", String.valueOf(timeConstraints.getTimeRemaining()))); } for (Map.Entry<String, Object> entry : message.getHeaderMap().entrySet()) { result.add(new BasicHeader(entry.getKey(), entry.getValue().toString())); } return result; } public void setGzipCompressionEnabled(boolean gzipCompressionEnabled) { this.gzipCompressionEnabled = gzipCompressionEnabled; } }