@Override
  public void deleteItem(final ProxyRepository repository, final ResourceStoreRequest request)
      throws ItemNotFoundException, UnsupportedStorageOperationException, RemoteStorageException {
    final URL remoteUrl =
        appendQueryString(repository, request, getAbsoluteUrlFromBase(repository, request));

    final HttpDelete method = new HttpDelete(remoteUrl.toExternalForm());

    final HttpResponse httpResponse =
        executeRequestAndRelease(repository, request, method, repository.getRemoteUrl());
    final int statusCode = httpResponse.getStatusLine().getStatusCode();

    if (statusCode != HttpStatus.SC_OK
        && statusCode != HttpStatus.SC_NO_CONTENT
        && statusCode != HttpStatus.SC_ACCEPTED) {
      throw new RemoteStorageException(
          "The response to HTTP "
              + method.getMethod()
              + " was unexpected HTTP Code "
              + statusCode
              + " : "
              + httpResponse.getStatusLine().getReasonPhrase()
              + " [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + remoteUrl.toString()
              + "\"]");
    }
  }
Example #2
0
  @Override
  public void storeItem(final ProxyRepository repository, final StorageItem item)
      throws UnsupportedStorageOperationException, RemoteStorageException {
    if (!(item instanceof StorageFileItem)) {
      throw new UnsupportedStorageOperationException(
          "Storing of non-files remotely is not supported!");
    }

    final StorageFileItem fileItem = (StorageFileItem) item;

    final ResourceStoreRequest request = new ResourceStoreRequest(item);

    final URL remoteUrl =
        appendQueryString(getAbsoluteUrlFromBase(repository, request), repository);

    final HttpPut method = new HttpPut(remoteUrl.toExternalForm());

    final InputStreamEntity entity;
    try {
      entity = new InputStreamEntity(fileItem.getInputStream(), fileItem.getLength());
    } catch (IOException e) {
      throw new RemoteStorageException(
          e.getMessage()
              + " [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + remoteUrl.toString()
              + "\"]",
          e);
    }

    entity.setContentType(fileItem.getMimeType());
    method.setEntity(entity);

    final HttpResponse httpResponse = executeRequestAndRelease(repository, request, method);
    final int statusCode = httpResponse.getStatusLine().getStatusCode();

    if (statusCode != HttpStatus.SC_OK
        && statusCode != HttpStatus.SC_CREATED
        && statusCode != HttpStatus.SC_NO_CONTENT
        && statusCode != HttpStatus.SC_ACCEPTED) {
      throw new RemoteStorageException(
          "Unexpected response code while executing "
              + method.getMethod()
              + " method [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + remoteUrl.toString()
              + "\"]. Expected: \"any success (2xx)\". Received: "
              + statusCode
              + " : "
              + httpResponse.getStatusLine().getReasonPhrase());
    }
  }
  @Override
  public void doApplyConfiguration(
      Repository repository,
      ApplicationConfiguration configuration,
      CRepositoryCoreConfiguration coreConfiguration)
      throws ConfigurationException {
    super.doApplyConfiguration(repository, configuration, coreConfiguration);

    if (repository.getRepositoryKind().isFacetAvailable(ProxyRepository.class)) {
      ProxyRepository proxy = repository.adaptToFacet(ProxyRepository.class);

      proxy.getItemContentValidators().put("checksum", checksumValidator);
      proxy.getItemContentValidators().put("filetypevalidator", fileTypeItemContentValidator);
    }
  }
Example #4
0
  @Override
  protected void updateContext(final ProxyRepository repository, final RemoteStorageContext ctx) {
    getLogger()
        .info(
            "Remote storage settings change detected for ProxyRepository ID=\""
                + repository.getId()
                + "\" (\""
                + repository.getName()
                + "\"), updating HttpClient...");

    // reset current http client, if exists
    HttpClientUtil.release(CTX_KEY, ctx);

    // and create a new one
    HttpClientUtil.configure(CTX_KEY, ctx, getLogger());
  }
 /**
  * Executes the HTTP request.
  *
  * <p>In case of any exception thrown by HttpClient, it will release the connection. In other
  * cases it is the duty of caller to do it, or process the input stream.
  *
  * @param repository to execute the HTTP method for
  * @param request resource store request that triggered the HTTP request
  * @param httpRequest HTTP request to be executed
  * @param baseUrl The BaseURL used to construct final httpRequest
  * @return response of making the request
  * @throws RemoteStorageException If an error occurred during execution of HTTP request
  */
 @VisibleForTesting
 HttpResponse executeRequest(
     final ProxyRepository repository,
     final ResourceStoreRequest request,
     final HttpUriRequest httpRequest,
     final String baseUrl,
     final boolean contentRequest)
     throws RemoteStorageException {
   final Timer timer = timer(repository, httpRequest, baseUrl);
   final TimerContext timerContext = timer.time();
   Stopwatch stopwatch = null;
   if (outboundRequestLog.isDebugEnabled()) {
     stopwatch = new Stopwatch().start();
   }
   try {
     return doExecuteRequest(repository, request, httpRequest, contentRequest);
   } finally {
     timerContext.stop();
     if (stopwatch != null) {
       outboundRequestLog.debug(
           "[{}] {} {} - {}",
           repository.getId(),
           httpRequest.getMethod(),
           httpRequest.getURI(),
           stopwatch);
     }
   }
 }
  protected void validateResponse(
      final ProxyRepository repository,
      final ResourceStoreRequest request,
      final String method,
      final String remoteUrl,
      final Response response,
      int... expectedCodes)
      throws ItemNotFoundException, RemoteStorageException {
    // maintain the S3 flag
    checkForRemotePeerAmazonS3Storage(repository, response.getHeader("server"));

    if (response.isRedirected()) {
      getLogger()
          .info(
              String.format(
                  "Proxy repository %s (id=%s) got redirected from %s, please verify your remoteUrl is up-to-date!",
                  repository.getName(), repository.getId(), remoteUrl));
    }

    if (AHCUtils.isAnyOfTheseStatusCodes(response, expectedCodes)) {
      // good, an expected one
      return;
    }

    // 404 NotFound
    if (404 == response.getStatusCode()) {
      throw new ItemNotFoundException(request, repository);
    }

    // 401 Unauthorized
    if (401 == response.getStatusCode()) {
      throw new RemoteAuthenticationNeededException(
          repository, remoteUrl, response.getStatusText());
    }

    // 403 Forbidden
    if (403 == response.getStatusCode()) {
      throw new RemoteAccessDeniedException(repository, remoteUrl, response.getStatusText());
    }

    // anything else "unexpected"?
    throw new RemoteStorageException(
        String.format(
            "Coult not perform %s against Url %s, unexpected response is %s",
            method, remoteUrl, response.getStatusText()));
  }
Example #7
0
 protected AsyncHttpClient getHttpClient(final ProxyRepository proxyRepository) {
   final AsyncHttpClientConfig.Builder clientConfigBuilder =
       ahcProvider.getAsyncHttpClientConfigBuilder(
           proxyRepository, proxyRepository.getRemoteStorageContext());
   clientConfigBuilder.setFollowRedirects(true);
   clientConfigBuilder.setMaximumNumberOfRedirects(3);
   clientConfigBuilder.setMaxRequestRetry(2);
   final AsyncHttpClient client = new AsyncHttpClient(clientConfigBuilder.build());
   return client;
 }
  @Before
  public void setup() {
    this.underTest = new DefaultUserAgentBuilder(statusSource, contributors);

    when(statusSource.getSystemStatus()).thenReturn(status);
    when(status.getVersion()).thenReturn("2.1-FAKE");
    when(status.getEditionShort()).thenReturn("FAKENEXUS");

    when(ctx.getRemoteConnectionSettings()).thenReturn(settings);
    when(settings.getUserAgentCustomizationString()).thenReturn("SETTINGS_CUSTOMIZATION");

    when(repository.getRemoteStorage()).thenReturn(storage);
    when(storage.getProviderId()).thenReturn("RRS_PROVIDER");
    when(storage.getVersion()).thenReturn("RRS_VERSION");

    when(contributor.getUserAgent(ctx, repository)).thenReturn("PLUGIN_CONTRIBUTED");
  }
  // CLEAN
  public String getRestRepoRemoteStatus(
      ProxyRepository repository, Request request, Response response) throws ResourceException {
    Form form = request.getResourceRef().getQueryAsForm();

    boolean forceCheck = form.getFirst("forceCheck") != null;

    RemoteStatus rs =
        repository.getRemoteStatus(
            new ResourceStoreRequest(RepositoryItemUid.PATH_ROOT), forceCheck);

    if (RemoteStatus.UNKNOWN.equals(rs)) {
      // set status to ACCEPTED, since we have incomplete info
      response.setStatus(Status.SUCCESS_ACCEPTED);
    }

    return rs == null ? null : rs.toString();
  }
  @Override
  protected boolean checkRemoteAvailability(
      long newerThen, ProxyRepository repository, ResourceStoreRequest request, boolean isStrict)
      throws RemoteStorageException {
    final URL remoteURL = getAbsoluteUrlFromBase(repository, request);

    final String itemUrl = remoteURL.toString();

    final AsyncHttpClient client = getClient(repository);

    if (getLogger().isDebugEnabled()) {
      getLogger()
          .debug(
              String.format(
                  "Checking remote availability of proxy repository \"%s\" (id=%s) on URL %s",
                  repository.getName(), repository.getId(), itemUrl));
    }

    // artifactory hack, it pukes on HEAD so we will try with GET if HEAD fails
    boolean doGet = false;

    Response responseObject = null;

    int response = 400;

    try {
      responseObject = client.prepareHead(itemUrl).execute().get();

      response = responseObject.getStatusCode();

      validateResponse(repository, request, "HEAD", itemUrl, responseObject, 200);
    } catch (ItemNotFoundException e) {
      return false;
    } catch (RemoteStorageException e) {
      // If HEAD failed, attempt a GET. Some repos may not support HEAD method
      doGet = true;

      getLogger().debug("HEAD method failed, will attempt GET.  Exception: " + e.getMessage(), e);
    } catch (Exception e) {
      throw new RemoteStorageException(e);
    } finally {
      // HEAD returned error, but not exception, try GET before failing
      if (!doGet && response != 200) {
        // try with GET unless some known to fail responses are in
        doGet = (response != 401) && (response != 403);

        getLogger().debug("HEAD method failed, will attempt GET.  Status: " + response);
      }
    }

    if (doGet) {
      try {
        responseObject = client.prepareGet(itemUrl).execute().get();

        response = responseObject.getStatusCode();

        validateResponse(repository, request, "GET", itemUrl, responseObject, 200);
      } catch (ItemNotFoundException e) {
        return false;
      } catch (Exception e) {
        throw new RemoteStorageException(e);
      }
    }

    // if we are not strict and remote is S3
    if (!isStrict && isRemotePeerAmazonS3Storage(repository)) {
      // if we are relaxed, we will accept any HTTP response code below 500. This means anyway the
      // HTTP
      // transaction succeeded. This method was never really detecting that the remoteUrl really
      // denotes a root of
      // repository (how could we do that?)
      // this "relaxed" check will help us to "pass" S3 remote storage.
      return response >= 200 && response < 500;
    } else {
      // non relaxed check is strict, and will select only the OK response
      if (response == 200) {
        // we have it
        // we have newer if this below is true
        return AHCUtils.getLastModified(responseObject, System.currentTimeMillis()) > newerThen;
      } else if ((response >= 300 && response < 400) || response == 404) {
        return false;
      } else {
        throw new RemoteStorageException(
            "Unexpected response code while executing GET"
                + " method [repositoryId=\""
                + repository.getId()
                + "\", requestPath=\""
                + request.getRequestPath()
                + "\", remoteUrl=\""
                + itemUrl
                + "\"]. Expected: \"SUCCESS (200)\". Received: "
                + response
                + " : "
                + responseObject.getStatusText());
      }
    }
  }
