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

groovyx.net.http.JavaHttpBuilder Maven / Gradle / Ivy

/**
 * Copyright (C) 2017 HttpBuilder-NG Project
 *
 * 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 groovyx.net.http;

import groovyx.net.http.util.IoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;

import static groovyx.net.http.HttpBuilder.ResponseHandlerFunction.HANDLER_FUNCTION;

/**
 * `HttpBuilder` implementation based on the {@link HttpURLConnection} class.
 * 

* Generally, this class should not be used directly, the preferred method of instantiation is via the * `groovyx.net.http.HttpBuilder.configure(java.util.function.Function)` or * `groovyx.net.http.HttpBuilder.configure(java.util.function.Function, groovy.lang.Closure)` methods. */ public class JavaHttpBuilder extends HttpBuilder { private static final Logger log = LoggerFactory.getLogger(JavaHttpBuilder.class); private static final Logger contentLog = LoggerFactory.getLogger("groovy.net.http.JavaHttpBuilder.content"); private static final Logger headerLog = LoggerFactory.getLogger("groovy.net.http.JavaHttpBuilder.headers"); protected class Action { private final HttpURLConnection connection; private final ChainedHttpConfig requestConfig; private final URI theUri; boolean failed = false; public Action(final ChainedHttpConfig requestConfig, final String verb) throws IOException, URISyntaxException { this.requestConfig = requestConfig; final ChainedHttpConfig.ChainedRequest cr = requestConfig.getChainedRequest(); this.theUri = cr.getUri().toURI(); this.connection = (HttpURLConnection) theUri.toURL().openConnection(); this.connection.setRequestMethod(verb); if (cr.actualBody() != null) { this.connection.setDoOutput(true); } } private void addHeaders() throws URISyntaxException { final ChainedHttpConfig.ChainedRequest cr = requestConfig.getChainedRequest(); for (Map.Entry entry : cr.actualHeaders(new LinkedHashMap<>()).entrySet()) { connection.addRequestProperty(entry.getKey(), entry.getValue()); } final String contentType = cr.actualContentType(); if (contentType != null) { connection.addRequestProperty("Content-Type", contentType); } connection.addRequestProperty("Accept-Encoding", "gzip, deflate"); for (Map.Entry e : cookiesToAdd(clientConfig, cr).entrySet()) { connection.addRequestProperty(e.getKey(), e.getValue()); } if (headerLog.isDebugEnabled()) { connection.getRequestProperties().forEach((name, values) -> headerLog.debug("Request-Header: {} -> {}", name, values)); } } private PasswordAuthentication getAuthInfo() { final HttpConfig.Auth auth = requestConfig.getChainedRequest().actualAuth(); if (auth == null) { return null; } if (auth.getAuthType() == HttpConfig.AuthType.BASIC || auth.getAuthType() == HttpConfig.AuthType.DIGEST) { return new PasswordAuthentication(auth.getUser(), auth.getPassword().toCharArray()); } else { throw new UnsupportedOperationException("HttpURLConnection does not support " + auth.getAuthType() + " authentication"); } } public Object execute() throws Exception { return ThreadLocalAuth.with(getAuthInfo(), () -> { if (sslContext != null && connection instanceof HttpsURLConnection) { HttpsURLConnection https = (HttpsURLConnection) connection; if (hostnameVerifier != null) { https.setHostnameVerifier(hostnameVerifier); } https.setSSLSocketFactory(sslContext.getSocketFactory()); } final ChainedHttpConfig.ChainedRequest cr = requestConfig.getChainedRequest(); JavaToServer j2s = null; if (cr.actualBody() != null) { j2s = new JavaToServer(); requestConfig.findEncoder().accept(requestConfig, j2s); } if (log.isDebugEnabled()) { log.debug("Request-URI: {}", theUri); } addHeaders(); connection.connect(); if (j2s != null) { if (contentLog.isDebugEnabled()) { contentLog.debug("Request-Body: {}", j2s.content()); } j2s.transfer(); } final JavaFromServer fromServer = new JavaFromServer(theUri); if (contentLog.isDebugEnabled()) { contentLog.debug("Response-Body: {}", fromServer.content()); } if (headerLog.isDebugEnabled()) { fromServer.getHeaders().forEach(header -> headerLog.debug("Response-Header: {} -> {}", header.getKey(), header.getValue())); } return HANDLER_FUNCTION.apply(requestConfig, fromServer); }); } protected class JavaToServer implements ToServer { private BufferedInputStream inputStream; public void toServer(final InputStream inputStream) { this.inputStream = inputStream instanceof BufferedInputStream ? (BufferedInputStream) inputStream : new BufferedInputStream(inputStream); } void transfer() throws IOException { IoUtils.transfer(inputStream, connection.getOutputStream(), true); } public String content() { try { return IoUtils.copyAsString(inputStream); } catch (IOException ioe) { if (log.isWarnEnabled()) { log.warn("Unable to render request stream due to error (may not affect actual content)", ioe); } } catch (IllegalStateException ise) { if (log.isErrorEnabled()) { log.error("Unable to reset request stream - actual content may be corrupted (consider disabling content logging)", ise); } } return ""; } } protected class JavaFromServer implements FromServer { private final BufferedInputStream is; private final List> headers; private final URI uri; private final int statusCode; private final String message; public JavaFromServer(final URI originalUri) throws IOException { this.uri = originalUri; headers = populateHeaders(); addCookieStore(uri, headers); statusCode = connection.getResponseCode(); message = connection.getResponseMessage(); BufferedInputStream bis = buffered(correctInputStream()); is = (bis == null) ? null : handleEncoding(bis); } String content() { try { return IoUtils.copyAsString(is); } catch (IOException ioe) { log.warn("Unable to render response stream due to error (may not affect actual content)", ioe); } catch (IllegalStateException ise) { log.error("Unable to reset response stream - actual content may be corrupted (consider disabling content logging)", ise); } return ""; } private BufferedInputStream buffered(final InputStream is) throws IOException { if (is == null) { return null; } final BufferedInputStream bis = new BufferedInputStream(is); bis.mark(0); if (bis.read() == -1) { return null; } else { bis.reset(); return bis; } } private InputStream correctInputStream() throws IOException { if (getStatusCode() < 400) { return connection.getInputStream(); } else { return connection.getErrorStream(); } } private BufferedInputStream handleEncoding(final BufferedInputStream is) throws IOException { Header encodingHeader = Header.find(headers, "Content-Encoding"); if (encodingHeader != null) { if (encodingHeader.getValue().equals("gzip")) { return new BufferedInputStream(new GZIPInputStream(is)); } else if (encodingHeader.getValue().equals("deflate")) { return new BufferedInputStream(new InflaterInputStream(is)); } } return is; } private String clean(final String str) { if (str == null) { return null; } final String tmp = str.trim(); return "".equals(tmp) ? null : tmp; } private List> populateHeaders() { final List> ret = new ArrayList<>(); for (int i = 0; i < Integer.MAX_VALUE; ++i) { final String key = clean(connection.getHeaderFieldKey(i)); final String value = clean(connection.getHeaderField(i)); if (key == null && value == null) { break; } if (key != null && value != null) { ret.add(Header.keyValue(key.trim(), value.trim())); } } return Collections.unmodifiableList(ret); } public InputStream getInputStream() { return is; } public final int getStatusCode() { return statusCode; } public String getMessage() { return message; } public List> getHeaders() { return headers; } public boolean getHasBody() { return is != null; } public URI getUri() { return uri; } public void finish() { //do nothing, should auto cleanup } } } protected static class ThreadLocalAuth extends Authenticator { private static final ThreadLocal tlAuth = new ThreadLocal(); public PasswordAuthentication getPasswordAuthentication() { return tlAuth.get(); } public static final V with(final PasswordAuthentication pa, final Callable callable) throws Exception { tlAuth.set(pa); try { return callable.call(); } finally { tlAuth.set(null); } } } static { Authenticator.setDefault(new ThreadLocalAuth()); } final private ChainedHttpConfig config; final private Executor executor; final private SSLContext sslContext; final private HostnameVerifier hostnameVerifier; final private HttpObjectConfig.Client clientConfig; public JavaHttpBuilder(final HttpObjectConfig config) { super(config); this.config = new HttpConfigs.ThreadSafeHttpConfig(config.getChainedConfig()); this.executor = config.getExecution().getExecutor(); this.clientConfig = config.getClient(); this.hostnameVerifier = config.getExecution().getHostnameVerifier(); this.sslContext = config.getExecution().getSslContext(); } protected ChainedHttpConfig getObjectConfig() { return config; } private Object createAndExecute(final ChainedHttpConfig config, final String verb) { try { Action action = new Action(config, verb); return action.execute(); } catch (Exception e) { return handleException(config.getChainedResponse(), e); } } protected Object doGet(final ChainedHttpConfig requestConfig) { return createAndExecute(requestConfig, "GET"); } protected Object doHead(final ChainedHttpConfig requestConfig) { return createAndExecute(requestConfig, "HEAD"); } protected Object doPost(final ChainedHttpConfig requestConfig) { return createAndExecute(requestConfig, "POST"); } protected Object doPut(final ChainedHttpConfig requestConfig) { return createAndExecute(requestConfig, "PUT"); } protected Object doDelete(final ChainedHttpConfig requestConfig) { return createAndExecute(requestConfig, "DELETE"); } protected Object doPatch(final ChainedHttpConfig requestConfig) { // The Java HttpURLConnection class only allows standard HTTP/1.1 verbs and will // throw a ProtocolException if the user tries to specified PATCH as the HTTP method. // See https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html#setRequestMethod-java.lang.String- throw new UnsupportedOperationException("java.net.HttpURLConnection does not support the PATCH method. Use the Apache or OkHttp providers instead."); } public Executor getExecutor() { return executor; } public void close() { //do nothing, not needed } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy