/*
   * (non-Javadoc)
   *
   * @see
   * com.sun.jersey.api.client.filter.ClientFilter#handle(com.sun.jersey.api
   * .client.ClientRequest)
   */
  @Override
  public ClientResponse handle(ClientRequest cr) {
    String key = getKey();

    if (cr.getProperties().containsKey(key)) {
      return this.getNext().handle(cr);
    }
    cr.getProperties().put(key, this);
    return doHandle(cr);
  }
 private Request buildRequest(ClientRequest req) throws IOException {
   final Request request =
       client.newRequest(req.getURI()).method(HttpMethod.fromString(req.getMethod()));
   if (req.getEntity() != null) {
     final RequestEntityWriter writer = getRequestEntityWriter(req);
     final PipedOutputStream output = new PipedOutputStream();
     final PipedInputStream input = new PipedInputStream(output);
     request.content(new InputStreamContentProvider(input));
     writer.writeRequestEntity(output);
   }
   return request;
 }
  @Override
  public ClientResponse handle(ClientRequest cr) throws ClientHandlerException {
    // Only sign if no other filter is handling authorization
    if (cr.getProperties().get(SharedKeyUtils.AUTHORIZATION_FILTER_MARKER) == null) {
      cr.getProperties().put(SharedKeyUtils.AUTHORIZATION_FILTER_MARKER, null);

      // Register ourselves as  listener so we are called back when the entity is
      // written to the output stream by the next filter in line.
      if (cr.getProperties().get(EntityStreamingListener.class.getName()) == null) {
        cr.getProperties().put(EntityStreamingListener.class.getName(), this);
      }
    }
    return this.getNext().handle(cr);
  }
 private void addOptionalDateHeader(ClientRequest cr) {
   String date = getHeader(cr, "Date");
   if (date == "") {
     date = new RFC1123DateConverter().format(new Date());
     cr.getHeaders().add("Date", date);
   }
 }
 public ClientResponse handle(ClientRequest cr) {
   cr.getHeaders()
       .add(
           HttpHeaders.AUTHORIZATION,
           "Basic " + new String(Base64.encode(user + ":" + pass), Charset.forName("ASCII")));
   ClientResponse resp = getNext().handle(cr);
   return resp;
 }
 /*
  * (non-Javadoc)
  *
  * @see
  * com.microsoft.windowsazure.services.core.IdempotentClientFilter#doHandle
  * (com.sun.jersey.api.client.ClientRequest)
  */
 @Override
 public ClientResponse doHandle(ClientRequest cr) {
   MultivaluedMap<String, Object> headers = cr.getHeaders();
   headers.add("DataServiceVersion", "3.0");
   headers.add("MaxDataServiceVersion", "3.0");
   headers.add("x-ms-version", "2.5");
   return getNext().handle(cr);
 }
 private void writeOutBoundHeaders(MultivaluedMap<String, Object> headers, Request request) {
   for (Map.Entry<String, List<Object>> e : headers.entrySet()) {
     final List<Object> vs = e.getValue();
     if (vs.size() == 1) {
       request.header(e.getKey(), ClientRequest.getHeaderValue(vs.get(0)));
     } else {
       final StringBuilder header = new StringBuilder();
       for (Object v : e.getValue()) {
         if (header.length() > 0) {
           header.append(',');
         }
         header.append(ClientRequest.getHeaderValue(v));
       }
       request.header(e.getKey(), header.toString());
     }
   }
 }
 private void writeOutBoundHeaders(MultivaluedMap<String, Object> metadata, HttpURLConnection uc) {
   for (Map.Entry<String, List<Object>> e : metadata.entrySet()) {
     List<Object> vs = e.getValue();
     if (vs.size() == 1) {
       uc.setRequestProperty(e.getKey(), ClientRequest.getHeaderValue(vs.get(0)));
     } else {
       StringBuilder b = new StringBuilder();
       boolean add = false;
       for (Object v : e.getValue()) {
         if (add) b.append(',');
         add = true;
         b.append(ClientRequest.getHeaderValue(v));
       }
       uc.setRequestProperty(e.getKey(), b.toString());
     }
   }
 }
  /**
   * This format supports Shared Key authentication for the 2009-09-19 version of the Blob and Queue
   * services. Construct the CanonicalizedResource string in this format as follows:
   *
   * <p>1. Beginning with an empty string (""), append a forward slash (/), followed by the name of
   * the account that owns the resource being accessed.
   *
   * <p>2. Append the resource's encoded URI path, without any query parameters.
   *
   * <p>3. Retrieve all query parameters on the resource URI, including the comp parameter if it
   * exists.
   *
   * <p>4. Convert all parameter names to lowercase.
   *
   * <p>5. Sort the query parameters lexicographically by parameter name, in ascending order.
   *
   * <p>6. URL-decode each query parameter name and value.
   *
   * <p>7. Append each query parameter name and value to the string in the following format, making
   * sure to include the colon (:) between the name and the value:
   *
   * <p>parameter-name:parameter-value
   *
   * <p>8. If a query parameter has more than one value, sort all values lexicographically, then
   * include them in a comma-separated list:
   *
   * <p>parameter-name:parameter-value-1,parameter-value-2,parameter-value-n
   *
   * <p>9. Append a new line character (\n) after each name-value pair.
   */
  private String getCanonicalizedResource(ClientRequest cr) {
    // 1. Beginning with an empty string (""), append a forward slash (/), followed by the name of
    // the account that owns
    //    the resource being accessed.
    String result = "/" + this.accountName;

    // 2. Append the resource's encoded URI path, without any query parameters.
    result += cr.getURI().getPath();

    // 3. Retrieve all query parameters on the resource URI, including the comp parameter if it
    // exists.
    // 6. URL-decode each query parameter name and value.
    List<QueryParam> queryParams = SharedKeyUtils.getQueryParams(cr.getURI().getQuery());

    // 4. Convert all parameter names to lowercase.
    for (QueryParam param : queryParams) {
      param.setName(param.getName().toLowerCase(Locale.US));
    }

    // 5. Sort the query parameters lexicographically by parameter name, in ascending order.
    Collections.sort(queryParams);

    // 7. Append each query parameter name and value to the string
    // 8. If a query parameter has more than one value, sort all values lexicographically, then
    // include them in a comma-separated list
    for (int i = 0; i < queryParams.size(); i++) {
      QueryParam param = queryParams.get(i);

      List<String> values = param.getValues();
      // Collections.sort(values);

      // 9. Append a new line character (\n) after each name-value pair.
      result += "\n";
      result += param.getName();
      result += ":";
      for (int j = 0; j < values.size(); j++) {
        if (j > 0) {
          result += ",";
        }
        result += values.get(j);
      }
    }

    return result;
  }
  public ClientResponse handle(ClientRequest clientRequest) {
    byte[] requestEntity = writeRequestEntity(clientRequest);

    InBoundHeaders rh = getInBoundHeaders(clientRequest.getMetadata());

    final ContainerRequest cRequest =
        new ContainerRequest(
            w,
            clientRequest.getMethod(),
            baseUri,
            clientRequest.getURI(),
            rh,
            new ByteArrayInputStream(requestEntity));

    // TODO this is a hack
    List<String> cookies = cRequest.getRequestHeaders().get("Cookie");
    if (cookies != null) {
      for (String cookie : cookies) {
        if (cookie != null) cRequest.getCookies().putAll(HttpHeaderReader.readCookies(cookie));
      }
    }

    final TstContainerResponseWriter writer = new TstContainerResponseWriter();
    final ContainerResponse cResponse = new ContainerResponse(w, cRequest, writer);

    try {
      w.handleRequest(cRequest, cResponse);
    } catch (IOException e) {
      throw new ContainerException(e);
    }

    byte[] responseEntity = writer.baos.toByteArray();
    ClientResponse clientResponse =
        new ClientResponse(
            cResponse.getStatus(),
            getInBoundHeaders(cResponse.getHttpHeaders()),
            new ByteArrayInputStream(responseEntity),
            getMessageBodyWorkers());

    clientResponse.getProperties().put("request.entity", requestEntity);
    clientResponse.getProperties().put("response.entity", responseEntity);
    return clientResponse;
  }
  /*
   * StringToSign = VERB + "\n" +
   *          Content-Encoding + "\n"
   *          Content-Language + "\n"
   *          Content-Length + "\n"
   *          Content-MD5 + "\n" +
   *          Content-Type + "\n" +
   *          Date + "\n" +
   *          If-Modified-Since + "\n"
   *          If-Match + "\n"
   *          If-None-Match + "\n"
   *          If-Unmodified-Since + "\n"
   *          Range + "\n"
   *          CanonicalizedHeaders +
   *          CanonicalizedResource;
   */
  public void sign(ClientRequest cr) {
    // gather signed material
    addOptionalDateHeader(cr);

    // build signed string
    String stringToSign =
        cr.getMethod()
            + "\n"
            + getHeader(cr, "Content-Encoding")
            + "\n"
            + getHeader(cr, "Content-Language")
            + "\n"
            + getHeader(cr, "Content-Length")
            + "\n"
            + getHeader(cr, "Content-MD5")
            + "\n"
            + getHeader(cr, "Content-Type")
            + "\n"
            + getHeader(cr, "Date")
            + "\n"
            + getHeader(cr, "If-Modified-Since")
            + "\n"
            + getHeader(cr, "If-Match")
            + "\n"
            + getHeader(cr, "If-None-Match")
            + "\n"
            + getHeader(cr, "If-Unmodified-Since")
            + "\n"
            + getHeader(cr, "Range")
            + "\n";

    stringToSign += getCanonicalizedHeaders(cr);
    stringToSign += getCanonicalizedResource(cr);

    if (log.isDebugEnabled()) {
      log.debug(String.format("String to sign: \"%s\"", stringToSign));
    }
    // System.out.println(String.format("String to sign: \"%s\"", stringToSign));

    String signature = this.signer.sign(stringToSign);
    cr.getHeaders().putSingle("Authorization", "SharedKey " + this.accountName + ":" + signature);
  }
 @Override
 public ClientResponse handle(ClientRequest clientRequest) throws ClientHandlerException {
   final MultivaluedMap<String, Object> headers = clientRequest.getHeaders();
   List<Object> hcookies = headers.get(COOKIE_HEADER);
   if (hcookies == null) {
     hcookies = new ArrayList<Object>();
   }
   hcookies.addAll(cookies);
   headers.put(COOKIE_HEADER, hcookies);
   return getNext().handle(clientRequest);
 }
  public void configureRequest(
      final RequestBuilder requestBuilder, final ClientRequest cr, final boolean needsBody) {
    final Map<String, Object> props = cr.getProperties();

    // Set the read timeout
    final Integer readTimeout = (Integer) props.get(AhcConfig.PROPERTY_READ_TIMEOUT);
    if (readTimeout != null) {
      final PerRequestConfig c = new PerRequestConfig();
      c.setRequestTimeoutInMs(readTimeout);
      requestBuilder.setPerRequestConfig(c);
    }
    if (cr.getEntity() != null && needsBody) {
      final RequestEntityWriter re = getRequestEntityWriter(cr);

      final ByteArrayOutputStream baos = new ByteArrayOutputStream();
      try {
        re.writeRequestEntity(
            new CommittingOutputStream(baos) {
              @Override
              protected void commit() throws IOException {
                configureHeaders(cr.getHeaders(), requestBuilder);
              }
            });
      } catch (final IOException ex) {
        throw new ClientHandlerException(ex);
      }

      final byte[] content = baos.toByteArray();
      requestBuilder.setBody(
          new Request.EntityWriter() {
            @Override
            public void writeEntity(final OutputStream out) throws IOException {
              out.write(content);
            }
          });
    } else {
      configureHeaders(cr.getHeaders(), requestBuilder);
    }
  }
 @Override
 public ClientResponse handle(ClientRequest cr) throws ClientHandlerException {
   try {
     final Request request = buildRequest(cr);
     writeOutBoundHeaders(cr.getHeaders(), request);
     final ContentResponse response = request.send();
     return new ClientResponse(
         response.getStatus(),
         getInBoundHeaders(response),
         new ByteArrayInputStream(response.getContent()),
         getMessageBodyWorkers());
   } catch (InterruptedException | TimeoutException | ExecutionException | IOException e) {
     throw new ClientHandlerException(e);
   }
 }
  @Override
  public void executeMethod(final HttpMethod method, final ClientRequest cr) {
    final Map<String, Object> props = cr.getProperties();

    method.setDoAuthentication(true);

    final HttpMethodParams methodParams = method.getParams();

    // Set the handle cookies property
    if (!cr.getPropertyAsFeature(ApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES)) {
      methodParams.setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
    }

    // Set the interactive and credential provider properties
    if (cr.getPropertyAsFeature(ApacheHttpClientConfig.PROPERTY_INTERACTIVE)) {
      CredentialsProvider provider =
          (CredentialsProvider) props.get(ApacheHttpClientConfig.PROPERTY_CREDENTIALS_PROVIDER);
      if (provider == null) {
        provider = DEFAULT_CREDENTIALS_PROVIDER;
      }
      methodParams.setParameter(CredentialsProvider.PROVIDER, provider);
    } else {
      methodParams.setParameter(CredentialsProvider.PROVIDER, null);
    }

    // Set the read timeout
    final Integer readTimeout = (Integer) props.get(ApacheHttpClientConfig.PROPERTY_READ_TIMEOUT);
    if (readTimeout != null) {
      methodParams.setSoTimeout(readTimeout);
    }

    if (method instanceof EntityEnclosingMethod) {
      final EntityEnclosingMethod entMethod = (EntityEnclosingMethod) method;

      if (cr.getEntity() != null) {
        final RequestEntityWriter re = getRequestEntityWriter(cr);
        final Integer chunkedEncodingSize =
            (Integer) props.get(ApacheHttpClientConfig.PROPERTY_CHUNKED_ENCODING_SIZE);
        if (chunkedEncodingSize != null) {
          // There doesn't seems to be a way to set the chunk size.
          entMethod.setContentChunked(true);

          // It is not possible for a MessageBodyWriter to modify
          // the set of headers before writing out any bytes to
          // the OutputStream
          // This makes it impossible to use the multipart
          // writer that modifies the content type to add a boundary
          // parameter
          writeOutBoundHeaders(cr.getHeaders(), method);

          // Do not buffer the request entity when chunked encoding is
          // set
          entMethod.setRequestEntity(
              new RequestEntity() {

                @Override
                public boolean isRepeatable() {
                  return false;
                }

                @Override
                public void writeRequest(OutputStream out) throws IOException {
                  re.writeRequestEntity(out);
                }

                @Override
                public long getContentLength() {
                  return re.getSize();
                }

                @Override
                public String getContentType() {
                  return re.getMediaType().toString();
                }
              });

        } else {
          entMethod.setContentChunked(false);

          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          try {
            re.writeRequestEntity(
                new CommittingOutputStream(baos) {

                  @Override
                  protected void commit() throws IOException {
                    writeOutBoundHeaders(cr.getMetadata(), method);
                  }
                });
          } catch (IOException ex) {
            throw new ClientHandlerException(ex);
          }

          final byte[] content = baos.toByteArray();
          entMethod.setRequestEntity(
              new RequestEntity() {

                @Override
                public boolean isRepeatable() {
                  return true;
                }

                @Override
                public void writeRequest(OutputStream out) throws IOException {
                  out.write(content);
                }

                @Override
                public long getContentLength() {
                  return content.length;
                }

                @Override
                public String getContentType() {
                  return re.getMediaType().toString();
                }
              });
        }
      }
    } else {
      writeOutBoundHeaders(cr.getHeaders(), method);

      // Follow redirects
      method.setFollowRedirects(
          cr.getPropertyAsFeature(ApacheHttpClientConfig.PROPERTY_FOLLOW_REDIRECTS));
    }
    try {
      httpClient.executeMethod(
          getHostConfiguration(httpClient, props), method, getHttpState(props));
    } catch (Exception e) {
      method.releaseConnection();
      throw new ClientHandlerException(e);
    }
  }
  @Override
  public ClientResponse handle(final ClientRequest request) throws ClientHandlerException {

    // Remember if we sent a request a with headers
    boolean reqHadAuthHeaders = false;

    // Have we already login ? : Then add authorization info to the headers
    if (state.get().nextNonce != null) {

      // Remember we sent headers
      reqHadAuthHeaders = true;

      // Alias to string representation of qop
      String qopStr = null;
      if (state.get().qop != null) qopStr = (state.get().qop == QOP.AUTH_INT) ? "auth-int" : "auth";

      // Init the value of the "authorized" header
      StringBuffer buff = new StringBuffer();

      // Authorization scheme
      buff.append("Digest ");

      // Key/val pairs
      addKeyVal(buff, "username", this.user);
      addKeyVal(buff, "realm", state.get().realm);
      addKeyVal(buff, "nonce", state.get().nextNonce);
      if (state.get().opaque != null) addKeyVal(buff, "opaque", state.get().opaque);
      if (state.get().algorithm != null) addKeyVal(buff, "algorithm", state.get().algorithm, false);
      if (state.get().qop != null) addKeyVal(buff, "qop", qopStr, false);
      // if (this.domain != null) addKeyVal(buff, "domain", this.domain);

      // -------------------------------------------------------
      // Compute the Digest Hash
      // -------------------------------------------------------

      // HA1
      String HA1 = concatMD5(this.user, state.get().realm, this.pass);

      // Get exact requested URI
      String uri = request.getURI().getPath();

      // Repeat URI in header
      addKeyVal(buff, "uri", uri);

      // HA2 : Switch on qop
      String HA2;
      if (state.get().qop == QOP.AUTH_INT && (request.getEntity() != null)) {
        HA2 = concatMD5(request.getMethod(), uri, request.getEntity().toString());
      } else {
        HA2 = concatMD5(request.getMethod(), uri);
      }

      // Compute response
      String response;
      if (state.get().qop == null) { // Simple response

        response = concatMD5(HA1, state.get().nextNonce, HA2);

      } else { // Quality of protection is set

        // Generate client nonce (UID)
        String cnonce = randHexBytes(CNONCE_NB_BYTES);

        // Counter in hexadecimal
        String nc = String.format("%08x", state.get().counter);
        state.get().counter += 1;

        // Add them to key/value pairs
        addKeyVal(buff, "cnonce", cnonce);
        addKeyVal(buff, "nc", nc, false);

        response = concatMD5(HA1, state.get().nextNonce, nc, cnonce, qopStr, HA2);
      }

      // Append the response
      addKeyVal(buff, "response", response);

      // Remove the last coma
      buff.deleteCharAt(buff.length() - 1);
      String authLine = buff.toString();

      // Add the whole Authorization line to the header
      request.getMetadata().add(HttpHeaders.AUTHORIZATION, authLine);
    } // End of "we already logged in ?"

    // Forward the request to the next filter and get the result back
    ClientResponse response = getNext().handle(request);

    // The server asked for authentication ? (status 401)
    if (response.getResponseStatus() == Status.UNAUTHORIZED) {

      // Parse the www-authentication headers
      HashMap<String, String> map =
          parseHeaders(response.getHeaders().get(HttpHeaders.WWW_AUTHENTICATE));

      // No digest authentication request found ? => We can do nothing more
      if (map == null) return response;

      // Get header values
      state.get().realm = map.get("realm");
      state.get().nextNonce = map.get("nonce");
      state.get().opaque = map.get("opaque");
      state.get().algorithm = map.get("algorithm");
      state.get().domain = map.get("domain");

      // Parse Qop
      String qop = map.get("qop");
      if (qop == null) {
        state.get().qop = null;
      } else {
        if (qop.contains("auth-int")) {
          state.get().qop = QOP.AUTH_INT;
        } else if (qop.contains("auth")) {
          state.get().qop = QOP.AUTH;
        } else {
          state.get().qop = null;
        }
      }

      // Parse "stale"
      String staleStr = map.get("stale");

      boolean stale = (staleStr != null) && staleStr.toLowerCase().equals("true");

      // Did we send the initial request without headers ?
      // Or the server asked to retry with new nonce ?
      if (stale || !reqHadAuthHeaders) {
        // Then try to resent same request with updated headers
        return this.handle(request);
      } else {
        // We already tried to log, but the authentication failed :
        // Just forward this response
        return response;
      }
    }

    // Not 401 status : no authentication issue
    return response;
  } // End of #handle()
  private ClientResponse _invoke(final ClientRequest ro) throws IOException {
    final HttpURLConnection uc;

    if (this.httpURLConnectionFactory == null) {
      uc = (HttpURLConnection) ro.getURI().toURL().openConnection();
    } else {
      uc = this.httpURLConnectionFactory.getHttpURLConnection(ro.getURI().toURL());
    }

    Integer readTimeout = (Integer) ro.getProperties().get(ClientConfig.PROPERTY_READ_TIMEOUT);
    if (readTimeout != null) {
      uc.setReadTimeout(readTimeout);
    }

    Integer connectTimeout =
        (Integer) ro.getProperties().get(ClientConfig.PROPERTY_CONNECT_TIMEOUT);
    if (connectTimeout != null) {
      uc.setConnectTimeout(connectTimeout);
    }

    Boolean followRedirects =
        (Boolean) ro.getProperties().get(ClientConfig.PROPERTY_FOLLOW_REDIRECTS);
    if (followRedirects != null) {
      uc.setInstanceFollowRedirects(followRedirects);
    }

    if (uc instanceof HttpsURLConnection) {
      HTTPSProperties httpsProperties =
          (HTTPSProperties) ro.getProperties().get(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES);
      if (httpsProperties != null) {
        httpsProperties.setConnection((HttpsURLConnection) uc);
      }
    }

    Boolean httpUrlConnectionSetMethodWorkaround =
        (Boolean) ro.getProperties().get(PROPERTY_HTTP_URL_CONNECTION_SET_METHOD_WORKAROUND);
    if (httpUrlConnectionSetMethodWorkaround != null
        && httpUrlConnectionSetMethodWorkaround == true) {
      setRequestMethodUsingWorkaroundForJREBug(uc, ro.getMethod());
    } else {
      uc.setRequestMethod(ro.getMethod());
    }

    // Write the request headers
    writeOutBoundHeaders(ro.getHeaders(), uc);

    // Write the entity (if any)
    Object entity = ro.getEntity();
    if (entity != null) {
      uc.setDoOutput(true);

      if (ro.getMethod().equalsIgnoreCase("GET")) {
        final Logger logger = Logger.getLogger(URLConnectionClientHandler.class.getName());
        if (logger.isLoggable(Level.INFO)) {
          logger.log(
              Level.INFO,
              "GET method with entity will be most likely replaced by POST, see http://java.net/jira/browse/JERSEY-1161");
        }
      }

      writeRequestEntity(
          ro,
          new RequestEntityWriterListener() {
            public void onRequestEntitySize(long size) {
              if (size != -1 && size < Integer.MAX_VALUE) {
                // HttpURLConnection uses the int type for content length
                uc.setFixedLengthStreamingMode((int) size);
              } else {
                // TODO it appears HttpURLConnection has some bugs in
                // chunked encoding
                // uc.setChunkedStreamingMode(0);
                Integer chunkedEncodingSize =
                    (Integer) ro.getProperties().get(ClientConfig.PROPERTY_CHUNKED_ENCODING_SIZE);
                if (chunkedEncodingSize != null) {
                  uc.setChunkedStreamingMode(chunkedEncodingSize);
                }
              }
            }

            public OutputStream onGetOutputStream() throws IOException {
              return new CommittingOutputStream() {
                @Override
                protected OutputStream getOutputStream() throws IOException {
                  return uc.getOutputStream();
                }

                @Override
                public void commit() throws IOException {
                  writeOutBoundHeaders(ro.getHeaders(), uc);
                }
              };
            }
          });
    } else {
      writeOutBoundHeaders(ro.getHeaders(), uc);
    }

    // Return the in-bound response
    return new URLConnectionResponse(
        uc.getResponseCode(), getInBoundHeaders(uc), getInputStream(uc), ro.getMethod(), uc);
  }
