All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.nosan.embedded.cassandra.commons.web.JdkHttpClient Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
/*
 * Copyright 2020-2021 the original author or authors.
 *
 * 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
 *
 *      https://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.github.nosan.embedded.cassandra.commons.web;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.time.Duration;
import java.util.Objects;

import com.github.nosan.embedded.cassandra.commons.StreamUtils;
import com.github.nosan.embedded.cassandra.commons.StringUtils;
import com.github.nosan.embedded.cassandra.commons.function.IOSupplier;

/**
 * {@link HttpURLConnection} based implementation of {@link HttpClient}.
 *
 * @author Dmytro Nosan
 * @since 4.0.0
 */
public class JdkHttpClient implements HttpClient {

	private static final String COOKIE = "Cookie";

	private final Duration connectTimeout;

	private final Duration readTimeout;

	private final Proxy proxy;

	/**
	 * Creates {@link JdkHttpClient} with no timeouts and proxy.
	 */
	public JdkHttpClient() {
		this.readTimeout = null;
		this.connectTimeout = null;
		this.proxy = null;
	}

	/**
	 * Creates {@link JdkHttpClient} with no timeouts and provided proxy.
	 *
	 * @param proxy proxy to use
	 */
	public JdkHttpClient(Proxy proxy) {
		this.readTimeout = null;
		this.connectTimeout = null;
		this.proxy = proxy;
	}

	/**
	 * Creates {@link JdkHttpClient} with provided timeouts and no proxy.
	 *
	 * @param connectTimeout connection timeout
	 * @param readTimeout read timeout
	 */
	public JdkHttpClient(Duration connectTimeout, Duration readTimeout) {
		this.connectTimeout = connectTimeout;
		this.readTimeout = readTimeout;
		this.proxy = null;
	}

	/**
	 * Creates {@link JdkHttpClient} with provided timeouts and  proxy.
	 *
	 * @param connectTimeout connection timeout
	 * @param readTimeout read timeout
	 * @param proxy proxy to use
	 */
	public JdkHttpClient(Duration connectTimeout, Duration readTimeout, Proxy proxy) {
		this.connectTimeout = connectTimeout;
		this.readTimeout = readTimeout;
		this.proxy = proxy;
	}

	@Override
	public final HttpResponse send(HttpRequest httpRequest, IOSupplier bodySupplier)
			throws IOException {
		HttpURLConnection connection = open(httpRequest.getUri().toURL(), this.proxy);
		try {
			connect(connection, httpRequest, bodySupplier);
			return new JdkHttpResponse(connection);
		}
		catch (Exception ex) {
			try {
				connection.disconnect();
			}
			catch (Exception suppressed) {
				ex.addSuppressed(suppressed);
			}
			throw ex;
		}
	}

	protected HttpURLConnection open(URL url, Proxy proxy) throws IOException {
		URLConnection urlConnection = (proxy != null) ? url.openConnection(proxy) : url.openConnection();
		if (!(urlConnection instanceof HttpURLConnection)) {
			throw new IllegalStateException("HttpURLConnection required for [" + url + "] but got: " + urlConnection);
		}
		return (HttpURLConnection) urlConnection;
	}

	private void connect(HttpURLConnection connection, HttpRequest httpRequest,
			IOSupplier bodySupplier) throws IOException {
		String method = httpRequest.getMethod();
		if (this.connectTimeout != null) {
			connection.setConnectTimeout(Math.toIntExact(this.connectTimeout.toMillis()));
		}
		if (this.readTimeout != null) {
			connection.setReadTimeout(Math.toIntExact(this.readTimeout.toMillis()));
		}
		connection.setDoInput(true);
		connection.setInstanceFollowRedirects("GET".equals(method));
		connection.setDoOutput("POST".equals(method) || "PUT".equals(method) || "DELETE".equals(method));
		connection.setRequestMethod(method);

		HttpHeaders headers = httpRequest.getHeaders();
		headers.forEach((name, values) -> {
			if (COOKIE.equalsIgnoreCase(name)) {
				connection.setRequestProperty(name, (values != null) ? String.join("; ", values) : "");
			}
			else if (values != null) {
				for (String value : values) {
					connection.addRequestProperty(name, Objects.toString(value, ""));
				}
			}
			else {
				connection.addRequestProperty(name, "");
			}
		});

		if (bodySupplier != null && connection.getDoOutput()) {
			try (InputStream is = bodySupplier.get(); OutputStream os = connection.getOutputStream()) {
				StreamUtils.copy(is, os);
			}
		}

		connection.connect();
	}

	private static final class JdkHttpResponse implements HttpResponse {

		private final HttpURLConnection connection;

		private final HttpHeaders headers;

		JdkHttpResponse(HttpURLConnection connection) {
			this.connection = connection;
			this.headers = HttpHeaders.readOnly(connection.getHeaderFields());
		}

		@Override
		public URI getUri() {
			try {
				return this.connection.getURL().toURI();
			}
			catch (URISyntaxException ex) {
				throw new IllegalStateException(
						"URL '" + this.connection.getURL() + "' is not formatted strictly according to RFC2396"
								+ " and cannot be converted to a URI", ex);
			}
		}

		@Override
		public int getStatus() {
			try {
				return this.connection.getResponseCode();
			}
			catch (FileNotFoundException ex) {
				return 404;
			}
			catch (IOException ex) {
				return 500;
			}
		}

		@Override
		public InputStream getInputStream() throws IOException {
			if (getStatus() >= 400) {
				InputStream errorStream = this.connection.getErrorStream();
				if (errorStream != null) {
					return errorStream;
				}
			}
			return this.connection.getInputStream();
		}

		@Override
		public HttpHeaders getHeaders() {
			return this.headers;
		}

		@Override
		public void close() {
			this.connection.disconnect();
		}

		@Override
		public String toString() {
			return "JdkHttpResponse{" + "uri='" + getUri() + "', status='" + getStatusMessage() + "'}";
		}

		private String getStatusMessage() {
			try {
				String message = this.connection.getResponseMessage();
				if (StringUtils.hasText(message)) {
					return getStatus() + " " + message;
				}
				return Integer.toString(getStatus());
			}
			catch (Exception ex) {
				return Integer.toString(getStatus());
			}
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy