Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private Path dependencyPath(ResolvedDependency dependency) {

return resolvedPath.resolve(dependency.getArtifactId() + "-" +
(dependency.getDatedSnapshotVersion() == null ? dependency.getVersion() : dependency.getDatedSnapshotVersion()) +
(dependency.getRequested().getClassifier() == null ? "" : dependency.getRequested().getClassifier()) +
(dependency.getRequested().getClassifier() == null ? "" : "-" + dependency.getRequested().getClassifier()) +
".jar");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,35 @@ public MavenArtifactDownloader(MavenArtifactCache mavenArtifactCache,
bodyStream = Files.newInputStream(Paths.get(URI.create(uri)));
} else {
HttpSender.Request.Builder request = applyAuthentication(dependency.getRepository(), httpSender.get(uri));
try (HttpSender.Response response = Failsafe.with(retryPolicy).get(() -> httpSender.send(request.build()));
InputStream body = response.getBody()) {
if (!response.isSuccessful() || body == null) {
onError.accept(new MavenDownloadingException(String.format("Unable to download dependency %s:%s:%s from %s. Response was %d",
dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), uri, response.getCode()), null,
try {
byte[] responseBytes = null;
int responseCode;
try (HttpSender.Response response = Failsafe.with(retryPolicy).get(() -> httpSender.send(request.build()));
InputStream body = response.getBody()) {
responseCode = response.getCode();
if (response.isSuccessful() && body != null) {
responseBytes = readAllBytes(body);
}
}
// Fall back to anonymous if authenticated request fails with a 4xx client error
if (responseBytes == null && hasCredentials(dependency.getRepository()) && responseCode >= 400 && responseCode < 500) {
try (HttpSender.Response response = Failsafe.with(retryPolicy).get(() -> httpSender.send(httpSender.get(uri).build()));
InputStream body = response.getBody()) {
responseCode = response.getCode();
if (response.isSuccessful() && body != null) {
responseBytes = readAllBytes(body);
}
}
}
if (responseBytes == null) {
onError.accept(new MavenDownloadingException(String.format("Unable to download dependency %s:%s:%s%s from %s. Response was %d",
dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(),
dependency.getClassifier() == null ? "" : ":" + dependency.getClassifier(),
uri, responseCode), null,
dependency.getRequested().getGav()));
return null;
}
bodyStream = new ByteArrayInputStream(readAllBytes(body));
bodyStream = new ByteArrayInputStream(responseBytes);
} catch (Throwable t) {
Throwable cause = t instanceof FailsafeException && t.getCause() != null ? t.getCause() : t;
throw new MavenDownloadingException("Unable to download dependency", cause,
Expand All @@ -130,14 +150,27 @@ public MavenArtifactDownloader(MavenArtifactCache mavenArtifactCache,
}

private HttpSender.Request.Builder applyAuthentication(MavenRepository repository, HttpSender.Request.Builder request) {
MavenSettings.Server authInfo = serverIdToServer.get(repository.getId());
if (authInfo != null && authInfo.getConfiguration() != null && authInfo.getConfiguration().getHttpHeaders() != null) {
for (MavenSettings.HttpHeader header : authInfo.getConfiguration().getHttpHeaders()) {
request.withHeader(header.getName(), header.getValue());
}
}
String[] credentials = resolveCredentials(repository);
if (credentials != null) {
return request.withBasicAuthentication(credentials[0], credentials[1]);
}
return request;
}

private boolean hasCredentials(MavenRepository repository) {
return resolveCredentials(repository) != null;
}

private String @Nullable [] resolveCredentials(MavenRepository repository) {
String username, password;
MavenSettings.Server authInfo = serverIdToServer.get(repository.getId());
if (authInfo != null) {
if (authInfo.getConfiguration() != null && authInfo.getConfiguration().getHttpHeaders() != null) {
for (MavenSettings.HttpHeader header : authInfo.getConfiguration().getHttpHeaders()) {
request.withHeader(header.getName(), header.getValue());
}
}
username = authInfo.getUsername();
password = authInfo.getPassword();
} else {
Expand All @@ -146,8 +179,8 @@ private HttpSender.Request.Builder applyAuthentication(MavenRepository repositor
}
if (username != null && !username.contains("${") &&
password != null && !password.contains("${")) {
return request.withBasicAuthentication(username, password);
return new String[]{username, password};
}
return request;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,63 @@ void downloadDependenciesWithClassifier(@TempDir Path tempDir) {
}
}

@Test
void fallsBackToAnonymousWhenServerReturns401(@TempDir Path tempDir) throws Exception {
byte[] jarBytes = {0x50, 0x4B, 0x03, 0x04};

try (MockWebServer mockRepo = new MockWebServer()) {
mockRepo.setDispatcher(new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) {
if (request.getHeader("Authorization") != null) {
return new MockResponse().setResponseCode(401);
}
return new MockResponse().setResponseCode(200)
.setBody(new okio.Buffer().write(jarBytes));
}
});
mockRepo.start();

String repoUrl = "http://" + mockRepo.getHostName() + ":" + mockRepo.getPort();
MavenSettings settings = MavenSettings.parse(new Parser.Input(
Path.of("settings.xml"), () -> new ByteArrayInputStream(
//language=xml
"""
<settings>
<servers>
<server>
<id>mock-repo</id>
<username>bad-user</username>
<password>bad-password</password>
</server>
</servers>
</settings>
""".getBytes())), new InMemoryExecutionContext());

MavenArtifactCache artifactCache = new LocalMavenArtifactCache(tempDir);
AtomicReference<Throwable> error = new AtomicReference<>();
MavenArtifactDownloader downloader = new MavenArtifactDownloader(
artifactCache, settings, error::set);

MavenRepository repo = new MavenRepository(
"mock-repo", repoUrl, "true", "false", true, null, null, null, false);
GroupArtifactVersion gav = new GroupArtifactVersion("com.example", "test-lib", "1.0.0");
ResolvedDependency dep = ResolvedDependency.builder()
.repository(repo)
.gav(new ResolvedGroupArtifactVersion(repoUrl, gav.getGroupId(), gav.getArtifactId(), gav.getVersion(), null))
.requested(Dependency.builder().gav(gav).build())
.build();

Path artifact = downloader.downloadArtifact(dep);

assertThat(artifact).isNotNull();
assertThat(error.get()).isNull();
assertThat(mockRepo.getRequestCount()).isEqualTo(2);
assertThat(mockRepo.takeRequest().getHeader("Authorization")).isNotNull();
assertThat(mockRepo.takeRequest().getHeader("Authorization")).isNull();
}
}

@Test
void fallsBackToAnonymousWhenCredentialsRejected(@TempDir Path tempDir) throws Exception {
byte[] jarBytes = {0x50, 0x4B, 0x03, 0x04}; // minimal ZIP magic bytes
Expand Down
Loading