diff --git a/src/main/java/me/itzg/helpers/http/FetchBuilderBase.java b/src/main/java/me/itzg/helpers/http/FetchBuilderBase.java index 218a2655..71039a6c 100644 --- a/src/main/java/me/itzg/helpers/http/FetchBuilderBase.java +++ b/src/main/java/me/itzg/helpers/http/FetchBuilderBase.java @@ -1,12 +1,9 @@ package me.itzg.helpers.http; -import static io.netty.handler.codec.http.HttpHeaderNames.*; +import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT; +import static io.netty.handler.codec.http.HttpHeaderNames.AUTHORIZATION; +import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpStatusClass; -import io.netty.handler.codec.http.HttpUtil; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -25,11 +22,19 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; + +import org.slf4j.Logger; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpStatusClass; +import io.netty.handler.codec.http.HttpUtil; import lombok.extern.slf4j.Slf4j; import me.itzg.helpers.errors.GenericException; import me.itzg.helpers.http.SharedFetch.Options; import me.itzg.helpers.json.ObjectMappers; -import org.slf4j.Logger; import reactor.core.publisher.Mono; import reactor.netty.ByteBufMono; import reactor.netty.Connection; @@ -246,12 +251,14 @@ protected boolean notExpectedContentType(HttpClientResponse resp) { /** * CDN providers like bukkit don't support HEAD and respond with a 200 OK and redirect to a path like /error?aspxerrorpath=/projects/worldedit/files/latest + * Also, S3 object storage services generally don't support HEAD and respond with 403 Forbidden when trying to fetch an object. * @param resp the response to evaluate * @return true if this response to a HEAD seems to indicate it is not supported */ protected static boolean isHeadUnsupportedResponse(HttpClientResponse resp) { - return resp.path().equals("error") && - HttpUtil.getMimeType(resp.responseHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString())).equals("text/html"); + return resp.status().code() == 403 || + (resp.path().equals("error") && + HttpUtil.getMimeType(resp.responseHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString())).equals("text/html")); } protected Mono failedContentTypeMono(HttpClientResponse resp) { diff --git a/src/main/java/me/itzg/helpers/http/OutputToDirectoryFetchBuilder.java b/src/main/java/me/itzg/helpers/http/OutputToDirectoryFetchBuilder.java index 0cb5970e..bd815089 100644 --- a/src/main/java/me/itzg/helpers/http/OutputToDirectoryFetchBuilder.java +++ b/src/main/java/me/itzg/helpers/http/OutputToDirectoryFetchBuilder.java @@ -4,17 +4,19 @@ import static io.netty.handler.codec.http.HttpHeaderNames.LAST_MODIFIED; import static java.util.Objects.requireNonNull; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpResponseStatus; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; + +import org.jetbrains.annotations.NotNull; + +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.Setter; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; import me.itzg.helpers.files.ReactiveFileUtils; -import org.jetbrains.annotations.NotNull; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.netty.ByteBufFlux; @@ -75,14 +77,14 @@ public Mono assemble() { .head() .uri(uri()) .responseSingle((resp, bodyMono) -> { - if (notSuccess(resp)) { - return failedRequestMono(resp, bodyMono, "Extracting filename"); - } - if (isHeadUnsupportedResponse(resp)) { return assembleFileDownloadNameViaGet(client); } + if (notSuccess(resp)) { + return failedRequestMono(resp, bodyMono, "Extracting filename"); + } + final Path outputFile = outputDirectory.resolve(extractFilename(resp)); final Instant lastModified = respLastModified(resp);