Skip to content
Merged
Changes from 1 commit
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 @@ -24,6 +24,8 @@
import java.util.*;
import java.util.function.Function;

import org.jetbrains.annotations.Nullable;

@ServerType("valhalla")
public class ValhallaSkinServer implements SkinServer {

Expand Down Expand Up @@ -63,10 +65,6 @@ private URI buildBackendUserUri(UUID uuid) {
return buildBackendUri(String.format("user/%s", uuid.toString()));
}

private URI buildBackendHistoryUri(UUID uuid) {
return buildBackendUri(String.format("history/%s", uuid.toString()));
}

@Override
public Set<Feature> getFeatures() {
return FEATURES;
Expand All @@ -89,10 +87,25 @@ public boolean ownsUrl(String url) {
return false;
}

private static HttpRequest.Builder newHttpBuilder(URI uri) {
return newHttpBuilder(uri, null);
}

private static HttpRequest.Builder newHttpBuilder(URI uri, @Nullable String accessToken) {
var builder = HttpRequest.newBuilder(uri)
.header(FileTypes.HEADER_ACCEPT, FileTypes.APPLICATION_JSON)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent())
;
Comment thread
mattmess1221 marked this conversation as resolved.
Resolved
if (accessToken != null) {
builder.header(FileTypes.HEADER_AUTHORIZATION, accessToken);
}
return builder;
}

@Override
public TexturePayload loadSkins(GameProfile profile) throws IOException {
var path = buildBackendUserUri(profile.id());
return MoreHttpResponses.execute(HttpRequest.newBuilder(path)
return MoreHttpResponses.execute(newHttpBuilder(path)
.GET()
.timeout(Duration.ofSeconds(5))
.build())
Expand All @@ -103,11 +116,10 @@ public TexturePayload loadSkins(GameProfile profile) throws IOException {
@Override
public List<TexturePayload> loadSkins(Collection<GameProfile> profiles) throws IOException {
var data = new BulkTextures(profiles.stream().map(GameProfile::id).toList());
return MoreHttpResponses.execute(HttpRequest.newBuilder(buildBackendUri("bulk_textures"))
return MoreHttpResponses.execute(newHttpBuilder(buildBackendUri("bulk_textures"))
.POST(FileTypes.json(data))
.timeout(Duration.ofSeconds(10))
.header(FileTypes.HEADER_CONTENT_TYPE, FileTypes.APPLICATION_JSON)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent())
.build())
.requireOk()
.json(BulkTexturesResponse.class, "Invalid texture payload")
Expand All @@ -116,12 +128,10 @@ public List<TexturePayload> loadSkins(Collection<GameProfile> profiles) throws I

@Override
public TexturePayload loadSkins(Session session) throws IOException, AuthenticationException {
return doAuthorizedRequest(session, (accessToken) -> new TexturePayload(
return doAuthorizedRequest(session, (builder) -> new TexturePayload(
session.profile(),
MoreHttpResponses.execute(HttpRequest.newBuilder(buildBackendUri("textures"))
MoreHttpResponses.execute(builder.newHttpBuilder(buildBackendUri("textures"))
.GET()
.header(FileTypes.HEADER_AUTHORIZATION, accessToken)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent())
.build())
.requireOk()
.json(TexturePayload.Textures.class, "Invalid texture payload")
Expand All @@ -131,37 +141,29 @@ public TexturePayload loadSkins(Session session) throws IOException, Authenticat
@SuppressWarnings("deprecation")
@Override
public void uploadSkin(SkinUpload upload) throws IOException, AuthenticationException {
doAuthorizedRequest(upload.session(), (accessToken) -> switch (upload) {
doAuthorizedRequest(upload.session(), (builder) -> switch (upload) {
// TODO: (@Killjoy) Use namespaced ids and translate old unnamespaced to namespaced
case SkinUpload.Delete _ ->
MoreHttpResponses.execute(HttpRequest.newBuilder(buildBackendUri("textures", param("type", upload.type().getParameterizedName())))
MoreHttpResponses.execute(builder.newHttpBuilder(buildBackendUri("textures", param("type", upload.type().getParameterizedName())))
.DELETE()
.header(FileTypes.HEADER_AUTHORIZATION, accessToken)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent())
.build())
.requireOk();
case SkinUpload.FileUpload fileUpload ->
MoreHttpResponses.execute(FileTypes.multiPart()
.field("type", fileUpload.type().getParameterizedName())
.field("file", fileUpload.file())
.field("meta", new Gson().toJson(addChecksum(fileUpload.metadata(), fileUpload.file().toUri())))
.build(HttpRequest.newBuilder(buildBackendUri("textures"))::PUT)
.header(FileTypes.HEADER_ACCEPT, FileTypes.APPLICATION_JSON)
.header(FileTypes.HEADER_AUTHORIZATION, accessToken)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent())
.build(builder.newHttpBuilder(buildBackendUri("textures"))::PUT)
.build())
.requireOk();
case SkinUpload.UriUpload uriUpload ->
MoreHttpResponses.execute(HttpRequest.newBuilder(buildBackendUri("textures"))
MoreHttpResponses.execute(builder.newHttpBuilder(buildBackendUri("textures"))
.POST(FileTypes.json(Map.of(
"type", uriUpload.type().getParameterizedName(),
"file", uriUpload.uri().toString(),
"meta", addChecksum(uriUpload.metadata(), uriUpload.uri())
)))
.header(FileTypes.HEADER_CONTENT_TYPE, FileTypes.APPLICATION_JSON)
.header(FileTypes.HEADER_ACCEPT, FileTypes.APPLICATION_JSON)
.header(FileTypes.HEADER_AUTHORIZATION, accessToken)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent())
.build())
.requireOk();
});
Expand Down Expand Up @@ -199,9 +201,8 @@ public void authorize(Session session) throws IOException, AuthenticationExcepti

@Override
public Optional<SkinServerProfile<?>> loadProfile(Session session) throws IOException, AuthenticationException {
return MoreHttpResponses.execute(HttpRequest.newBuilder(buildBackendHistoryUri(session.profile().id()))
return doAuthorizedRequest(session, (builder) -> MoreHttpResponses.execute(builder.newHttpBuilder(buildBackendUri("history"))
.GET()
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent())
.build()).accept(r -> r.json(Textures.class, "Server sent invalid profile response")).map(p -> {
Function<SkinType, List<Texture>> textures = Util.memoize(type -> {
Set<String> visited = new HashSet<>();
Expand All @@ -223,30 +224,37 @@ public void setActive(SkinType type, Texture texture) throws IOException, Authen
uploadSkin(new SkinUpload.UriUpload(session, type, URI.create(texture.getUri()), texture.metadata));
}
};
});
}));
}

@FunctionalInterface
private interface RequestBuilderFactory {
HttpRequest.Builder newHttpBuilder(URI uri);
}

@FunctionalInterface
private interface AuthorizedRequest<T> {
T doRequest(String accessToken) throws IOException, AuthenticationException;
T doRequest(RequestBuilderFactory builder) throws IOException, AuthenticationException;
}

private <T> T doAuthorizedRequest(Session session, AuthorizedRequest<T> requester) throws IOException, AuthenticationException {
authorize(session);
RequestBuilderFactory builder = (uri) -> newHttpBuilder(uri, accessToken);
try {
return requester.doRequest(accessToken);
return requester.doRequest(builder);
} catch (HttpException e) {
if (e.getStatusCode() != 401) {
throw e;
}

accessToken = null;
authorize(session);
return requester.doRequest(accessToken);
return requester.doRequest(builder);
} catch (IOException e) {
if (e.getMessage().equals("Authorization failed")) {
accessToken = null;
authorize(session);
return requester.doRequest(accessToken);
return requester.doRequest(builder);
}

throw e;
Expand All @@ -256,9 +264,7 @@ private <T> T doAuthorizedRequest(Session session, AuthorizedRequest<T> requeste
private AuthHandshake authHandshake(String name) throws IOException {
return MoreHttpResponses.execute(FileTypes.multiPart()
.field("name", name)
.build(HttpRequest.newBuilder(buildBackendUri("auth/minecraft"))::POST)
.header(FileTypes.HEADER_ACCEPT, FileTypes.APPLICATION_JSON)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent()))
.build(newHttpBuilder(buildBackendUri("auth/minecraft"))::POST))
.requireOk()
.json(AuthHandshake.class, "Invalid handshake response");
}
Expand All @@ -267,9 +273,7 @@ private AuthResponse authResponse(String name, long verifyToken) throws IOExcept
return MoreHttpResponses.execute(FileTypes.multiPart()
.field("name", name)
.field("verifyToken", verifyToken)
.build(HttpRequest.newBuilder(buildBackendUri("auth/minecraft/callback"))::POST)
.header(FileTypes.HEADER_ACCEPT, FileTypes.APPLICATION_JSON)
.header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent()))
.build(newHttpBuilder(buildBackendUri("auth/minecraft/callback"))::POST))
.requireOk()
.json(AuthResponse.class, "Invalid auth response");
}
Expand Down
Loading