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

nl.topicus.jdbc.shaded.com.google.api.client.http.HttpRequest Maven / Gradle / Ivy

/*
 * Copyright (c) 2010 Google Inc.
 *
 * 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 nl.topicus.jdbc.shaded.com.google.api.client.http;

import nl.topicus.jdbc.shaded.com.google.api.client.util.Beta;
import nl.topicus.jdbc.shaded.com.google.api.client.util.IOUtils;
import nl.topicus.jdbc.shaded.com.google.api.client.util.LoggingStreamingContent;
import nl.topicus.jdbc.shaded.com.google.api.client.util.ObjectParser;
import nl.topicus.jdbc.shaded.com.google.api.client.util.Preconditions;
import nl.topicus.jdbc.shaded.com.google.api.client.util.Sleeper;
import nl.topicus.jdbc.shaded.com.google.api.client.util.StreamingContent;
import nl.topicus.jdbc.shaded.com.google.api.client.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * HTTP request.
 *
 * 

* Implementation is not thread-safe. *

* * @since 1.0 * @author Yaniv Inbar */ public final class HttpRequest { /** * Current version of the Google API Client Library for Java. * * @since 1.8 */ public static final String VERSION = "1.23.0"; /** * User agent suffix for all requests. * *

* Includes a {@code "(gzip)"} suffix in case the server -- as Google's servers may do -- checks * the {@code User-Agent} header to try to detect if the client accepts gzip-encoded responses. *

* * @since 1.4 */ public static final String USER_AGENT_SUFFIX = "Google-HTTP-Java-Client/" + VERSION + " (gzip)"; /** * The default number of retries that will be allowed to execute before the request will be * terminated. * * @see #getNumberOfRetries * @since 1.22 */ public static final int DEFAULT_NUMBER_OF_RETRIES = 10; /** * HTTP request execute interceptor to intercept the start of {@link #execute()} (before executing * the HTTP request) or {@code null} for none. */ private HttpExecuteInterceptor executeInterceptor; /** HTTP request headers. */ private HttpHeaders headers = new HttpHeaders(); /** * HTTP response headers. * *

* For example, this can be used if you want to use a subclass of {@link HttpHeaders} called * MyHeaders to process the response: *

* *
  static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) {
    MyHeaders responseHeaders = new MyHeaders();
    request.responseHeaders = responseHeaders;
    HttpResponse response = request.execute();
    return responseHeaders.someCustomHeader;
  }
   * 
*/ private HttpHeaders responseHeaders = new HttpHeaders(); /** * The number of retries that will be allowed to execute before the request will be terminated or * {@code 0} to not retry requests. Retries occur as a result of either * {@link HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles * abnormal HTTP response or the I/O exception. */ private int numRetries = DEFAULT_NUMBER_OF_RETRIES; /** * Determines the limit to the content size that will be logged during {@link #execute()}. * *

* Content will only be logged if {@link #isLoggingEnabled} is {@code true}. *

* *

* If the content size is greater than this limit then it will not be logged. *

* *

* Can be set to {@code 0} to disable content logging. This is useful for example if content has * sensitive data such as authentication information. *

* *

* Defaults to 16KB. *

*/ private int contentLoggingLimit = 0x4000; /** Determines whether logging should be enabled for this request. Defaults to {@code true}. */ private boolean loggingEnabled = true; /** Determines whether logging in form of curl commands should be enabled for this request. */ private boolean curlLoggingEnabled = true; /** HTTP request content or {@code null} for none. */ private HttpContent content; /** HTTP transport. */ private final HttpTransport transport; /** HTTP request method or {@code null} for none. */ private String requestMethod; /** HTTP request URL. */ private GenericUrl url; /** Timeout in milliseconds to establish a connection or {@code 0} for an infinite timeout. */ private int connectTimeout = 20 * 1000; /** * Timeout in milliseconds to read data from an established connection or {@code 0} for an * infinite timeout. */ private int readTimeout = 20 * 1000; /** HTTP unsuccessful (non-2XX) response handler or {@code null} for none. */ private HttpUnsuccessfulResponseHandler unsuccessfulResponseHandler; /** HTTP I/O exception handler or {@code null} for none. */ @Beta private HttpIOExceptionHandler ioExceptionHandler; /** HTTP response interceptor or {@code null} for none. */ private HttpResponseInterceptor responseInterceptor; /** Parser used to parse responses. */ private ObjectParser objectParser; /** HTTP content encoding or {@code null} for none. */ private HttpEncoding encoding; /** * The {@link BackOffPolicy} to use between retry attempts or {@code null} for none. */ @Deprecated @Beta private BackOffPolicy backOffPolicy; /** Whether to automatically follow redirects ({@code true} by default). */ private boolean followRedirects = true; /** * Whether to throw an exception at the end of {@link #execute()} on an HTTP error code (non-2XX) * after all retries and response handlers have been exhausted ({@code true} by default). */ private boolean throwExceptionOnExecuteError = true; /** * Whether to retry the request if an {@link IOException} is encountered in * {@link LowLevelHttpRequest#execute()}. */ @Deprecated @Beta private boolean retryOnExecuteIOException = false; /** * Whether to not add the suffix {@link #USER_AGENT_SUFFIX} to the User-Agent header. * *

* It is {@code false} by default. *

*/ private boolean suppressUserAgentSuffix; /** Sleeper. */ private Sleeper sleeper = Sleeper.DEFAULT; /** * @param transport HTTP transport * @param requestMethod HTTP request method or {@code null} for none */ HttpRequest(HttpTransport transport, String requestMethod) { this.transport = transport; setRequestMethod(requestMethod); } /** * Returns the HTTP transport. * * @since 1.5 */ public HttpTransport getTransport() { return transport; } /** * Returns the HTTP request method or {@code null} for none. * * @since 1.12 */ public String getRequestMethod() { return requestMethod; } /** * Sets the HTTP request method or {@code null} for none. * * @since 1.12 */ public HttpRequest setRequestMethod(String requestMethod) { Preconditions.checkArgument(requestMethod == null || HttpMediaType.matchesToken(requestMethod)); this.requestMethod = requestMethod; return this; } /** * Returns the HTTP request URL. * * @since 1.5 */ public GenericUrl getUrl() { return url; } /** * Sets the HTTP request URL. * * @since 1.5 */ public HttpRequest setUrl(GenericUrl url) { this.url = Preconditions.checkNotNull(url); return this; } /** * Returns the HTTP request content or {@code null} for none. * * @since 1.5 */ public HttpContent getContent() { return content; } /** * Sets the HTTP request content or {@code null} for none. * * @since 1.5 */ public HttpRequest setContent(HttpContent content) { this.content = content; return this; } /** * Returns the HTTP content encoding or {@code null} for none. * * @since 1.14 */ public HttpEncoding getEncoding() { return encoding; } /** * Sets the HTTP content encoding or {@code null} for none. * * @since 1.14 */ public HttpRequest setEncoding(HttpEncoding encoding) { this.encoding = encoding; return this; } /** * {@link Beta}
* Returns the {@link BackOffPolicy} to use between retry attempts or {@code null} for none. * * @since 1.7 * @deprecated (scheduled to be removed in 1.18). * {@link #setUnsuccessfulResponseHandler(HttpUnsuccessfulResponseHandler)} with a new * {@link HttpBackOffUnsuccessfulResponseHandler} instead. */ @Deprecated @Beta public BackOffPolicy getBackOffPolicy() { return backOffPolicy; } /** * {@link Beta}
* Sets the {@link BackOffPolicy} to use between retry attempts or {@code null} for none. * * @since 1.7 * @deprecated (scheduled to be removed in 1.18). Use * {@link #setUnsuccessfulResponseHandler(HttpUnsuccessfulResponseHandler)} with a new * {@link HttpBackOffUnsuccessfulResponseHandler} instead. */ @Deprecated @Beta public HttpRequest setBackOffPolicy(BackOffPolicy backOffPolicy) { this.backOffPolicy = backOffPolicy; return this; } /** * Returns the limit to the content size that will be logged during {@link #execute()}. * *

* If the content size is greater than this limit then it will not be logged. *

* *

* Content will only be logged if {@link #isLoggingEnabled} is {@code true}. *

* *

* Can be set to {@code 0} to disable content logging. This is useful for example if content has * sensitive data such as authentication information. *

* *

* Defaults to 16KB. *

* * @since 1.7 */ public int getContentLoggingLimit() { return contentLoggingLimit; } /** * Set the limit to the content size that will be logged during {@link #execute()}. * *

* If the content size is greater than this limit then it will not be logged. *

* *

* Content will only be logged if {@link #isLoggingEnabled} is {@code true}. *

* *

* Can be set to {@code 0} to disable content logging. This is useful for example if content has * sensitive data such as authentication information. *

* *

* Defaults to 16KB. *

* * @since 1.7 */ public HttpRequest setContentLoggingLimit(int contentLoggingLimit) { Preconditions.checkArgument( contentLoggingLimit >= 0, "The content logging limit must be non-negative."); this.contentLoggingLimit = contentLoggingLimit; return this; } /** * Returns whether logging should be enabled for this request. * *

* Defaults to {@code true}. *

* * @since 1.9 */ public boolean isLoggingEnabled() { return loggingEnabled; } /** * Sets whether logging should be enabled for this request. * *

* Defaults to {@code true}. *

* * @since 1.9 */ public HttpRequest setLoggingEnabled(boolean loggingEnabled) { this.loggingEnabled = loggingEnabled; return this; } /** * Returns whether logging in form of curl commands is enabled for this request. * * @since 1.11 */ public boolean isCurlLoggingEnabled() { return curlLoggingEnabled; } /** * Sets whether logging in form of curl commands should be enabled for this request. * *

* Defaults to {@code true}. *

* * @since 1.11 */ public HttpRequest setCurlLoggingEnabled(boolean curlLoggingEnabled) { this.curlLoggingEnabled = curlLoggingEnabled; return this; } /** * Returns the timeout in milliseconds to establish a connection or {@code 0} for an infinite * timeout. * * @since 1.5 */ public int getConnectTimeout() { return connectTimeout; } /** * Sets the timeout in milliseconds to establish a connection or {@code 0} for an infinite * timeout. * *

* By default it is 20000 (20 seconds). *

* * @since 1.5 */ public HttpRequest setConnectTimeout(int connectTimeout) { Preconditions.checkArgument(connectTimeout >= 0); this.connectTimeout = connectTimeout; return this; } /** * Returns the timeout in milliseconds to read data from an established connection or {@code 0} * for an infinite timeout. * *

* By default it is 20000 (20 seconds). *

* * @since 1.5 */ public int getReadTimeout() { return readTimeout; } /** * Sets the timeout in milliseconds to read data from an established connection or {@code 0} for * an infinite timeout. * * @since 1.5 */ public HttpRequest setReadTimeout(int readTimeout) { Preconditions.checkArgument(readTimeout >= 0); this.readTimeout = readTimeout; return this; } /** * Returns the HTTP request headers. * * @since 1.5 */ public HttpHeaders getHeaders() { return headers; } /** * Sets the HTTP request headers. * *

* By default, this is a new unmodified instance of {@link HttpHeaders}. *

* * @since 1.5 */ public HttpRequest setHeaders(HttpHeaders headers) { this.headers = Preconditions.checkNotNull(headers); return this; } /** * Returns the HTTP response headers. * * @since 1.5 */ public HttpHeaders getResponseHeaders() { return responseHeaders; } /** * Sets the HTTP response headers. * *

* By default, this is a new unmodified instance of {@link HttpHeaders}. *

* *

* For example, this can be used if you want to use a subclass of {@link HttpHeaders} called * MyHeaders to process the response: *

* *
  static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) {
    MyHeaders responseHeaders = new MyHeaders();
    request.responseHeaders = responseHeaders;
    HttpResponse response = request.execute();
    return responseHeaders.someCustomHeader;
  }
   * 
* * @since 1.5 */ public HttpRequest setResponseHeaders(HttpHeaders responseHeaders) { this.responseHeaders = Preconditions.checkNotNull(responseHeaders); return this; } /** * Returns the HTTP request execute interceptor to intercept the start of {@link #execute()} * (before executing the HTTP request) or {@code null} for none. * * @since 1.5 */ public HttpExecuteInterceptor getInterceptor() { return executeInterceptor; } /** * Sets the HTTP request execute interceptor to intercept the start of {@link #execute()} (before * executing the HTTP request) or {@code null} for none. * * @since 1.5 */ public HttpRequest setInterceptor(HttpExecuteInterceptor interceptor) { this.executeInterceptor = interceptor; return this; } /** * Returns the HTTP unsuccessful (non-2XX) response handler or {@code null} for none. * * @since 1.5 */ public HttpUnsuccessfulResponseHandler getUnsuccessfulResponseHandler() { return unsuccessfulResponseHandler; } /** * Sets the HTTP unsuccessful (non-2XX) response handler or {@code null} for none. * * @since 1.5 */ public HttpRequest setUnsuccessfulResponseHandler( HttpUnsuccessfulResponseHandler unsuccessfulResponseHandler) { this.unsuccessfulResponseHandler = unsuccessfulResponseHandler; return this; } /** * {@link Beta}
* Returns the HTTP I/O exception handler or {@code null} for none. * * @since 1.15 */ @Beta public HttpIOExceptionHandler getIOExceptionHandler() { return ioExceptionHandler; } /** * {@link Beta}
* Sets the HTTP I/O exception handler or {@code null} for none. * * @since 1.15 */ @Beta public HttpRequest setIOExceptionHandler(HttpIOExceptionHandler ioExceptionHandler) { this.ioExceptionHandler = ioExceptionHandler; return this; } /** * Returns the HTTP response interceptor or {@code null} for none. * * @since 1.13 */ public HttpResponseInterceptor getResponseInterceptor() { return responseInterceptor; } /** * Sets the HTTP response interceptor or {@code null} for none. * * @since 1.13 */ public HttpRequest setResponseInterceptor(HttpResponseInterceptor responseInterceptor) { this.responseInterceptor = responseInterceptor; return this; } /** * Returns the number of retries that will be allowed to execute before the request will be * terminated or {@code 0} to not retry requests. Retries occur as a result of either * {@link HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles * abnormal HTTP response or the I/O exception. * * * @since 1.5 */ public int getNumberOfRetries() { return numRetries; } /** * Sets the number of retries that will be allowed to execute before the request will be * terminated or {@code 0} to not retry requests. Retries occur as a result of either * {@link HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles * abnormal HTTP response or the I/O exception. * *

* The default value is {@link #DEFAULT_NUMBER_OF_RETRIES}. *

* * @since 1.5 */ public HttpRequest setNumberOfRetries(int numRetries) { Preconditions.checkArgument(numRetries >= 0); this.numRetries = numRetries; return this; } /** * Sets the {@link ObjectParser} used to parse the response to this request or {@code null} for * none. * *

* This parser will be preferred over any registered HttpParser. *

* * @since 1.10 */ public HttpRequest setParser(ObjectParser parser) { this.objectParser = parser; return this; } /** * Returns the {@link ObjectParser} used to parse the response or {@code null} for none. * * @since 1.10 */ public final ObjectParser getParser() { return objectParser; } /** * Returns whether to follow redirects automatically. * * @since 1.6 */ public boolean getFollowRedirects() { return followRedirects; } /** * Sets whether to follow redirects automatically. * *

* The default value is {@code true}. *

* * @since 1.6 */ public HttpRequest setFollowRedirects(boolean followRedirects) { this.followRedirects = followRedirects; return this; } /** * Returns whether to throw an exception at the end of {@link #execute()} on an HTTP error code * (non-2XX) after all retries and response handlers have been exhausted. * * @since 1.7 */ public boolean getThrowExceptionOnExecuteError() { return throwExceptionOnExecuteError; } /** * Sets whether to throw an exception at the end of {@link #execute()} on a HTTP error code * (non-2XX) after all retries and response handlers have been exhausted. * *

* The default value is {@code true}. *

* * @since 1.7 */ public HttpRequest setThrowExceptionOnExecuteError(boolean throwExceptionOnExecuteError) { this.throwExceptionOnExecuteError = throwExceptionOnExecuteError; return this; } /** * {@link Beta}
* Returns whether to retry the request if an {@link IOException} is encountered in * {@link LowLevelHttpRequest#execute()}. * * @since 1.9 * @deprecated (scheduled to be removed in 1.18) Use * {@link #setIOExceptionHandler(HttpIOExceptionHandler)} instead. */ @Deprecated @Beta public boolean getRetryOnExecuteIOException() { return retryOnExecuteIOException; } /** * {@link Beta}
* Sets whether to retry the request if an {@link IOException} is encountered in * {@link LowLevelHttpRequest#execute()}. * *

* The default value is {@code false}. *

* * @since 1.9 * @deprecated (scheduled to be removed in 1.18) Use * {@link #setIOExceptionHandler(HttpIOExceptionHandler)} instead. */ @Deprecated @Beta public HttpRequest setRetryOnExecuteIOException(boolean retryOnExecuteIOException) { this.retryOnExecuteIOException = retryOnExecuteIOException; return this; } /** * Returns whether to not add the suffix {@link #USER_AGENT_SUFFIX} to the User-Agent header. * * @since 1.11 */ public boolean getSuppressUserAgentSuffix() { return suppressUserAgentSuffix; } /** * Sets whether to not add the suffix {@link #USER_AGENT_SUFFIX} to the User-Agent header. * *

* The default value is {@code false}. *

* * @since 1.11 */ public HttpRequest setSuppressUserAgentSuffix(boolean suppressUserAgentSuffix) { this.suppressUserAgentSuffix = suppressUserAgentSuffix; return this; } /** * Execute the HTTP request and returns the HTTP response. * *

* Note that regardless of the returned status code, the HTTP response content has not been parsed * yet, and must be parsed by the calling code. *

* *

* Note that when calling to this method twice or more, the state of this HTTP request object * isn't cleared, so the request will continue where it was left. For example, the state of the * {@link HttpUnsuccessfulResponseHandler} attached to this HTTP request will remain the same as * it was left after last execute. *

* *

* Almost all details of the request and response are logged if {@link Level#CONFIG} is loggable. * The only exception is the value of the {@code Authorization} header which is only logged if * {@link Level#ALL} is loggable. *

* *

* Callers should call {@link HttpResponse#disconnect} when the returned HTTP response object is * no longer needed. However, {@link HttpResponse#disconnect} does not have to be called if the * response stream is properly closed. Example usage: *

* *
     HttpResponse response = request.execute();
     try {
       // process the HTTP response object
     } finally {
       response.disconnect();
     }
   * 
* * @return HTTP response for an HTTP success response (or HTTP error response if * {@link #getThrowExceptionOnExecuteError()} is {@code false}) * @throws HttpResponseException for an HTTP error response (only if * {@link #getThrowExceptionOnExecuteError()} is {@code true}) * @see HttpResponse#isSuccessStatusCode() */ @SuppressWarnings("deprecation") public HttpResponse execute() throws IOException { boolean retryRequest = false; Preconditions.checkArgument(numRetries >= 0); int retriesRemaining = numRetries; if (backOffPolicy != null) { // Reset the BackOffPolicy at the start of each execute. backOffPolicy.reset(); } HttpResponse response = null; IOException executeException; Preconditions.checkNotNull(requestMethod); Preconditions.checkNotNull(url); do { // Cleanup any unneeded response from a previous iteration if (response != null) { response.ignore(); } response = null; executeException = null; // run the interceptor if (executeInterceptor != null) { executeInterceptor.intercept(this); } // build low-level HTTP request String urlString = url.build(); LowLevelHttpRequest lowLevelHttpRequest = transport.buildRequest(requestMethod, urlString); Logger logger = HttpTransport.LOGGER; boolean loggable = loggingEnabled && logger.isLoggable(Level.CONFIG); StringBuilder logbuf = null; StringBuilder curlbuf = null; // log method and URL if (loggable) { logbuf = new StringBuilder(); logbuf.append("-------------- REQUEST --------------").append(StringUtils.LINE_SEPARATOR); logbuf.append(requestMethod) .append(' ').append(urlString).append(StringUtils.LINE_SEPARATOR); // setup curl logging if (curlLoggingEnabled) { curlbuf = new StringBuilder("curl -v --compressed"); if (!requestMethod.equals(HttpMethods.GET)) { curlbuf.append(" -X ").append(requestMethod); } } } // add to user agent String originalUserAgent = headers.getUserAgent(); if (!suppressUserAgentSuffix) { if (originalUserAgent == null) { headers.setUserAgent(USER_AGENT_SUFFIX); } else { headers.setUserAgent(originalUserAgent + " " + USER_AGENT_SUFFIX); } } // headers HttpHeaders.serializeHeaders(headers, logbuf, curlbuf, logger, lowLevelHttpRequest); if (!suppressUserAgentSuffix) { // set the original user agent back so that retries do not keep appending to it headers.setUserAgent(originalUserAgent); } // content StreamingContent streamingContent = content; final boolean contentRetrySupported = streamingContent == null || content.retrySupported(); if (streamingContent != null) { final String contentEncoding; final long contentLength; final String contentType = content.getType(); // log content if (loggable) { streamingContent = new LoggingStreamingContent( streamingContent, HttpTransport.LOGGER, Level.CONFIG, contentLoggingLimit); } // encoding if (encoding == null) { contentEncoding = null; contentLength = content.getLength(); } else { contentEncoding = encoding.getName(); streamingContent = new HttpEncodingStreamingContent(streamingContent, encoding); contentLength = contentRetrySupported ? IOUtils.computeLength(streamingContent) : -1; } // append content headers to log buffer if (loggable) { if (contentType != null) { String header = "Content-Type: " + contentType; logbuf.append(header).append(StringUtils.LINE_SEPARATOR); if (curlbuf != null) { curlbuf.append(" -H '" + header + "'"); } } if (contentEncoding != null) { String header = "Content-Encoding: " + contentEncoding; logbuf.append(header).append(StringUtils.LINE_SEPARATOR); if (curlbuf != null) { curlbuf.append(" -H '" + header + "'"); } } if (contentLength >= 0) { String header = "Content-Length: " + contentLength; logbuf.append(header).append(StringUtils.LINE_SEPARATOR); // do not log @ curl as the user will most likely manipulate the content } } if (curlbuf != null) { curlbuf.append(" -d '@-'"); } // send content information to low-level HTTP request lowLevelHttpRequest.setContentType(contentType); lowLevelHttpRequest.setContentEncoding(contentEncoding); lowLevelHttpRequest.setContentLength(contentLength); lowLevelHttpRequest.setStreamingContent(streamingContent); } // log from buffer if (loggable) { logger.config(logbuf.toString()); if (curlbuf != null) { curlbuf.append(" -- '"); curlbuf.append(urlString.replaceAll("\'", "'\"'\"'")); curlbuf.append("'"); if (streamingContent != null) { curlbuf.append(" << $$$"); } logger.config(curlbuf.toString()); } } // We need to make sure our content type can support retry // null content is inherently able to be retried retryRequest = contentRetrySupported && retriesRemaining > 0; // execute lowLevelHttpRequest.setTimeout(connectTimeout, readTimeout); try { LowLevelHttpResponse lowLevelHttpResponse = lowLevelHttpRequest.execute(); // Flag used to indicate if an exception is thrown before the response is constructed. boolean responseConstructed = false; try { response = new HttpResponse(this, lowLevelHttpResponse); responseConstructed = true; } finally { if (!responseConstructed) { InputStream lowLevelContent = lowLevelHttpResponse.getContent(); if (lowLevelContent != null) { lowLevelContent.close(); } } } } catch (IOException e) { if (!retryOnExecuteIOException && (ioExceptionHandler == null || !ioExceptionHandler.handleIOException(this, retryRequest))) { throw e; } // Save the exception in case the retries do not work and we need to re-throw it later. executeException = e; logger.log(Level.WARNING, "exception thrown while executing request", e); } // Flag used to indicate if an exception is thrown before the response has completed // processing. boolean responseProcessed = false; try { if (response != null && !response.isSuccessStatusCode()) { boolean errorHandled = false; if (unsuccessfulResponseHandler != null) { // Even if we don't have the potential to retry, we might want to run the // handler to fix conditions (like expired tokens) that might cause us // trouble on our next request errorHandled = unsuccessfulResponseHandler.handleResponse(this, response, retryRequest); } if (!errorHandled) { if (handleRedirect(response.getStatusCode(), response.getHeaders())) { // The unsuccessful request's error could not be handled and it is a redirect request. errorHandled = true; } else if (retryRequest && backOffPolicy != null && backOffPolicy.isBackOffRequired(response.getStatusCode())) { // The unsuccessful request's error could not be handled and should be backed off // before retrying long backOffTime = backOffPolicy.getNextBackOffMillis(); if (backOffTime != BackOffPolicy.STOP) { try { sleeper.sleep(backOffTime); } catch (InterruptedException exception) { // ignore } errorHandled = true; } } } // A retry is required if the error was successfully handled or if it is a redirect // request or if the back off policy determined a retry is necessary. retryRequest &= errorHandled; // need to close the response stream before retrying a request if (retryRequest) { response.ignore(); } } else { // Retry is not required for a successful status code unless the response is null. retryRequest &= (response == null); } // Once there are no more retries remaining, this will be -1 // Count redirects as retries, we want a finite limit of redirects. retriesRemaining--; responseProcessed = true; } finally { if (response != null && !responseProcessed) { response.disconnect(); } } } while (retryRequest); if (response == null) { // Retries did not help resolve the execute exception, re-throw it. throw executeException; } // response interceptor if (responseInterceptor != null) { responseInterceptor.interceptResponse(response); } // throw an exception if unsuccessful response if (throwExceptionOnExecuteError && !response.isSuccessStatusCode()) { try { throw new HttpResponseException(response); } finally { response.disconnect(); } } return response; } /** * {@link Beta}
* Executes this request asynchronously in a single separate thread using the supplied executor. * * @param executor executor to run the asynchronous request * @return future for accessing the HTTP response * @since 1.13 */ @Beta public Future executeAsync(Executor executor) { FutureTask future = new FutureTask(new Callable() { public HttpResponse call() throws Exception { return execute(); } }); executor.execute(future); return future; } /** * {@link Beta}
* Executes this request asynchronously using {@link #executeAsync(Executor)} in a single separate * thread using {@link Executors#newSingleThreadExecutor()}. * * @return A future for accessing the results of the asynchronous request. * @since 1.13 */ @Beta public Future executeAsync() { return executeAsync(Executors.newSingleThreadExecutor()); } /** * Sets up this request object to handle the necessary redirect if redirects are turned on, it is * a redirect status code and the header has a location. * *

* When the status code is {@code 303} the method on the request is changed to a GET as per the * RFC2616 specification. On a redirect, it also removes the {@code "Authorization"} and all * {@code "If-*"} request headers. *

* *

* Upgrade warning: When handling a status code of 303, {@link #handleRedirect(int, HttpHeaders)} * now correctly removes any content from the body of the new request, as GET requests should not * have content. It did not do this in prior version 1.16. *

* * @return whether the redirect was successful * @since 1.11 */ public boolean handleRedirect(int statusCode, HttpHeaders responseHeaders) { String redirectLocation = responseHeaders.getLocation(); if (getFollowRedirects() && HttpStatusCodes.isRedirect(statusCode) && redirectLocation != null) { // resolve the redirect location relative to the current location setUrl(new GenericUrl(url.toURL(redirectLocation))); // on 303 change method to GET if (statusCode == HttpStatusCodes.STATUS_CODE_SEE_OTHER) { setRequestMethod(HttpMethods.GET); // GET requests do not support non-zero content length setContent(null); } // remove Authorization and If-* headers headers.setAuthorization((String) null); headers.setIfMatch((String) null); headers.setIfNoneMatch((String) null); headers.setIfModifiedSince((String) null); headers.setIfUnmodifiedSince((String) null); headers.setIfRange((String) null); return true; } return false; } /** * Returns the sleeper. * * @since 1.15 */ public Sleeper getSleeper() { return sleeper; } /** * Sets the sleeper. The default value is {@link Sleeper#DEFAULT}. * * @since 1.15 */ public HttpRequest setSleeper(Sleeper sleeper) { this.sleeper = Preconditions.checkNotNull(sleeper); return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy