mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-20 00:11:22 +00:00
244 lines
15 KiB
HTML
244 lines
15 KiB
HTML
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>GitHubHttpUrlConnectionClient.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">GitHub API for Java</a> > <a href="index.source.html" class="el_package">org.kohsuke.github</a> > <span class="el_source">GitHubHttpUrlConnectionClient.java</span></div><h1>GitHubHttpUrlConnectionClient.java</h1><pre class="source lang-java linenums">package org.kohsuke.github;
|
|
|
|
import org.apache.commons.io.IOUtils;
|
|
import org.kohsuke.github.authorization.AuthorizationProvider;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.lang.reflect.Field;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.ProtocolException;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.function.Consumer;
|
|
import java.util.logging.Logger;
|
|
import java.util.zip.GZIPInputStream;
|
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
import static java.util.logging.Level.*;
|
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
|
|
|
/**
|
|
* A GitHub API Client for HttpUrlConnection
|
|
* <p>
|
|
* A GitHubClient can be used to send requests and retrieve their responses. GitHubClient is thread-safe and can be used
|
|
* to send multiple requests. GitHubClient also track some GitHub API information such as {@link GHRateLimit}.
|
|
* </p>
|
|
* <p>
|
|
* GitHubHttpUrlConnectionClient gets a new {@link HttpURLConnection} for each call to send.
|
|
* </p>
|
|
*/
|
|
class GitHubHttpUrlConnectionClient extends GitHubClient {
|
|
|
|
GitHubHttpUrlConnectionClient(String apiUrl,
|
|
HttpConnector connector,
|
|
RateLimitHandler rateLimitHandler,
|
|
AbuseLimitHandler abuseLimitHandler,
|
|
GitHubRateLimitChecker rateLimitChecker,
|
|
Consumer<GHMyself> myselfConsumer,
|
|
AuthorizationProvider authorizationProvider) throws IOException {
|
|
<span class="fc" id="L43"> super(apiUrl,</span>
|
|
connector,
|
|
rateLimitHandler,
|
|
abuseLimitHandler,
|
|
rateLimitChecker,
|
|
myselfConsumer,
|
|
authorizationProvider);
|
|
<span class="fc" id="L50"> }</span>
|
|
|
|
@Nonnull
|
|
protected GitHubResponse.ResponseInfo getResponseInfo(GitHubRequest request) throws IOException {
|
|
HttpURLConnection connection;
|
|
try {
|
|
<span class="fc" id="L56"> connection = HttpURLConnectionResponseInfo.setupConnection(this, request);</span>
|
|
<span class="fc" id="L57"> } catch (IOException e) {</span>
|
|
// An error in here should be wrapped to bypass http exception wrapping.
|
|
<span class="fc" id="L59"> throw new GHIOException(e.getMessage(), e);</span>
|
|
<span class="fc" id="L60"> }</span>
|
|
|
|
// HttpUrlConnection is nuts. This call opens the connection and gets a response.
|
|
// Putting this on it's own line for ease of debugging if needed.
|
|
<span class="fc" id="L64"> int statusCode = connection.getResponseCode();</span>
|
|
<span class="fc" id="L65"> Map<String, List<String>> headers = connection.getHeaderFields();</span>
|
|
|
|
<span class="fc" id="L67"> return new HttpURLConnectionResponseInfo(request, statusCode, headers, connection);</span>
|
|
}
|
|
|
|
protected void handleLimitingErrors(@Nonnull GitHubResponse.ResponseInfo responseInfo) throws IOException {
|
|
<span class="fc bfc" id="L71" title="All 2 branches covered."> if (isRateLimitResponse(responseInfo)) {</span>
|
|
<span class="fc" id="L72"> GHIOException e = new HttpException("Rate limit violation",</span>
|
|
<span class="fc" id="L73"> responseInfo.statusCode(),</span>
|
|
<span class="fc" id="L74"> responseInfo.headerField("Status"),</span>
|
|
<span class="fc" id="L75"> responseInfo.url().toString()).withResponseHeaderFields(responseInfo.headers());</span>
|
|
<span class="fc" id="L76"> rateLimitHandler.onError(e, ((HttpURLConnectionResponseInfo) responseInfo).connection);</span>
|
|
<span class="pc bpc" id="L77" title="1 of 2 branches missed."> } else if (isAbuseLimitResponse(responseInfo)) {</span>
|
|
<span class="fc" id="L78"> GHIOException e = new HttpException("Abuse limit violation",</span>
|
|
<span class="fc" id="L79"> responseInfo.statusCode(),</span>
|
|
<span class="fc" id="L80"> responseInfo.headerField("Status"),</span>
|
|
<span class="fc" id="L81"> responseInfo.url().toString()).withResponseHeaderFields(responseInfo.headers());</span>
|
|
<span class="fc" id="L82"> abuseLimitHandler.onError(e, ((HttpURLConnectionResponseInfo) responseInfo).connection);</span>
|
|
}
|
|
<span class="fc" id="L84"> }</span>
|
|
|
|
/**
|
|
* Initial response information supplied to a {@link GitHubResponse.BodyHandler} when a response is initially
|
|
* received and before the body is processed.
|
|
*
|
|
* Implementation specific to {@link HttpURLConnection}.
|
|
*/
|
|
static class HttpURLConnectionResponseInfo extends GitHubResponse.ResponseInfo {
|
|
|
|
@Nonnull
|
|
private final HttpURLConnection connection;
|
|
|
|
HttpURLConnectionResponseInfo(@Nonnull GitHubRequest request,
|
|
int statusCode,
|
|
@Nonnull Map<String, List<String>> headers,
|
|
@Nonnull HttpURLConnection connection) {
|
|
<span class="fc" id="L101"> super(request, statusCode, headers);</span>
|
|
<span class="fc" id="L102"> this.connection = connection;</span>
|
|
<span class="fc" id="L103"> }</span>
|
|
|
|
@Nonnull
|
|
static HttpURLConnection setupConnection(@Nonnull GitHubClient client, @Nonnull GitHubRequest request)
|
|
throws IOException {
|
|
<span class="fc" id="L108"> HttpURLConnection connection = client.getConnector().connect(request.url());</span>
|
|
|
|
// if the authentication is needed but no credential is given, try it anyway (so that some calls
|
|
// that do work with anonymous access in the reduced form should still work.)
|
|
<span class="fc bfc" id="L112" title="All 2 branches covered."> if (!request.headers().containsKey("Authorization")) {</span>
|
|
<span class="fc" id="L113"> String authorization = client.getEncodedAuthorization();</span>
|
|
<span class="fc bfc" id="L114" title="All 2 branches covered."> if (authorization != null) {</span>
|
|
<span class="fc" id="L115"> connection.setRequestProperty("Authorization", client.getEncodedAuthorization());</span>
|
|
}
|
|
}
|
|
|
|
<span class="fc" id="L119"> setRequestMethod(request.method(), connection);</span>
|
|
<span class="fc" id="L120"> buildRequest(request, connection);</span>
|
|
|
|
<span class="fc" id="L122"> return connection;</span>
|
|
}
|
|
|
|
/**
|
|
* Set up the request parameters or POST payload.
|
|
*/
|
|
private static void buildRequest(GitHubRequest request, HttpURLConnection connection) throws IOException {
|
|
<span class="fc bfc" id="L129" title="All 2 branches covered."> for (Map.Entry<String, String> e : request.headers().entrySet()) {</span>
|
|
<span class="fc" id="L130"> String v = e.getValue();</span>
|
|
<span class="fc bfc" id="L131" title="All 2 branches covered."> if (v != null)</span>
|
|
<span class="fc" id="L132"> connection.setRequestProperty(e.getKey(), v);</span>
|
|
<span class="fc" id="L133"> }</span>
|
|
<span class="fc" id="L134"> connection.setRequestProperty("Accept-Encoding", "gzip");</span>
|
|
|
|
<span class="fc bfc" id="L136" title="All 2 branches covered."> if (request.inBody()) {</span>
|
|
<span class="fc" id="L137"> connection.setDoOutput(true);</span>
|
|
|
|
<span class="fc" id="L139"> try (InputStream body = request.body()) {</span>
|
|
<span class="fc bfc" id="L140" title="All 2 branches covered."> if (body != null) {</span>
|
|
<span class="fc" id="L141"> connection.setRequestProperty("Content-type",</span>
|
|
<span class="fc" id="L142"> defaultString(request.contentType(), "application/x-www-form-urlencoded"));</span>
|
|
<span class="fc" id="L143"> byte[] bytes = new byte[32768];</span>
|
|
int read;
|
|
<span class="fc bfc" id="L145" title="All 2 branches covered."> while ((read = body.read(bytes)) != -1) {</span>
|
|
<span class="fc" id="L146"> connection.getOutputStream().write(bytes, 0, read);</span>
|
|
}
|
|
<span class="fc" id="L148"> } else {</span>
|
|
<span class="fc" id="L149"> connection.setRequestProperty("Content-type",</span>
|
|
<span class="fc" id="L150"> defaultString(request.contentType(), "application/json"));</span>
|
|
<span class="fc" id="L151"> Map<String, Object> json = new HashMap<>();</span>
|
|
<span class="fc bfc" id="L152" title="All 2 branches covered."> for (GitHubRequest.Entry e : request.args()) {</span>
|
|
<span class="fc" id="L153"> json.put(e.key, e.value);</span>
|
|
<span class="fc" id="L154"> }</span>
|
|
<span class="fc" id="L155"> getMappingObjectWriter().writeValue(connection.getOutputStream(), json);</span>
|
|
}
|
|
}
|
|
}
|
|
<span class="fc" id="L159"> }</span>
|
|
|
|
private static void setRequestMethod(String method, HttpURLConnection connection) throws IOException {
|
|
try {
|
|
<span class="fc" id="L163"> connection.setRequestMethod(method);</span>
|
|
<span class="fc" id="L164"> } catch (ProtocolException e) {</span>
|
|
// JDK only allows one of the fixed set of verbs. Try to override that
|
|
try {
|
|
<span class="fc" id="L167"> Field $method = HttpURLConnection.class.getDeclaredField("method");</span>
|
|
<span class="fc" id="L168"> $method.setAccessible(true);</span>
|
|
<span class="fc" id="L169"> $method.set(connection, method);</span>
|
|
<span class="nc" id="L170"> } catch (Exception x) {</span>
|
|
<span class="nc" id="L171"> throw (IOException) new IOException("Failed to set the custom verb").initCause(x);</span>
|
|
<span class="fc" id="L172"> }</span>
|
|
// sun.net.www.protocol.https.DelegatingHttpsURLConnection delegates to another HttpURLConnection
|
|
try {
|
|
<span class="nc" id="L175"> Field $delegate = connection.getClass().getDeclaredField("delegate");</span>
|
|
<span class="nc" id="L176"> $delegate.setAccessible(true);</span>
|
|
<span class="nc" id="L177"> Object delegate = $delegate.get(connection);</span>
|
|
<span class="nc bnc" id="L178" title="All 2 branches missed."> if (delegate instanceof HttpURLConnection) {</span>
|
|
<span class="nc" id="L179"> HttpURLConnection nested = (HttpURLConnection) delegate;</span>
|
|
<span class="nc" id="L180"> setRequestMethod(method, nested);</span>
|
|
}
|
|
<span class="fc" id="L182"> } catch (NoSuchFieldException x) {</span>
|
|
// no problem
|
|
<span class="nc" id="L184"> } catch (IllegalAccessException x) {</span>
|
|
<span class="nc" id="L185"> throw (IOException) new IOException("Failed to set the custom verb").initCause(x);</span>
|
|
<span class="pc" id="L186"> }</span>
|
|
<span class="fc" id="L187"> }</span>
|
|
<span class="pc bpc" id="L188" title="1 of 2 branches missed."> if (!connection.getRequestMethod().equals(method))</span>
|
|
<span class="nc" id="L189"> throw new IllegalStateException("Failed to set the request method to " + method);</span>
|
|
<span class="fc" id="L190"> }</span>
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
InputStream bodyStream() throws IOException {
|
|
<span class="fc" id="L196"> return wrapStream(connection.getInputStream());</span>
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
String errorMessage() {
|
|
<span class="fc" id="L203"> String result = null;</span>
|
|
<span class="fc" id="L204"> InputStream stream = null;</span>
|
|
try {
|
|
<span class="fc" id="L206"> stream = connection.getErrorStream();</span>
|
|
<span class="fc bfc" id="L207" title="All 2 branches covered."> if (stream != null) {</span>
|
|
<span class="fc" id="L208"> result = IOUtils.toString(wrapStream(stream), StandardCharsets.UTF_8);</span>
|
|
}
|
|
<span class="nc" id="L210"> } catch (Exception e) {</span>
|
|
<span class="nc" id="L211"> LOGGER.log(FINER, "Ignored exception get error message", e);</span>
|
|
} finally {
|
|
<span class="fc" id="L213"> IOUtils.closeQuietly(stream);</span>
|
|
}
|
|
<span class="fc" id="L215"> return result;</span>
|
|
}
|
|
|
|
/**
|
|
* Handles the "Content-Encoding" header.
|
|
*
|
|
* @param stream
|
|
* the stream to possibly wrap
|
|
*
|
|
*/
|
|
private InputStream wrapStream(InputStream stream) throws IOException {
|
|
<span class="fc" id="L226"> String encoding = headerField("Content-Encoding");</span>
|
|
<span class="pc bpc" id="L227" title="1 of 4 branches missed."> if (encoding == null || stream == null)</span>
|
|
<span class="fc" id="L228"> return stream;</span>
|
|
<span class="pc bpc" id="L229" title="1 of 2 branches missed."> if (encoding.equals("gzip"))</span>
|
|
<span class="fc" id="L230"> return new GZIPInputStream(stream);</span>
|
|
|
|
<span class="nc" id="L232"> throw new UnsupportedOperationException("Unexpected Content-Encoding: " + encoding);</span>
|
|
}
|
|
|
|
<span class="fc" id="L235"> private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());</span>
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
<span class="fc" id="L239"> IOUtils.closeQuietly(connection.getInputStream());</span>
|
|
<span class="fc" id="L240"> }</span>
|
|
}
|
|
|
|
}
|
|
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.7.202105040129</span></div></body></html> |