Example #11
0
  /**
   * Executes the HTTP request.
   *
   * <p>In case of any exception thrown by HttpClient, it will release the connection. In other
   * cases it is the duty of caller to do it, or process the input stream.
   *
   * @param repository to execute the HTTP method fpr
   * @param request resource store request that triggered the HTTP request
   * @param httpRequest HTTP request to be executed
   * @return response of making the request
   * @throws RemoteStorageException If an error occurred during execution of HTTP request
   */
  private HttpResponse executeRequest(
      final ProxyRepository repository,
      final ResourceStoreRequest request,
      final HttpUriRequest httpRequest)
      throws RemoteStorageException {
    final URI methodUri = httpRequest.getURI();

    if (getLogger().isDebugEnabled()) {
      getLogger()
          .debug(
              "Invoking HTTP "
                  + httpRequest.getMethod()
                  + " method against remote location "
                  + methodUri);
    }

    final RemoteStorageContext ctx = getRemoteStorageContext(repository);

    final HttpClient httpClient = HttpClientUtil.getHttpClient(CTX_KEY, ctx);

    httpRequest.setHeader("user-agent", formatUserAgentString(ctx, repository));
    httpRequest.setHeader("accept", "*/*");
    httpRequest.setHeader("accept-language", "en-us");
    httpRequest.setHeader("accept-encoding", "gzip,deflate,identity");
    httpRequest.setHeader("cache-control", "no-cache");

    // HTTP keep alive should not be used, except when NTLM is used
    final Boolean isNtlmUsed = HttpClientUtil.isNTLMAuthenticationUsed(CTX_KEY, ctx);
    if (isNtlmUsed == null || !isNtlmUsed) {
      httpRequest.setHeader("Connection", "close");
      httpRequest.setHeader("Proxy-Connection", "close");
    }

    HttpResponse httpResponse = null;
    try {
      httpResponse = httpClient.execute(httpRequest);
      final int statusCode = httpResponse.getStatusLine().getStatusCode();

      final Header httpServerHeader = httpResponse.getFirstHeader("server");
      checkForRemotePeerAmazonS3Storage(
          repository, httpServerHeader == null ? null : httpServerHeader.getValue());

      Header proxyReturnedErrorHeader = httpResponse.getFirstHeader(NEXUS_MISSING_ARTIFACT_HEADER);
      boolean proxyReturnedError =
          proxyReturnedErrorHeader != null && Boolean.valueOf(proxyReturnedErrorHeader.getValue());

      if (statusCode == HttpStatus.SC_FORBIDDEN) {
        throw new RemoteAccessDeniedException(
            repository, methodUri.toASCIIString(), httpResponse.getStatusLine().getReasonPhrase());
      } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
        throw new RemoteAuthenticationNeededException(
            repository, httpResponse.getStatusLine().getReasonPhrase());
      } else if (statusCode == HttpStatus.SC_OK && proxyReturnedError) {
        throw new RemoteStorageException(
            "Invalid artifact found, most likely a proxy redirected to an HTML error page.");
      }

      return httpResponse;
    } catch (RemoteStorageException ex) {
      release(httpResponse);
      throw ex;
    } catch (ClientProtocolException ex) {
      release(httpResponse);
      throw new RemoteStorageException(
          "Protocol error while executing "
              + httpRequest.getMethod()
              + " method. [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + methodUri.toASCIIString()
              + "\"]",
          ex);
    } catch (IOException ex) {
      release(httpResponse);
      throw new RemoteStorageException(
          "Transport error while executing "
              + httpRequest.getMethod()
              + " method [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + methodUri.toASCIIString()
              + "\"]",
          ex);
    }
  }
Example #12
0
 /** @since 2.3 */
 public String getQueryString(final ProxyRepository repository) {
   checkNotNull(repository);
   return getQueryString(repository.getRemoteStorageContext(), repository);
 }
Example #13
0
  @Override
  protected boolean checkRemoteAvailability(
      final long newerThen,
      final ProxyRepository repository,
      final ResourceStoreRequest request,
      final boolean isStrict)
      throws RemoteStorageException {
    final URL remoteUrl =
        appendQueryString(getAbsoluteUrlFromBase(repository, request), repository);

    HttpRequestBase method;
    HttpResponse httpResponse = null;
    int statusCode = HttpStatus.SC_BAD_REQUEST;

    // artifactory hack, it pukes on HEAD so we will try with GET if HEAD fails
    boolean doGet = false;

    {
      method = new HttpHead(remoteUrl.toExternalForm());
      try {
        httpResponse = executeRequestAndRelease(repository, request, method);
        statusCode = httpResponse.getStatusLine().getStatusCode();
      } catch (RemoteStorageException e) {
        // If HEAD failed, attempt a GET. Some repos may not support HEAD method
        doGet = true;

        getLogger().debug("HEAD method failed, will attempt GET. Exception: " + e.getMessage(), e);
      } finally {
        // HEAD returned error, but not exception, try GET before failing
        if (!doGet && statusCode != HttpStatus.SC_OK) {
          doGet = true;

          getLogger().debug("HEAD method failed, will attempt GET. Status: " + statusCode);
        }
      }
    }

    {
      if (doGet) {
        // create a GET
        method = new HttpGet(remoteUrl.toExternalForm());

        // execute it
        httpResponse = executeRequestAndRelease(repository, request, method);
        statusCode = httpResponse.getStatusLine().getStatusCode();
      }
    }

    // if we are not strict and remote is S3
    if (!isStrict && isRemotePeerAmazonS3Storage(repository)) {
      // if we are relaxed, we will accept any HTTP response code below 500. This means anyway the
      // HTTP
      // transaction succeeded. This method was never really detecting that the remoteUrl really
      // denotes a root of
      // repository (how could we do that?)
      // this "relaxed" check will help us to "pass" S3 remote storage.
      return statusCode >= HttpStatus.SC_OK && statusCode <= HttpStatus.SC_INTERNAL_SERVER_ERROR;
    } else {
      // non relaxed check is strict, and will select only the OK response
      if (statusCode == HttpStatus.SC_OK) {
        // we have it
        // we have newer if this below is true
        return makeDateFromHeader(httpResponse.getFirstHeader("last-modified")) > newerThen;
      } else if ((statusCode >= HttpStatus.SC_MULTIPLE_CHOICES
              && statusCode < HttpStatus.SC_BAD_REQUEST)
          || statusCode == HttpStatus.SC_NOT_FOUND) {
        return false;
      } else {
        throw new RemoteStorageException(
            "Unexpected response code while executing "
                + method.getMethod()
                + " method [repositoryId=\""
                + repository.getId()
                + "\", requestPath=\""
                + request.getRequestPath()
                + "\", remoteUrl=\""
                + remoteUrl.toString()
                + "\"]. Expected: \"SUCCESS (200)\". Received: "
                + statusCode
                + " : "
                + httpResponse.getStatusLine().getReasonPhrase());
      }
    }
  }
