docs: private void postProcessS3Object(final S3Object s3Object, final boolean skipClientSideValidation,
                                           final ProgressListener listener) {
          InputStream is = s3Object.getObjectContent();
          HttpRequestBase httpRequest = s3Object.getObjectContent().getHttpRequest();
          // Hold a reference to this client while the InputStream is still
          // around - otherwise a finalizer in the HttpClient may reset the
          // underlying TCP connection out from under us.
          is = new ServiceClientHolderInputStream(is, this);
          // used trigger a tranfer complete event when the stream is entirely consumed
          ProgressInputStream progressInputStream =
              new ProgressInputStream(is, listener) {
                  @Override protected void onEOF() {
                      publishProgress(getListener(), ProgressEventType.TRANSFER_COMPLETED_EVENT);
                  }
              };
          is = progressInputStream;
  
          // The Etag header contains a server-side MD5 of the object. If
          // we're downloading the whole object, by default we wrap the
          // stream in a validator that calculates an MD5 of the downloaded
          // bytes and complains if what we received doesn't match the Etag.
          if (!skipClientSideValidation) {
              byte[] serverSideHash = BinaryUtils.fromHex(s3Object.getObjectMetadata().getETag());
              try {
                  // No content length check is performed when the
                  // MD5 check is enabled, since a correct MD5 check would
                  // imply a correct content length.
                  MessageDigest digest = MessageDigest.getInstance("MD5");
                  is = new DigestValidationInputStream(is, digest, serverSideHash);
              } catch (NoSuchAlgorithmException e) {
                  log.warn("No MD5 digest algorithm available.  Unable to calculate "
                           + "checksum and verify data integrity.", e);
              }
          } else {
              // Ensures the data received from S3 has the same length as the
              // expected content-length
              is = new LengthCheckInputStream(is,
                                              s3Object.getObjectMetadata().getContentLength(), // expected length
                                              INCLUDE_SKIPPED_BYTES); // bytes received from S3 are all included even if skipped
          }
  
          S3AbortableInputStream abortableInputStream =
              new S3AbortableInputStream(is, httpRequest, s3Object.getObjectMetadata().getContentLength());
          s3Object.setObjectContent(new S3ObjectInputStream(abortableInputStream, httpRequest, false));
      }