diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java
index b9df3067..10662436 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/HDSkinManager.java
@@ -25,10 +25,8 @@ import com.voxelmodpack.hdskins.resources.SkinResourceManager;
 import com.voxelmodpack.hdskins.resources.TextureLoader;
 import com.voxelmodpack.hdskins.resources.texture.ImageBufferDownloadHD;
 import com.voxelmodpack.hdskins.server.SkinServer;
-import com.voxelmodpack.hdskins.util.CallableFutures;
-import com.voxelmodpack.hdskins.util.MoreStreams;
-import com.voxelmodpack.hdskins.util.PlayerUtil;
-import com.voxelmodpack.hdskins.util.ProfileTextureUtil;
+import com.voxelmodpack.hdskins.util.*;
+
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.entity.AbstractClientPlayer;
 import net.minecraft.client.gui.GuiButton;
@@ -45,8 +43,6 @@ import net.minecraft.init.Items;
 import net.minecraft.item.ItemStack;
 import net.minecraft.util.ResourceLocation;
 import org.apache.commons.io.FileUtils;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -77,7 +73,6 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
 
     public static final ExecutorService skinUploadExecutor = Executors.newSingleThreadExecutor();
     public static final ExecutorService skinDownloadExecutor = Executors.newFixedThreadPool(8);
-    public static final CloseableHttpClient httpClient = HttpClients.createSystem();
 
     public static final HDSkinManager INSTANCE = new HDSkinManager();
 