Example #14
0
  @Override
  public AbstractStorageItem retrieveItem(
      final ProxyRepository repository, final ResourceStoreRequest request, final String baseUrl)
      throws ItemNotFoundException, RemoteStorageException {
    final URL remoteURL =
        appendQueryString(getAbsoluteUrlFromBase(baseUrl, request.getRequestPath()), repository);

    final HttpGet method = new HttpGet(remoteURL.toExternalForm());

    final HttpResponse httpResponse = executeRequest(repository, request, method);

    if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

      if (method.getURI().getPath().endsWith("/")) {
        // this is a collection and not a file!
        // httpClient will follow redirections, and the getPath()
        // _should_
        // give us URL with ending "/"
        release(httpResponse);
        throw new ItemNotFoundException(
            "The remoteURL we got to looks like is a collection, and Nexus cannot fetch collections over plain HTTP (remoteUrl=\""
                + remoteURL.toString()
                + "\")",
            request,
            repository);
      }

      InputStream is;
      try {
        is = httpResponse.getEntity().getContent();

        String mimeType = EntityUtils.getContentMimeType(httpResponse.getEntity());
        if (mimeType == null) {
          mimeType =
              getMimeSupport()
                  .guessMimeTypeFromPath(repository.getMimeRulesSource(), request.getRequestPath());
        }

        final DefaultStorageFileItem httpItem =
            new DefaultStorageFileItem(
                repository, request, CAN_READ, CAN_WRITE, new PreparedContentLocator(is, mimeType));

        if (httpResponse.getEntity().getContentLength() != -1) {
          httpItem.setLength(httpResponse.getEntity().getContentLength());
        }
        httpItem.setRemoteUrl(remoteURL.toString());
        httpItem.setModified(makeDateFromHeader(httpResponse.getFirstHeader("last-modified")));
        httpItem.setCreated(httpItem.getModified());
        httpItem.getItemContext().putAll(request.getRequestContext());

        return httpItem;
      } catch (IOException ex) {
        release(httpResponse);
        throw new RemoteStorageException(
            "IO Error during response stream handling [repositoryId=\""
                + repository.getId()
                + "\", requestPath=\""
                + request.getRequestPath()
                + "\", remoteUrl=\""
                + remoteURL.toString()
                + "\"]!",
            ex);
      } catch (RuntimeException ex) {
        release(httpResponse);
        throw ex;
      }
    } else {
      release(httpResponse);
      if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
        throw new ItemNotFoundException(
            "The remoteURL we requested does not exists on remote server (remoteUrl=\""
                + remoteURL.toString()
                + "\")",
            request,
            repository);
      } else {
        throw new RemoteStorageException(
            "The method execution returned result code "
                + httpResponse.getStatusLine().getStatusCode()
                + ". [repositoryId=\""
                + repository.getId()
                + "\", requestPath=\""
                + request.getRequestPath()
                + "\", remoteUrl=\""
                + remoteURL.toString()
                + "\"]");
      }
    }
  }
  private HttpResponse doExecuteRequest(
      final ProxyRepository repository,
      final ResourceStoreRequest request,
      final HttpUriRequest httpRequest,
      final boolean contentRequest)
      throws RemoteStorageException {
    final URI methodUri = httpRequest.getURI();

    if (log.isDebugEnabled()) {
      log.debug(
          "Invoking HTTP {} method against remote location {}", httpRequest.getMethod(), methodUri);
    }

    final RemoteStorageContext ctx = getRemoteStorageContext(repository);

    final HttpClient httpClient = (HttpClient) ctx.getContextObject(CTX_KEY_CLIENT);

    httpRequest.setHeader("Accept", "*/*");
    httpRequest.setHeader("Accept-Language", "en-us");
    httpRequest.setHeader("Accept-Encoding", "gzip,deflate,identity");
    httpRequest.setHeader("Cache-Control", "no-cache");

    HttpResponse httpResponse = null;
    try {
      final BasicHttpContext httpContext = new BasicHttpContext();
      httpContext.setAttribute(Hc4Provider.HTTP_CTX_KEY_REPOSITORY, repository);
      if (contentRequest) {
        httpContext.setAttribute(CONTENT_RETRIEVAL_MARKER_KEY, Boolean.TRUE);
      }

      httpResponse = httpClient.execute(httpRequest, httpContext);
      final int statusCode = httpResponse.getStatusLine().getStatusCode();

      final Header httpServerHeader = httpResponse.getFirstHeader("server");
      checkForRemotePeerAmazonS3Storage(
          repository, httpServerHeader == null ? null : httpServerHeader.getValue());

      Header proxyReturnedErrorHeader = httpResponse.getFirstHeader(NEXUS_MISSING_ARTIFACT_HEADER);
      boolean proxyReturnedError =
          proxyReturnedErrorHeader != null && Boolean.valueOf(proxyReturnedErrorHeader.getValue());

      if (statusCode == HttpStatus.SC_FORBIDDEN) {
        throw new RemoteAccessDeniedException(
            repository, methodUri.toASCIIString(), httpResponse.getStatusLine().getReasonPhrase());
      } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
        throw new RemoteAuthenticationNeededException(
            repository, httpResponse.getStatusLine().getReasonPhrase());
      } else if (statusCode == HttpStatus.SC_OK && proxyReturnedError) {
        throw new RemoteStorageException(
            "Invalid artifact found, most likely a proxy redirected to an HTML error page.");
      }

      return httpResponse;
    } catch (RemoteStorageException ex) {
      release(httpResponse);
      throw ex;
    } catch (ClientProtocolException ex) {
      release(httpResponse);
      throw new RemoteStorageException(
          "Protocol error while executing "
              + httpRequest.getMethod()
              + " method. [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + methodUri.toASCIIString()
              + "\"]",
          ex);
    } catch (ConnectionPoolTimeoutException ex) {
      release(httpResponse);
      throw new RemoteStorageTransportOverloadedException(
          repository,
          "Connection pool timeout error while executing "
              + httpRequest.getMethod()
              + " method [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + methodUri.toASCIIString()
              + "\"]",
          ex);
    } catch (IOException ex) {
      release(httpResponse);
      throw new RemoteStorageException(
          "Transport error while executing "
              + httpRequest.getMethod()
              + " method [repositoryId=\""
              + repository.getId()
              + "\", requestPath=\""
              + request.getRequestPath()
              + "\", remoteUrl=\""
              + methodUri.toASCIIString()
              + "\"]",
          ex);
    }
  }
  /** Converting App model to REST DTO. */
  public RepositoryProxyResource getRepositoryProxyRestModel(ProxyRepository repository) {
    RepositoryProxyResource resource = new RepositoryProxyResource();

    resource.setRemoteStorage(new RepositoryResourceRemoteStorage());

    resource.getRemoteStorage().setRemoteStorageUrl(repository.getRemoteUrl());

    resource
        .getRemoteStorage()
        .setAuthentication(
            AbstractGlobalConfigurationPlexusResource.convert(
                NexusCompat.getRepositoryRawConfiguration(repository)
                    .getRemoteStorage()
                    .getAuthentication()));

    resource
        .getRemoteStorage()
        .setConnectionSettings(
            AbstractGlobalConfigurationPlexusResource.convert(
                NexusCompat.getRepositoryRawConfiguration(repository)
                    .getRemoteStorage()
                    .getConnectionSettings()));

    resource
        .getRemoteStorage()
        .setHttpProxySettings(
            AbstractGlobalConfigurationPlexusResource.convert(
                NexusCompat.getRepositoryRawConfiguration(repository)
                    .getRemoteStorage()
                    .getHttpProxySettings()));

    // set auto block
    resource.setAutoBlockActive(repository.isAutoBlockActive());

    // set content validation
    resource.setFileTypeValidation(repository.isFileTypeValidation());

    if (repository.getRepositoryKind().isFacetAvailable(MavenProxyRepository.class)) {
      resource.setArtifactMaxAge(
          repository.adaptToFacet(MavenProxyRepository.class).getArtifactMaxAge());

      resource.setMetadataMaxAge(
          repository.adaptToFacet(MavenProxyRepository.class).getMetadataMaxAge());

      resource.setItemMaxAge(repository.adaptToFacet(MavenProxyRepository.class).getItemMaxAge());
    } else {
      // This is a total hack to be able to retrieve this data from a non core repo if available
      try {
        Method artifactMethod =
            repository.getClass().getMethod("getArtifactMaxAge", new Class<?>[0]);
        Method metadataMethod =
            repository.getClass().getMethod("getMetadataMaxAge", new Class<?>[0]);
        Method itemMethod = repository.getClass().getMethod("getItemMaxAge", new Class<?>[0]);

        if (artifactMethod != null) {
          resource.setArtifactMaxAge((Integer) artifactMethod.invoke(repository, new Object[0]));
        }
        if (metadataMethod != null) {
          resource.setMetadataMaxAge((Integer) metadataMethod.invoke(repository, new Object[0]));
        }
        if (itemMethod != null) {
          resource.setItemMaxAge((Integer) itemMethod.invoke(repository, new Object[0]));
        }
      } catch (Exception e) {
        // nothing to do here, doesn't support artifactmax age
      }
    }

    return resource;
  }
  @Override
  protected boolean checkRemoteAvailability(
      final long newerThen,
      final ProxyRepository repository,
      final ResourceStoreRequest request,
      final boolean isStrict)
      throws RemoteStorageException {
    final URL remoteUrl =
        appendQueryString(repository, request, getAbsoluteUrlFromBase(repository, request));

    HttpRequestBase method;
    HttpResponse httpResponse = null;
    int statusCode = HttpStatus.SC_BAD_REQUEST;

    // artifactory hack, it pukes on HEAD so we will try with GET if HEAD fails
    boolean doGet = false;

    {
      method = new HttpHead(remoteUrl.toExternalForm());
      try {
        httpResponse =
            executeRequestAndRelease(repository, request, method, repository.getRemoteUrl());
        statusCode = httpResponse.getStatusLine().getStatusCode();
      } catch (RemoteStorageException e) {
        // If HEAD failed, attempt a GET. Some repos may not support HEAD method
        doGet = true;

        log.debug("HEAD method failed, will attempt GET. Exception: " + e.getMessage(), e);
      } finally {
        // HEAD returned error, but not exception, try GET before failing
        if (!doGet && statusCode != HttpStatus.SC_OK) {
          doGet = true;

          log.debug("HEAD method failed, will attempt GET. Status: " + statusCode);
        }
      }
    }

    {
      if (doGet) {
        // create a GET
        method = new HttpGet(remoteUrl.toExternalForm());

        // execute it
        httpResponse =
            executeRequestAndRelease(repository, request, method, repository.getRemoteUrl());
        statusCode = httpResponse.getStatusLine().getStatusCode();
      }
    }

    // if we are not strict and remote is S3
    if (!isStrict && isRemotePeerAmazonS3Storage(repository)) {
      // if we are relaxed, we will accept any HTTP response code below 500. This means anyway the
      // HTTP
      // transaction succeeded. This method was never really detecting that the remoteUrl really
      // denotes a root of
      // repository (how could we do that?)
      // this "relaxed" check will help us to "pass" S3 remote storage.
      return statusCode >= HttpStatus.SC_OK && statusCode <= HttpStatus.SC_INTERNAL_SERVER_ERROR;
    } else {
      // non relaxed check is strict, and will select only the OK response
      if (statusCode == HttpStatus.SC_OK) {
        // we have it
        // we have newer if this below is true
        return makeDateFromHeader(httpResponse.getFirstHeader("last-modified")) > newerThen;
      } else if ((statusCode >= HttpStatus.SC_MULTIPLE_CHOICES
              && statusCode < HttpStatus.SC_BAD_REQUEST)
          || statusCode == HttpStatus.SC_NOT_FOUND) {
        if (RepositoryItemUid.PATH_ROOT.equals(request.getRequestPath())
            && statusCode == HttpStatus.SC_NOT_FOUND) {
          // NEXUS-5944: Give it a chance: it might be remote Nexus with browsing disabled?
          // to check that, we will check is remote is Nexus by pinging "well know" location
          // if we got it, we will know it's only browsing forbidden on remote
          final RemoteStorageContext ctx = getRemoteStorageContext(repository);
          final HttpClient httpClient = (HttpClient) ctx.getContextObject(CTX_KEY_CLIENT);
          final PageContext pageContext = new RepositoryPageContext(httpClient, repository);
          final ResourceStoreRequest rmRequest =
              new ResourceStoreRequest("/.meta/repository-metadata.xml");
          final URL nxRepoMetadataUrl =
              appendQueryString(
                  repository, rmRequest, getAbsoluteUrlFromBase(repository, rmRequest));
          try {
            final Page page = Page.getPageFor(pageContext, nxRepoMetadataUrl.toExternalForm());
            if (page.getStatusCode() == 200) {
              // this is a Nexus with browsing disabled. say OK
              log.debug(
                  "Original GET request for URL {} failed with 404, but GET request for URL {} succeeded, we assume remote is a Nexus repository having browsing disabled.",
                  remoteUrl,
                  nxRepoMetadataUrl);
              return true;
            }
          } catch (IOException e) {
            // just fall trough
          }
        }
        return false;
      } else {
        throw new RemoteStorageException(
            "Unexpected response code while executing "
                + method.getMethod()
                + " method [repositoryId=\""
                + repository.getId()
                + "\", requestPath=\""
                + request.getRequestPath()
                + "\", remoteUrl=\""
                + remoteUrl.toString()
                + "\"]. Expected: \"SUCCESS (200)\". Received: "
                + statusCode
                + " : "
                + httpResponse.getStatusLine().getReasonPhrase());
      }
    }
  }
  @Override
  public AbstractStorageItem retrieveItem(
      final ProxyRepository repository, final ResourceStoreRequest request, final String baseUrl)
      throws ItemNotFoundException, RemoteStorageException {
    final URL remoteURL =
        appendQueryString(
            repository, request, getAbsoluteUrlFromBase(baseUrl, request.getRequestPath()));

    final String url = remoteURL.toExternalForm();
    if (remoteURL.getPath().endsWith("/")) {
      // NEXUS-5125 we do not want to fetch any collection
      // Even though it is unlikely that we actually see a request for a collection here,
      // requests for paths like this over the REST layer will be localOnly not trigger a remote
      // request.
      //
      // The usual case is that there is a request for a directory that is redirected to '/', see
      // below behavior
      // for SC_MOVED_*
      throw new RemoteItemNotFoundException(
          request, repository, "remoteIsCollection", remoteURL.toString());
    }

    final HttpGet method = new HttpGet(url);

    final HttpResponse httpResponse = executeRequest(repository, request, method, baseUrl, true);

    if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
      InputStream is;
      try {
        is =
            new Hc4InputStream(
                repository,
                new InterruptableInputStream(method, httpResponse.getEntity().getContent()));

        String mimeType = ContentType.getOrDefault(httpResponse.getEntity()).getMimeType();
        if (mimeType == null) {
          mimeType =
              getMimeSupport()
                  .guessMimeTypeFromPath(repository.getMimeRulesSource(), request.getRequestPath());
        }

        final long entityLength = httpResponse.getEntity().getContentLength();
        final DefaultStorageFileItem httpItem =
            new DefaultStorageFileItem(
                repository,
                request,
                CAN_READ,
                CAN_WRITE,
                new PreparedContentLocator(
                    is,
                    mimeType,
                    entityLength != -1 ? entityLength : ContentLocator.UNKNOWN_LENGTH));

        httpItem.setRemoteUrl(remoteURL.toString());
        httpItem.setModified(makeDateFromHeader(httpResponse.getFirstHeader("last-modified")));
        httpItem.setCreated(httpItem.getModified());

        return httpItem;
      } catch (IOException ex) {
        release(httpResponse);
        throw new RemoteStorageException(
            "IO Error during response stream handling [repositoryId=\""
                + repository.getId()
                + "\", requestPath=\""
                + request.getRequestPath()
                + "\", remoteUrl=\""
                + remoteURL.toString()
                + "\"]!",
            ex);
      } catch (RuntimeException ex) {
        release(httpResponse);
        throw ex;
      }
    } else {
      release(httpResponse);
      if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
        throw new RemoteItemNotFoundException(
            request, repository, "NotFound", remoteURL.toString());
      } else if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY
          || httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY) {
        // NEXUS-5125 unfollowed redirect means collection (path.endsWith("/"))
        // see also HttpClientUtil#configure
        throw new RemoteItemNotFoundException(
            request, repository, "redirected", remoteURL.toString());
      } else {
        throw new RemoteStorageException(
            "The method execution returned result code "
                + httpResponse.getStatusLine().getStatusCode()
                + " (expected 200). [repositoryId=\""
                + repository.getId()
                + "\", requestPath=\""
                + request.getRequestPath()
                + "\", remoteUrl=\""
                + remoteURL.toString()
                + "\"]");
      }
    }
  }