Files
github-api/jacoco/org.kohsuke.github/GitHubHttpUrlConnectionClient.java.html
2021-03-25 18:58:40 -07:00

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> &gt; <a href="index.source.html" class="el_package">org.kohsuke.github</a> &gt; <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
* &lt;p&gt;
* 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}.
* &lt;/p&gt;
* &lt;p&gt;
* GitHubHttpUrlConnectionClient gets a new {@link HttpURLConnection} for each call to send.
* &lt;/p&gt;
*/
class GitHubHttpUrlConnectionClient extends GitHubClient {
GitHubHttpUrlConnectionClient(String apiUrl,
HttpConnector connector,
RateLimitHandler rateLimitHandler,
AbuseLimitHandler abuseLimitHandler,
GitHubRateLimitChecker rateLimitChecker,
Consumer&lt;GHMyself&gt; 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&lt;String, List&lt;String&gt;&gt; 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(&quot;Rate limit violation&quot;,</span>
<span class="fc" id="L73"> responseInfo.statusCode(),</span>
<span class="fc" id="L74"> responseInfo.headerField(&quot;Status&quot;),</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(&quot;Abuse limit violation&quot;,</span>
<span class="fc" id="L79"> responseInfo.statusCode(),</span>
<span class="fc" id="L80"> responseInfo.headerField(&quot;Status&quot;),</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&lt;String, List&lt;String&gt;&gt; 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(&quot;Authorization&quot;)) {</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(&quot;Authorization&quot;, 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&lt;String, String&gt; 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(&quot;Accept-Encoding&quot;, &quot;gzip&quot;);</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(&quot;Content-type&quot;,</span>
<span class="fc" id="L142"> defaultString(request.contentType(), &quot;application/x-www-form-urlencoded&quot;));</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(&quot;Content-type&quot;,</span>
<span class="fc" id="L150"> defaultString(request.contentType(), &quot;application/json&quot;));</span>
<span class="fc" id="L151"> Map&lt;String, Object&gt; json = new HashMap&lt;&gt;();</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(&quot;method&quot;);</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(&quot;Failed to set the custom verb&quot;).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(&quot;delegate&quot;);</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(&quot;Failed to set the custom verb&quot;).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(&quot;Failed to set the request method to &quot; + 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, &quot;Ignored exception get error message&quot;, e);</span>
} finally {
<span class="fc" id="L213"> IOUtils.closeQuietly(stream);</span>
}
<span class="fc" id="L215"> return result;</span>
}
/**
* Handles the &quot;Content-Encoding&quot; header.
*
* @param stream
* the stream to possibly wrap
*
*/
private InputStream wrapStream(InputStream stream) throws IOException {
<span class="fc" id="L226"> String encoding = headerField(&quot;Content-Encoding&quot;);</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(&quot;gzip&quot;))</span>
<span class="fc" id="L230"> return new GZIPInputStream(stream);</span>
<span class="nc" id="L232"> throw new UnsupportedOperationException(&quot;Unexpected Content-Encoding: &quot; + 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.6.202009150832</span></div></body></html>