@@ -127,7 +122,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
 
             for (SkinServer server : skinServers) {
                 try {
-                    if (!server.supportsFeature(Feature.SYNTHETIC)) {
+                    if (!server.getFeatures().contains(Feature.SYNTHETIC)) {
                         server.loadProfileData(profile).getTextures().forEach(textureMap::putIfAbsent);
                         if (textureMap.size() == Type.values().length) {
                             break;
@@ -149,7 +144,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
             Property textures = Iterables.getFirst(profile.getProperties().get("textures"), null);
             if (textures != null) {
                 String json = new String(Base64.getDecoder().decode(textures.getValue()), StandardCharsets.UTF_8);
-                MinecraftTexturesPayload texturePayload = SkinServer.gson.fromJson(json, MinecraftTexturesPayload.class);
+                MinecraftTexturesPayload texturePayload = MoreHttpResponses.GSON.fromJson(json, MinecraftTexturesPayload.class);
                 if (texturePayload != null) {
                     // name is optional
                     String name = texturePayload.getProfileName();
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/SkinChooser.java b/src/hdskins/java/com/voxelmodpack/hdskins/SkinChooser.java
index 4bcabb16..610b08b6 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/SkinChooser.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/SkinChooser.java
@@ -3,16 +3,14 @@ package com.voxelmodpack.hdskins;
 import com.voxelmodpack.hdskins.upload.IFileDialog;
 import com.voxelmodpack.hdskins.upload.ThreadOpenFilePNG;
 import com.voxelmodpack.hdskins.upload.ThreadSaveFilePNG;
-import com.voxelmodpack.hdskins.util.MoreHttpResponses;
 
 import net.minecraft.client.Minecraft;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.FilenameUtils;
+import org.apache.logging.log4j.LogManager;
 
 import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
+import java.io.*;
 import javax.annotation.Nullable;
 import javax.imageio.ImageIO;
 import javax.swing.UIManager;
@@ -73,12 +71,10 @@ public class SkinChooser {
     public void openSavePNG(Minecraft mc, String title) {
         openFileThread = new ThreadSaveFilePNG(mc, title, mc.getSession().getUsername() + ".png", (file, dialogResult) -> {
             if (dialogResult == 0) {
-                try (MoreHttpResponses response = uploader.downloadSkin().get()) {
-                    if (response.ok()) {
-                        FileUtils.copyInputStreamToFile(response.getInputStream(), file);
-                    }
-                } catch (IOException | InterruptedException | ExecutionException e) {
-                    e.printStackTrace();
+                try (InputStream response = uploader.getServerTexture().openStream()) {
+                    FileUtils.copyInputStreamToFile(response, file);
+                } catch (IOException e) {
+                    LogManager.getLogger().error("Failed to save remote skin.", e);
                 }
             }
             openFileThread = null;
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java b/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java
index 60b144df..93ccbdff 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java
@@ -18,20 +18,16 @@ import com.mojang.authlib.minecraft.MinecraftProfileTexture;
 import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
 import com.voxelmodpack.hdskins.gui.EntityPlayerModel;
 import com.voxelmodpack.hdskins.gui.Feature;
+import com.voxelmodpack.hdskins.resources.PreviewTexture;
 import com.voxelmodpack.hdskins.resources.PreviewTextureManager;
 import com.voxelmodpack.hdskins.server.HttpException;
 import com.voxelmodpack.hdskins.server.SkinServer;
 import com.voxelmodpack.hdskins.server.SkinUpload;
-import com.voxelmodpack.hdskins.util.MoreHttpResponses;
-import com.voxelmodpack.hdskins.util.NetClient;
 
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.CompletableFuture;
 
 public class SkinUploader implements Closeable {
@@ -104,8 +100,8 @@ public class SkinUploader implements Closeable {
         return gateway == null ? "" : gateway.toString();
     }
 
-    public boolean supportsFeature(Feature feature) {
-        return gateway != null && gateway.supportsFeature(feature);
+    public Set<Feature> getFeatures() {
+        return gateway == null ? Collections.emptySet() : gateway.getFeatures();
     }
 
     protected void setError(String er) {
@@ -196,10 +192,8 @@ public class SkinUploader implements Closeable {
         }, HDSkinManager.skinUploadExecutor).thenRunAsync(this::fetchRemote);
     }
 
-    public CompletableFuture<MoreHttpResponses> downloadSkin() {
-        String loc = remotePlayer.getLocal(skinType).getRemote().getUrl();
-
-        return new NetClient("GET", loc).async(HDSkinManager.skinDownloadExecutor);
+    public PreviewTexture getServerTexture() {
+        return remotePlayer.getLocal(skinType).getRemote();
     }
 
     protected void fetchRemote() {
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java
index b58ca283..7ad4d2bc 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/gui/GuiSkins.java
@@ -509,12 +509,12 @@ public class GuiSkins extends GameGui implements ISkinUploadHandler {
 
     private void updateButtons() {
         btnClear.enabled = uploader.canClear();
-        btnUpload.enabled = uploader.canUpload() && uploader.supportsFeature(Feature.UPLOAD_USER_SKIN);
+        btnUpload.enabled = uploader.canUpload() && uploader.getFeatures().contains(Feature.UPLOAD_USER_SKIN);
         btnDownload.enabled = uploader.canClear() && !chooser.pickingInProgress();
         btnBrowse.enabled = !chooser.pickingInProgress();
 
-        boolean types = !uploader.supportsFeature(Feature.MODEL_TYPES);
-        boolean variants = !uploader.supportsFeature(Feature.MODEL_VARIANTS);
+        boolean types = !uploader.getFeatures().contains(Feature.MODEL_TYPES);
+        boolean variants = !uploader.getFeatures().contains(Feature.MODEL_VARIANTS);
 
         btnModeSkin.setLocked(types);
         btnModeElytra.setLocked(types);
@@ -522,9 +522,9 @@ public class GuiSkins extends GameGui implements ISkinUploadHandler {
         btnModeSteve.setLocked(variants);
         btnModeAlex.setLocked(variants);
 
-        btnClear.setLocked(!uploader.supportsFeature(Feature.DELETE_USER_SKIN));
-        btnUpload.setLocked(!uploader.supportsFeature(Feature.UPLOAD_USER_SKIN));
-        btnDownload.setLocked(!uploader.supportsFeature(Feature.DOWNLOAD_USER_SKIN));
+        btnClear.setLocked(!uploader.getFeatures().contains(Feature.DELETE_USER_SKIN));
+        btnUpload.setLocked(!uploader.getFeatures().contains(Feature.UPLOAD_USER_SKIN));
+        btnDownload.setLocked(!uploader.getFeatures().contains(Feature.DOWNLOAD_USER_SKIN));
     }
 
     protected class FeatureButton extends Button {
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/resources/PreviewTexture.java b/src/hdskins/java/com/voxelmodpack/hdskins/resources/PreviewTexture.java
index dd25aa9a..3cce99e2 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/resources/PreviewTexture.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/resources/PreviewTexture.java
@@ -6,6 +6,10 @@ import net.minecraft.util.ResourceLocation;
 
 import com.voxelmodpack.hdskins.VanillaModels;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
 import javax.annotation.Nullable;
 
 public class PreviewTexture extends ThreadDownloadImageData {
@@ -27,6 +31,10 @@ public class PreviewTexture extends ThreadDownloadImageData {
         return uploaded && this.getGlTextureId() > -1;
     }
 
+    public InputStream openStream() throws IOException {
+        return new URL(fileUrl).openStream();
+    }
+
     public String getUrl() {
         return fileUrl;
     }
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/resources/SkinData.java b/src/hdskins/java/com/voxelmodpack/hdskins/resources/SkinData.java
deleted file mode 100644
index 348ed2cc..00000000
--- a/src/hdskins/java/com/voxelmodpack/hdskins/resources/SkinData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.voxelmodpack.hdskins.resources;
-
-import net.minecraft.util.ResourceLocation;
-
-import java.util.List;
-import java.util.UUID;
-
-@SuppressWarnings("unused")
-class SkinData {
-
-    List<Skin> skins;
-}
-
-@SuppressWarnings("unused")
-class Skin {
-
-    String name;
-    UUID uuid;
-    String skin;
-
-    public ResourceLocation getTexture() {
-        return new ResourceLocation("hdskins", String.format("textures/skins/%s.png", skin));
-    }
-}
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/resources/SkinResourceManager.java b/src/hdskins/java/com/voxelmodpack/hdskins/resources/SkinResourceManager.java
index d894617f..31b9b611 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/resources/SkinResourceManager.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/resources/SkinResourceManager.java
@@ -6,6 +6,7 @@ import com.google.gson.JsonParseException;
 import com.mojang.authlib.GameProfile;
 import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
 import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
+
 import net.minecraft.client.resources.IResource;
 import net.minecraft.client.resources.IResourceManager;
 import net.minecraft.client.resources.IResourceManagerReloadListener;
@@ -17,8 +18,7 @@ import javax.annotation.Nullable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -126,4 +126,17 @@ public class SkinResourceManager implements IResourceManagerReloadListener {
         return skin;
     }
 
+    public static class SkinData {
+        List<Skin> skins;
+    }
+
+    public static class Skin {
+        String name;
+        UUID uuid;
+        String skin;
+
+        public ResourceLocation getTexture() {
+            return new ResourceLocation("hdskins", String.format("textures/skins/%s.png", skin));
+        }
+    }
 }
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/resources/texture/ThreadDownloadImageETag.java b/src/hdskins/java/com/voxelmodpack/hdskins/resources/texture/ThreadDownloadImageETag.java
index 2728877b..c3a64921 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/resources/texture/ThreadDownloadImageETag.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/resources/texture/ThreadDownloadImageETag.java
@@ -197,13 +197,13 @@ public class ThreadDownloadImageETag extends SimpleTexture implements IBufferedT
             if (resp.ok()) {
                 // write the image to disk
                 Files.createDirectories(cacheFile.getParent());
-                Files.copy(resp.getInputStream(), cacheFile);
+                Files.copy(resp.inputStream(), cacheFile);
 
                 try (InputStream in = Files.newInputStream(cacheFile)) {
                     BufferedImage bufferedimage = ImageIO.read(in);
 
                     // maybe write the etag to disk
-                    Header eTag = resp.getResponse().getFirstHeader(HttpHeaders.ETAG);
+                    Header eTag = resp.response().getFirstHeader(HttpHeaders.ETAG);
                     if (eTag != null) {
                         Files.write(eTagFile, Collections.singleton(eTag.getValue()));
                     }
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java
index 25d13818..c63c73fb 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java
@@ -1,6 +1,7 @@
 package com.voxelmodpack.hdskins.server;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.gson.annotations.Expose;
 import com.mojang.authlib.GameProfile;
@@ -13,8 +14,7 @@ import com.voxelmodpack.hdskins.util.MoreHttpResponses;
 import com.voxelmodpack.hdskins.util.NetClient;
 
 import java.io.IOException;
-import java.util.Locale;
-import java.util.Map;
+import java.util.*;
 
 @Deprecated
 @ServerType("bethlehem")
@@ -22,6 +22,14 @@ public class BethlehemSkinServer implements SkinServer {
 
     private static final String SERVER_ID = "7853dfddc358333843ad55a2c7485c4aa0380a51";
 
+    private static final Set<Feature> FEATURES = Sets.newHashSet(
+            Feature.UPLOAD_USER_SKIN,
+            Feature.DOWNLOAD_USER_SKIN,
+            Feature.MODEL_VARIANTS,
+            Feature.MODEL_TYPES,
+            Feature.LINK_PROFILE
+    );
+
     @Expose
     private final String address;
 
@@ -29,14 +37,19 @@ public class BethlehemSkinServer implements SkinServer {
         this.address = address;
     }
 
+    @Override
+    public Set<Feature> getFeatures() {
+        return FEATURES;
+    }
+
     @Override
     public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException {
         try (MoreHttpResponses response = new NetClient("GET", getPath(profile)).send()) {
             if (!response.ok()) {
-                throw new HttpException(response.getResponse());
+                throw new HttpException(response.response());
             }
 
-            return response.json(MinecraftTexturesPayload.class);
+            return response.unwrapAsJson(MinecraftTexturesPayload.class);
         }
     }
 
@@ -85,17 +98,4 @@ public class BethlehemSkinServer implements SkinServer {
                 .append("address", address)
                 .build();
     }
-
-    @Override
-    public boolean supportsFeature(Feature feature) {
-        switch (feature) {
-            case DOWNLOAD_USER_SKIN:
-            case UPLOAD_USER_SKIN:
-            case MODEL_VARIANTS:
-            case MODEL_TYPES:
-            case LINK_PROFILE:
-                return true;
-            default: return false;
-        }
-    }
 }
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java
index b430f190..b8d9d3b0 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java
@@ -2,6 +2,7 @@ package com.voxelmodpack.hdskins.server;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.gson.annotations.Expose;
 import com.mojang.authlib.GameProfile;
@@ -9,9 +10,7 @@ import com.mojang.authlib.exceptions.AuthenticationException;
 import com.mojang.authlib.minecraft.MinecraftProfileTexture;
 import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
 import com.mojang.util.UUIDTypeAdapter;
-import com.voxelmodpack.hdskins.HDSkinManager;
 import com.voxelmodpack.hdskins.gui.Feature;
-import com.voxelmodpack.hdskins.util.CallableFutures;
 import com.voxelmodpack.hdskins.util.IndentedToStringStyle;
 import com.voxelmodpack.hdskins.util.MoreHttpResponses;
 import com.voxelmodpack.hdskins.util.NetClient;
@@ -25,11 +24,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.io.IOException;
-import java.util.Date;
-import java.util.EnumMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
+import java.util.*;
 import javax.annotation.Nullable;
 
 @Deprecated
@@ -38,6 +33,12 @@ public class LegacySkinServer implements SkinServer {
 
     private static final String SERVER_ID = "7853dfddc358333843ad55a2c7485c4aa0380a51";
 
+    private static final Set<Feature> FEATURES = Sets.newHashSet(
+            Feature.DOWNLOAD_USER_SKIN,
+            Feature.UPLOAD_USER_SKIN,
+            Feature.DELETE_USER_SKIN
+    );
+
     private static final Logger logger = LogManager.getLogger();
 
     @Expose
@@ -88,13 +89,13 @@ public class LegacySkinServer implements SkinServer {
     }
 
     private MinecraftProfileTexture loadProfileTexture(GameProfile profile, String url) throws IOException {
-        try (MoreHttpResponses resp = MoreHttpResponses.execute(HDSkinManager.httpClient, new HttpHead(url))) {
+        try (MoreHttpResponses resp = MoreHttpResponses.execute(HTTP_CLIENT, new HttpHead(url))) {
             if (!resp.ok()) {
-                throw new HttpException(resp.getResponse());
+                throw new HttpException(resp.response());
             }
             logger.debug("Found skin for {} at {}", profile.getName(), url);
 
-            Header eTagHeader = resp.getResponse().getFirstHeader(HttpHeaders.ETAG);
+            Header eTagHeader = resp.response().getFirstHeader(HttpHeaders.ETAG);
             final String eTag = eTagHeader == null ? "" : StringUtils.strip(eTagHeader.getValue(), "\"");
 
             // Add the ETag onto the end of the texture hash. Should properly cache the textures.
@@ -131,7 +132,7 @@ public class LegacySkinServer implements SkinServer {
         }
 
         if (!response.equalsIgnoreCase("OK") && !response.endsWith("OK")) {
-            throw new HttpException(response, resp.getResponseCode(), null);
+            throw new HttpException(response, resp.responseCode(), null);
         }
     }
 
@@ -159,14 +160,8 @@ public class LegacySkinServer implements SkinServer {
     }
 
     @Override
-    public boolean supportsFeature(Feature feature) {
-        switch (feature) {
-            case DOWNLOAD_USER_SKIN:
-            case UPLOAD_USER_SKIN:
-            case DELETE_USER_SKIN:
-                return true;
-            default: return false;
-        }
+    public Set<Feature> getFeatures() {
+        return FEATURES;
     }
 
     @Override
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java
index 41225714..0eacd351 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java
@@ -1,30 +1,26 @@
 package com.voxelmodpack.hdskins.server;
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
 import com.mojang.authlib.GameProfile;
 import com.mojang.authlib.exceptions.AuthenticationException;
 import com.mojang.authlib.minecraft.MinecraftSessionService;
 import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
-import com.mojang.util.UUIDTypeAdapter;
 import com.mumfrey.liteloader.modconfig.Exposable;
 import com.voxelmodpack.hdskins.gui.Feature;
 import net.minecraft.client.Minecraft;
 import net.minecraft.util.Session;
 
 import java.io.IOException;
-import java.util.UUID;
+import java.util.Set;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
 
 public interface SkinServer extends Exposable {
-
-    Gson gson = new GsonBuilder()
-            .registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
-            .create();
+    CloseableHttpClient HTTP_CLIENT = HttpClients.createSystem();
 
     /**
-     * Returns true for any features that this skin server supports.
+     * Returns the set of features that this skin server supports.
      */
-    boolean supportsFeature(Feature feature);
+    Set<Feature> getFeatures();
 
     /**
      * Synchronously loads texture information for the provided profile.
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServerSerializer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServerSerializer.java
index 6029b9b4..5f74dd08 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServerSerializer.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServerSerializer.java
@@ -27,6 +27,7 @@ public class SkinServerSerializer implements JsonSerializer<SkinServer>, JsonDes
         // register default skin server types
         addSkinServerType(ValhallaSkinServer.class);
         addSkinServerType(YggdrasilSkinServer.class);
+        addSkinServerType(BethlehemSkinServer.class);
         addSkinServerType(LegacySkinServer.class);
     }
 
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java
index b2ef5b6f..cc0cbed7 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java
@@ -1,13 +1,13 @@
 package com.voxelmodpack.hdskins.server;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
 import com.google.gson.annotations.Expose;
 import com.mojang.authlib.GameProfile;
 import com.mojang.authlib.exceptions.AuthenticationException;
 import com.mojang.authlib.minecraft.MinecraftProfileTexture;
 import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
 import com.mojang.util.UUIDTypeAdapter;
-import com.voxelmodpack.hdskins.HDSkinManager;
 import com.voxelmodpack.hdskins.gui.Feature;
 import com.voxelmodpack.hdskins.util.IndentedToStringStyle;
 import com.voxelmodpack.hdskins.util.MoreHttpResponses;
@@ -23,13 +23,20 @@ import org.apache.http.entity.mime.MultipartEntityBuilder;
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
-import java.util.Locale;
-import java.util.UUID;
+import java.util.*;
 
 @ServerType("valhalla")
 public class ValhallaSkinServer implements SkinServer {
     private static final String API_PREFIX = "/api/v1";
 
+    private static final Set<Feature> FEATURES = Sets.newHashSet(
+            Feature.DOWNLOAD_USER_SKIN,
+            Feature.UPLOAD_USER_SKIN,
+            Feature.DELETE_USER_SKIN,
+            Feature.MODEL_VARIANTS,
+            Feature.MODEL_TYPES
+    );
+
     @Expose
     private final String address;
 
@@ -48,13 +55,13 @@ public class ValhallaSkinServer implements SkinServer {
 
     @Override
     public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException, AuthenticationException {
-        try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, new HttpGet(getTexturesURI(profile)))) {
+        try (MoreHttpResponses response = MoreHttpResponses.execute(HTTP_CLIENT, new HttpGet(getTexturesURI(profile)))) {
 
             if (response.ok()) {
                 return response.unwrapAsJson(MinecraftTexturesPayload.class);
             }
 
-            throw new HttpException(response.getResponse());
+            throw new HttpException(response.response());
         }
     }
 
@@ -123,7 +130,7 @@ public class ValhallaSkinServer implements SkinServer {
     }
 
     private void upload(HttpUriRequest request) throws IOException {
-        try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, request)) {
+        try (MoreHttpResponses response = MoreHttpResponses.execute(HTTP_CLIENT, request)) {
             if (!response.ok()) {
                 throw response.exception();
             }
@@ -152,7 +159,7 @@ public class ValhallaSkinServer implements SkinServer {
     }
 
     private AuthHandshake authHandshake(String name) throws IOException {
-        try (MoreHttpResponses resp = MoreHttpResponses.execute(HDSkinManager.httpClient, RequestBuilder.post()
+        try (MoreHttpResponses resp = MoreHttpResponses.execute(HTTP_CLIENT, RequestBuilder.post()
                 .setUri(getHandshakeURI())
                 .addParameter("name", name)
                 .build())) {
@@ -161,7 +168,7 @@ public class ValhallaSkinServer implements SkinServer {
     }
 
     private AuthResponse authResponse(String name, long verifyToken) throws IOException {
-        try (MoreHttpResponses resp = MoreHttpResponses.execute(HDSkinManager.httpClient, RequestBuilder.post()
+        try (MoreHttpResponses resp = MoreHttpResponses.execute(HTTP_CLIENT, RequestBuilder.post()
                 .setUri(getResponseURI())
                 .addParameter("name", name)
                 .addParameter("verifyToken", String.valueOf(verifyToken))
@@ -190,16 +197,8 @@ public class ValhallaSkinServer implements SkinServer {
     }
 
     @Override
-    public boolean supportsFeature(Feature feature) {
-        switch (feature) {
-            case DOWNLOAD_USER_SKIN:
-            case UPLOAD_USER_SKIN:
-            case DELETE_USER_SKIN:
-            case MODEL_VARIANTS:
-            case MODEL_TYPES:
-                return true;
-            default: return false;
-        }
+    public Set<Feature> getFeatures() {
+        return FEATURES;
     }
 
     @Override
@@ -211,7 +210,6 @@ public class ValhallaSkinServer implements SkinServer {
 
     @SuppressWarnings("WeakerAccess")
     private static class AuthHandshake {
-
         private boolean offline;
         private String serverId;
         private long verifyToken;
@@ -219,9 +217,7 @@ public class ValhallaSkinServer implements SkinServer {
 
     @SuppressWarnings("WeakerAccess")
     private static class AuthResponse {
-
         private String accessToken;
         private UUID userId;
-
     }
 }
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/YggdrasilSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/YggdrasilSkinServer.java
index bea947c8..82f6ff4d 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/server/YggdrasilSkinServer.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/YggdrasilSkinServer.java
@@ -39,7 +39,8 @@ public class YggdrasilSkinServer implements SkinServer {
             Feature.DOWNLOAD_USER_SKIN,
             Feature.DELETE_USER_SKIN,
             Feature.MODEL_VARIANTS,
-            Feature.MODEL_TYPES);
+            Feature.MODEL_TYPES
+    );
 
     private transient final String address = "https://api.mojang.com";
     private transient final String verify = "https://authserver.mojang.com/validate";
@@ -47,8 +48,8 @@ public class YggdrasilSkinServer implements SkinServer {
     private transient final boolean requireSecure = true;
 
     @Override
-    public boolean supportsFeature(Feature feature) {
-        return FEATURES.contains(feature);
+    public Set<Feature> getFeatures() {
+        return FEATURES;
     }
 
     @Override
@@ -142,7 +143,7 @@ public class YggdrasilSkinServer implements SkinServer {
     }
 
     private void send(RequestBuilder request) throws IOException {
-        try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, request.build())) {
+        try (MoreHttpResponses response = MoreHttpResponses.execute(HTTP_CLIENT, request.build())) {
             if (!response.ok()) {
                 throw new IOException(response.json(ErrorResponse.class, "Server error wasn't in json: {}").toString());
             }
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/util/CallableFutures.java b/src/hdskins/java/com/voxelmodpack/hdskins/util/CallableFutures.java
index 11112f8e..4b9abeda 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/util/CallableFutures.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/util/CallableFutures.java
@@ -27,12 +27,6 @@ public class CallableFutures {
         return ret;
     }
 
-    public static <T> CompletableFuture<T> failedFuture(Exception e) {
-        CompletableFuture<T> ret = new CompletableFuture<>();
-        ret.completeExceptionally(e);
-        return ret;
-    }
-
     public static <T> BiFunction<? super T, Throwable, Void> callback(Runnable c) {
         return (o, t) -> {
             if (t != null) {
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java b/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java
index 6baadb9f..f27c089a 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java
@@ -5,9 +5,7 @@ import com.google.common.io.CharStreams;
 import com.google.gson.*;
 import com.mojang.util.UUIDTypeAdapter;
 import com.voxelmodpack.hdskins.HDSkinManager;
-import com.voxelmodpack.hdskins.server.SkinServer;
 
-import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpStatus;
 import org.apache.http.NameValuePair;
@@ -35,72 +33,56 @@ public interface MoreHttpResponses extends AutoCloseable {
             .registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
             .create();
 
-    CloseableHttpResponse getResponse();
+    CloseableHttpResponse response();
 
     default boolean ok() {
-        return getResponseCode() == HttpStatus.SC_OK;
+        return responseCode() < HttpStatus.SC_MULTIPLE_CHOICES;
     }
 
     default boolean json() {
         return "application/json".contentEquals(contentType().getMimeType());
     }
 
-    default int getResponseCode() {
-        return getResponse().getStatusLine().getStatusCode();
+    default int responseCode() {
+        return response().getStatusLine().getStatusCode();
     }
 
-    default Optional<HttpEntity> getEntity() {
-        return Optional.ofNullable(getResponse().getEntity());
+    default Optional<HttpEntity> entity() {
+        return Optional.ofNullable(response().getEntity());
     }
 
     default ContentType contentType() {
-        return getEntity()
+        return entity()
                 .map(ContentType::get)
                 .orElse(ContentType.DEFAULT_TEXT);
     }
 
-    default String getContentType() {
-        return getEntity().map(HttpEntity::getContentType).map(Header::getValue).orElse("text/plain");
+    default InputStream inputStream() throws IOException {
+        return response().getEntity().getContent();
     }
 
-    default InputStream getInputStream() throws IOException {
-        return getResponse().getEntity().getContent();
-    }
-
-    default BufferedReader getReader() throws IOException {
-        return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
+    default BufferedReader reader() throws IOException {
+        return new BufferedReader(new InputStreamReader(inputStream(), StandardCharsets.UTF_8));
     }
 
     default byte[] bytes() throws IOException {
-        try (InputStream input = getInputStream()) {
+        try (InputStream input = inputStream()) {
             return ByteStreams.toByteArray(input);
         }
     }
 
     default String text() throws IOException {
-        try (BufferedReader reader = getReader()) {
+        try (BufferedReader reader = reader()) {
             return CharStreams.toString(reader);
         }
     }
 
     default Stream<String> lines() throws IOException {
-        try (BufferedReader reader = getReader()) {
+        try (BufferedReader reader = reader()) {
             return reader.lines();
         }
     }
 
-    default <T> T json(Class<T> type) throws IOException {
-        try (BufferedReader reader = getReader()) {
-            return SkinServer.gson.fromJson(reader, type);
-        }
-    }
-
-    default <T> T json(Type type) throws IOException {
-        try (BufferedReader reader = getReader()) {
-            return SkinServer.gson.fromJson(reader, type);
-        }
-    }
-
     default <T> T json(Class<T> type, String errorMessage) throws IOException {
         return json((Type)type, errorMessage);
     }
@@ -112,18 +94,14 @@ public interface MoreHttpResponses extends AutoCloseable {
             throw new IOException(text);
         }
 
-        try (BufferedReader reader = getReader()) {
+        try (BufferedReader reader = reader()) {
             return GSON.fromJson(reader, type);
         }
     }
 
     default <T> T unwrapAsJson(Type type) throws IOException {
-        if (!"application/json".equals(getContentType())) {
-            throw new IOException("Server returned a non-json response!");
-        }
-
         if (ok()) {
-            return json(type);
+            return json(type, "Server returned a non-json response!");
         }
 
         throw exception();
@@ -135,7 +113,7 @@ public interface MoreHttpResponses extends AutoCloseable {
 
     @Override
     default void close() throws IOException {
-        this.getResponse().close();
+        response().close();
     }
 
     static MoreHttpResponses execute(CloseableHttpClient client, HttpUriRequest request) throws IOException {
@@ -150,4 +128,4 @@ public interface MoreHttpResponses extends AutoCloseable {
                 )
                 .toArray(NameValuePair[]::new);
     }
-}
+}
\ No newline at end of file
diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/util/NetClient.java b/src/hdskins/java/com/voxelmodpack/hdskins/util/NetClient.java
index a479d198..71a6ec38 100644
--- a/src/hdskins/java/com/voxelmodpack/hdskins/util/NetClient.java
+++ b/src/hdskins/java/com/voxelmodpack/hdskins/util/NetClient.java
@@ -1,6 +1,7 @@
 package com.voxelmodpack.hdskins.util;
 
-import com.voxelmodpack.hdskins.HDSkinManager;
+import com.voxelmodpack.hdskins.server.SkinServer;
+
 import org.apache.http.HttpEntity;
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.client.methods.RequestBuilder;
@@ -17,6 +18,7 @@ import java.util.concurrent.Executor;
 /**
  * Ew. Why so many builders? >.<
  */
+@Deprecated
 public class NetClient {
 
     private final RequestBuilder rqBuilder;
@@ -71,7 +73,7 @@ public class NetClient {
             }
         }
 
-        return MoreHttpResponses.execute(HDSkinManager.httpClient, request);
+        return MoreHttpResponses.execute(SkinServer.HTTP_CLIENT, request);
     }
 
     public CompletableFuture<MoreHttpResponses> async(Executor exec) {
diff --git a/src/main/java/com/minelittlepony/MineLittlePony.java b/src/main/java/com/minelittlepony/MineLittlePony.java
index b8cdbf40..6bd948ca 100644
--- a/src/main/java/com/minelittlepony/MineLittlePony.java
+++ b/src/main/java/com/minelittlepony/MineLittlePony.java
@@ -8,9 +8,6 @@ import com.minelittlepony.pony.data.PonyManager;
 import com.minelittlepony.render.skull.PonySkullRenderer;
 import com.mumfrey.liteloader.core.LiteLoader;
 import com.voxelmodpack.hdskins.HDSkinManager;
-import com.voxelmodpack.hdskins.server.LegacySkinServer;
-import com.voxelmodpack.hdskins.server.SkinServer;
-import com.voxelmodpack.hdskins.server.ValhallaSkinServer;
 
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.renderer.entity.RenderManager;
diff --git a/src/main/java/com/minelittlepony/model/ponies/ModelBreezie.java b/src/main/java/com/minelittlepony/model/ponies/ModelBreezie.java
index 079b62f6..238691bd 100644
--- a/src/main/java/com/minelittlepony/model/ponies/ModelBreezie.java
+++ b/src/main/java/com/minelittlepony/model/ponies/ModelBreezie.java
@@ -75,7 +75,6 @@ public class ModelBreezie extends ModelBiped {
         rightWing.render(scale);
     }
 
-    @SuppressWarnings("incomplete-switch")
     @Override
     public void setRotationAngles(float move, float swing, float ticks, float headYaw, float headPitch, float scale, Entity entity) {