docs: public HttpConnection execute() throws IOException {
          boolean retry = true;
  
          while (retry && numberOfRetries-- > 0) {
              connection = connectionFactory.openConnection(url);
  
              if (url.getUserInfo() != null) {
                  // Insert at position 0 in case another interceptor wants to overwrite the BasicAuth
                  requestInterceptors.add(0, new BasicAuthInterceptor(url.getUserInfo()));
              }
              // always read the result, so we can retrieve the HTTP response code
              connection.setDoInput(true);
              connection.setRequestMethod(requestMethod);
              if (contentType != null) {
                  connection.setRequestProperty("Content-type", contentType);
              }
  
              // We set up the output config before the interceptors to allow the configuration to be
              // modified. For example an interceptor might change the chunk size by calling
              // context.connection.getConnection().setChunkedStreamingMode(16384);
              if (input != null) {
                  connection.setDoOutput(true);
                  if (inputLength != -1) {
                      // TODO Remove this cast to int when the minimum supported level is 1.7.
                      // On 1.7 upwards this method takes a long, otherwise int.
                      connection.setFixedLengthStreamingMode((int) this.inputLength);
                  } else {
                      connection.setChunkedStreamingMode(0); // Use 0 for the default size
  
                      // Note that CouchDB does not currently work for a chunked multipart stream, see
                      // https://issues.apache.org/jira/browse/COUCHDB-1403. Cases that use
                      // multipart need to provide the content length until that is fixed.
                  }
              }
  
              currentContext = (currentContext == null) ? new HttpConnectionInterceptorContext
                      (this) : new HttpConnectionInterceptorContext(this, currentContext
                      .interceptorStates);
  
              for (HttpConnectionRequestInterceptor requestInterceptor : requestInterceptors) {
                  currentContext = requestInterceptor.interceptRequest(currentContext);
              }
  
              //set request properties after interceptors, in case the interceptors have added
              // to the properties map
              for (Map.Entry<String, String> property : requestProperties.entrySet()) {
                  connection.setRequestProperty(property.getKey(), property.getValue());
              }
  
              // Log the request
              if (requestIsLoggable && logger.isLoggable(Level.FINE)) {
                  logger.fine(String.format("%s request%s", getLogRequestIdentifier(), (connection
                          .usingProxy() ? " via proxy" : "")));
              }
  
              // Log the request headers
              if (requestIsLoggable && logger.isLoggable(Level.FINER)) {
                  logger.finer(String.format("%s request headers %s", getLogRequestIdentifier(),
                          connection.getRequestProperties()));
              }
  
              if (input != null) {
                  InputStream is = input.getInputStream();
                  OutputStream os = connection.getOutputStream();
                  try {
                      // The buffer size used for writing to this output stream has an impact on the
                      //  HTTP chunk size, so we make it a pretty large size to avoid limiting the
                      // size
                      // of those chunks (although this appears in turn to set the chunk sizes).
                      IOUtils.copyLarge(is, os, new byte[16 * 1024]);
                      os.flush();
                  } finally {
                      Utils.close(is);
                      Utils.close(os);
                  }
              }
  
              // Log the response
              if (requestIsLoggable && logger.isLoggable(Level.FINE)) {
                  logger.fine(String.format("%s response %s %s", getLogRequestIdentifier(),
                          connection.getResponseCode(), connection.getResponseMessage()));
              }
  
              // Log the response headers
              if (requestIsLoggable && logger.isLoggable(Level.FINER)) {
                  logger.finer(String.format("%s response headers %s", getLogRequestIdentifier(),
                          connection.getHeaderFields()));
              }
  
              for (HttpConnectionResponseInterceptor responseInterceptor : responseInterceptors) {
                  try {
                      currentContext = responseInterceptor.interceptResponse(currentContext);
                  } catch (HttpConnectionInterceptorException e) {
                      // Sadly the current interceptor API doesn't allow an IOException to be thrown
                      // so to avoid swallowing them the interceptors need to wrap them in the runtime
                      // HttpConnectionInterceptorException and we can then unwrap them here.
                      Throwable cause = e.getCause();
                      if (cause != null && cause instanceof IOException) {
                          throw (IOException) cause;
                      } else {
                          throw e;
                      }
                  }
              }
  
              // retry flag is set from the final step in the response interceptRequest pipeline
              retry = currentContext.replayRequest;
  
              // If we're going to retry we should consume any existing error streams to avoid
              // leaking connections. Consuming the stream is preferable to just closing it as it
              // makes the connection eligible for re-use.
              if (retry && numberOfRetries > 0) {
                  Utils.consumeAndCloseStream(connection.getErrorStream());
              }
  
              if (numberOfRetries == 0) {
                  logger.info("Maximum number of retries reached");
              }
          }
          // return ourselves to allow method chaining
          return this;
      }