org.kohsuke.github.internal.GitHubConnectorHttpConnectorAdapter Maven / Gradle / Ivy
package org.kohsuke.github.internal;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.*;
import org.kohsuke.github.connector.GitHubConnector;
import org.kohsuke.github.connector.GitHubConnectorRequest;
import org.kohsuke.github.connector.GitHubConnectorResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Adapts an HttpConnector to be usable as GitHubConnector.
*
* For internal use only.
*
* @author Liam Newman
*/
public final class GitHubConnectorHttpConnectorAdapter implements GitHubConnector, HttpConnector {
/**
* Internal for testing.
*/
final HttpConnector httpConnector;
/**
* Constructor.
*
* @param httpConnector
* the HttpConnector to be adapted.
*/
public GitHubConnectorHttpConnectorAdapter(HttpConnector httpConnector) {
this.httpConnector = httpConnector;
}
/**
* Creates a GitHubConnector for an HttpConnector.
*
* If a well-known static HttpConnector is passed, a corresponding static GitHubConnector is returned.
*
* @param connector
* the HttpConnector to be adapted.
* @return a GitHubConnector that calls into the provided HttpConnector.
*/
@Nonnull
public static GitHubConnector adapt(@Nonnull HttpConnector connector) {
GitHubConnector gitHubConnector;
if (connector == HttpConnector.DEFAULT) {
gitHubConnector = GitHubConnector.DEFAULT;
} else if (connector == HttpConnector.OFFLINE) {
gitHubConnector = GitHubConnector.OFFLINE;
} else if (connector instanceof GitHubConnector) {
gitHubConnector = (GitHubConnector) connector;
} else {
gitHubConnector = new GitHubConnectorHttpConnectorAdapter(connector);
}
return gitHubConnector;
}
@Nonnull
public HttpURLConnection connect(URL url) throws IOException {
return this.httpConnector.connect(url);
}
@Nonnull
public GitHubConnectorResponse send(GitHubConnectorRequest request) throws IOException {
HttpURLConnection connection;
try {
connection = setupConnection(this, request);
} catch (IOException e) {
// An error in here should be wrapped to bypass http exception wrapping.
throw new GHIOException(e.getMessage(), e);
}
// HttpUrlConnection is nuts. This call opens the connection and gets a response.
// Putting this on its own line for ease of debugging if needed.
int statusCode = connection.getResponseCode();
Map> headers = connection.getHeaderFields();
return new HttpURLConnectionGitHubConnectorResponse(request, statusCode, headers, connection);
}
@Nonnull
private static HttpURLConnection setupConnection(@Nonnull HttpConnector connector,
@Nonnull GitHubConnectorRequest request) throws IOException {
HttpURLConnection connection = connector.connect(request.url());
setRequestMethod(request.method(), connection);
buildRequest(request, connection);
return connection;
}
/**
* Set up the request parameters or POST payload.
*/
private static void buildRequest(GitHubConnectorRequest request, HttpURLConnection connection) throws IOException {
for (Map.Entry> e : request.allHeaders().entrySet()) {
List v = e.getValue();
if (v != null)
connection.setRequestProperty(e.getKey(), String.join(", ", v));
}
if (request.hasBody()) {
connection.setDoOutput(true);
IOUtils.copyLarge(request.body(), connection.getOutputStream());
}
}
private static void setRequestMethod(String method, HttpURLConnection connection) throws IOException {
try {
connection.setRequestMethod(method);
} catch (ProtocolException e) {
// JDK only allows one of the fixed set of verbs. Try to override that
try {
Field $method = HttpURLConnection.class.getDeclaredField("method");
$method.setAccessible(true);
$method.set(connection, method);
} catch (Exception x) {
throw (IOException) new IOException("Failed to set the custom verb").initCause(x);
}
// sun.net.www.protocol.https.DelegatingHttpsURLConnection delegates to another HttpURLConnection
try {
Field $delegate = connection.getClass().getDeclaredField("delegate");
$delegate.setAccessible(true);
Object delegate = $delegate.get(connection);
if (delegate instanceof HttpURLConnection) {
HttpURLConnection nested = (HttpURLConnection) delegate;
setRequestMethod(method, nested);
}
} catch (NoSuchFieldException x) {
// no problem
} catch (IllegalAccessException x) {
throw (IOException) new IOException("Failed to set the custom verb").initCause(x);
}
}
if (!connection.getRequestMethod().equals(method))
throw new IllegalStateException("Failed to set the request method to " + method);
}
/**
* Initial response information supplied when a response is received but before the body is processed.
*
* Implementation specific to {@link HttpURLConnection}. For internal use only.
*/
public final static class HttpURLConnectionGitHubConnectorResponse
extends
GitHubConnectorResponse.ByteArrayResponse {
@Nonnull
private final HttpURLConnection connection;
HttpURLConnectionGitHubConnectorResponse(@Nonnull GitHubConnectorRequest request,
int statusCode,
@Nonnull Map> headers,
@Nonnull HttpURLConnection connection) {
super(request, statusCode, headers);
this.connection = connection;
}
@CheckForNull
@Override
protected InputStream rawBodyStream() throws IOException {
InputStream rawStream = connection.getErrorStream();
if (rawStream == null) {
rawStream = connection.getInputStream();
}
return rawStream;
}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" },
justification = "Internal implementation class. Should not be used externally.")
@Nonnull
@Override
@Deprecated
public HttpURLConnection toHttpURLConnection() {
return connection;
}
@Override
public void close() throws IOException {
super.close();
try {
IOUtils.closeQuietly(connection.getInputStream());
} catch (IOException e) {
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy