HttpUtil.java

/*
 * Copyright (c) 2007-2017 MetaSolutions AB
 *
 * 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 org.entrystore.rest.util;

import static com.google.common.base.Preconditions.checkArgument;
import static org.restlet.engine.header.HeaderConstants.HEADER_X_FORWARDED_FOR;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.entrystore.rest.EntryStoreApplication;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Preference;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.representation.Representation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Hannes Ebner
 */
public class HttpUtil {

	public static final String COOKIE_AUTH_TOKEN = "auth_token";

	private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);

	private static final Client client;

	private static final String USERAGENT;

	static {
		Context clientContext = new Context();
		client = new Client(clientContext, Arrays.asList(Protocol.HTTP, Protocol.HTTPS));
		setTimeouts(10000);
		log.debug("Initialized HTTP client");
		USERAGENT = "EntryStore/" + EntryStoreApplication.getVersion().trim()
				+ " ("
				+ System.getProperty("os.arch") + "; "
				+ System.getProperty("os.name") + " " + System.getProperty("os.version") + "; "
				+ "Java; "
				+ System.getProperty("java.vendor") + " " + System.getProperty("java.version")
				+ ")";
		log.debug("User-Agent for HTTP requests set to \"" + USERAGENT + "\"");
	}

	private HttpUtil() {}

	public static Response getResourceFromURL(String url) {
		return getResourceFromURL(url, 0, 0, 0, null);
	}

	public static Response getResourceFromURL(String url, int loopCount, int retriesOnError, long timeBetweenRetries, List<MediaType> prefMediaType) {
		if (loopCount > 10) {
			log.warn("More than 10 redirect loops detected, aborting");
			return null;
		}

		Request request = new Request(Method.GET, url);
		if (prefMediaType == null) {
			prefMediaType = new ArrayList<MediaType>();
			prefMediaType.add(MediaType.ALL);
		}

		List<Preference<MediaType>> accMediaTypes = new ArrayList<>();
		for (MediaType mt : prefMediaType) {
			accMediaTypes.add(new Preference<MediaType>(mt));
		}
		request.getClientInfo().setAcceptedMediaTypes(accMediaTypes);

		request.getClientInfo().setAgent(USERAGENT);
		Response response = null;
		int tries = 0;
		do {
			response = client.handle(request);
			tries++;
			if (retriesOnError > 0 && response.getStatus().isError()) {
				try {
					log.info("Error when fetching <" + url + ">, retrying in " + timeBetweenRetries + " ms");
					Thread.sleep(timeBetweenRetries);
				} catch (InterruptedException e) {
					log.error(e.getMessage());
				}
			} else {
				break;
			}
		} while (tries < retriesOnError);

		// Alternative to calling the client directly:
		// HttpClientHelper helper = new HttpClientHelper(client);
		// Response response = new Response(request);
		// helper.handle(request, response);

		if (response.getStatus().isRedirection()) {
			Reference ref = response.getLocationRef();
			try {
				response.getEntity().exhaust();
			} catch (IOException e) {
				log.warn(e.getMessage());
			}
			response.getEntity().release();
			if (ref != null) {
				String refURL = ref.getIdentifier();
				log.debug("Request redirected from <" + url + "> to <" + refURL + ">");
				return getResourceFromURL(refURL, loopCount + 1, retriesOnError, timeBetweenRetries, prefMediaType);
			}
		}

		if (response.getEntity() != null && response.getEntity().getLocationRef() != null && response.getEntity().getLocationRef().getBaseRef() == null) {
			response.getEntity().getLocationRef().setBaseRef(url.substring(0, url.lastIndexOf("/") + 1));
		}
		return response;
	}

	public static void setTimeouts(long timeout) {
		String timeoutStr = Long.toString(timeout);
		client.getContext().getParameters().set("connectTimeout", timeoutStr);
		client.getContext().getParameters().set("socketTimeout", timeoutStr);
		client.getContext().getParameters().set("readTimeout", timeoutStr);
		client.getContext().getParameters().set("socketConnectTimeoutMs", timeoutStr);
	}

	public static String readFirstLine(URL url) {
		BufferedReader in = null;
		try {
			in = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8));
			return in.readLine();
		} catch (IOException ioe) {
			log.error(ioe.getMessage());
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					log.error(e.getMessage());
				}
			}
		}
		return null;
	}

	public static boolean isLargerThan(Representation r, long maxSize) {
		if (r == null || r.isEmpty()) {
			return false;
		}
		long repSize = r.getSize();
		if (repSize == Representation.UNKNOWN_SIZE) {
			log.warn("Size of representation is unknown");
			return true;
		} else return repSize > maxSize;
	}

	/**
	 * Returns the client IP, or a comma separated list of
	 * @param request
	 * @return
	 */
	public static String getClientIpAddress(Request request) {
		checkArgument(request != null, "request must not be null");
		String s = request.getHeaders().getFirstValue(HEADER_X_FORWARDED_FOR, true, request.getClientInfo().getAddress());
		String[] clientIpArray = StringUtils.split(s, ',');
		if (ArrayUtils.isNotEmpty(clientIpArray)) {
			return clientIpArray[0];
		}
		return null;
	}
}