Exemple #18
0
  @Override
  public ClientResponse handle(ClientRequest clientRequest) throws ClientHandlerException {
    int retryCount = 0;
    InputStream entityStream = null;
    if (clientRequest.getEntity() instanceof InputStream)
      entityStream = (InputStream) clientRequest.getEntity();
    while (true) {
      try {
        // if using an InputStream, mark the stream so we can rewind it in case of an error
        if (entityStream != null && entityStream.markSupported())
          entityStream.mark(config.getRetryBufferSize());

        return getNext().handle(clientRequest);
      } catch (RuntimeException orig) {
        Throwable t = orig;

        // in this case, the exception was wrapped by Jersey
        if (t instanceof ClientHandlerException) t = t.getCause();

        if (t instanceof AtmosException) {
          AtmosException ae = (AtmosException) t;

          // retry all 50x errors
          if (ae.getHttpCode() < 500) throw orig;

          // add small delay to Atmos code 1040 (server busy)
          if (ae.getErrorCode() == 1040) {
            try {
              Thread.sleep(ATMOS_1040_DELAY_MS);
            } catch (InterruptedException e) {
              log.warn("Interrupted while waiting after a 1040 response: " + e.getMessage());
            }
          }

          // retry all IO exceptions unless wschecksum is enabled (can't overwrite data in this
          // case)
        } else if (!(t instanceof IOException)
            || clientRequest.getHeaders().getFirst(RestUtil.XHEADER_WSCHECKSUM) != null) throw orig;

        // only retry maxRetries times
        if (++retryCount > config.getMaxRetries()) throw orig;

        // attempt to reset InputStream if it has been read from
        if (entityStream != null) {
          if (!(entityStream instanceof MeasuredInputStream)
              || ((MeasuredInputStream) entityStream).getRead() > 0) {
            try {
              if (!entityStream.markSupported()) throw new IOException("Mark is not supported");
              entityStream.reset();
            } catch (IOException e) {
              log.warn("Could not reset entity stream for retry: " + e.getMessage());
              throw orig;
            }
          }
        }

        log.info("Error received in response (" + t + "), retrying...");

        // wait for retry delay
        if (config.getRetryDelayMillis() > 0) {
          try {
            Thread.sleep(config.getRetryDelayMillis());
          } catch (InterruptedException e) {
            log.warn("Interrupted while waiting to retry: " + e.getMessage());
          }
        }
      }
    }
  }
 @Override
 public ClientResponse handle(ClientRequest clientRequest) throws ClientHandlerException {
   clientRequest.getHeaders().add("Connection", "close");
   return getNext().handle(clientRequest);
 }