From c2535f811f500690d0e6d34ce641a78eea7a74b5 Mon Sep 17 00:00:00 2001 From: Matchu Date: Sat, 19 Jun 2021 15:41:52 -0700 Subject: [PATCH] Remove proxy for most images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've noticed that our Fastly proxy adds a surprising amount of latency on cache misses (500-1000ms). And, while our overall hit ratio of 80% is pretty good, most misses happen at inopportune times, like loading items from search. But now that the Neopets CDN supports HTTPS, we can safely switch back to theirs for *most* image loads. (Some features, like downloads and movies, still require CORS headers, which our proxy is still reponsible for adding.) This forgoes some minor performance wins (like the Download button now requires separate network requests), and some potential filesize reduction opportunities (like Fastly's auto-gzip which we're today using for SVGs, and eventually using their Image Optimizer for assets), to decrease latency. We could still potentially do something more powerful for low-power connections someday… but for now, with the cache miss latency being *so* heavy, this seems like the clear win for almost certainly *all* users today. --- src/app/WardrobePage/OutfitControls.js | 8 ++++++- src/app/components/OutfitMovieLayer.js | 4 +++- src/app/components/OutfitPreview.js | 32 +++++++++++--------------- src/app/util.js | 12 +++++++--- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/app/WardrobePage/OutfitControls.js b/src/app/WardrobePage/OutfitControls.js index 4326334..2d81b2c 100644 --- a/src/app/WardrobePage/OutfitControls.js +++ b/src/app/WardrobePage/OutfitControls.js @@ -607,7 +607,13 @@ function useDownloadableImage(visibleLayers) { // performance (can use cached SVG), and predictability (image will // look like what you see here). const imagePromises = visibleLayers.map((layer) => - loadImage(getBestImageUrlForLayer(layer, { hiResMode })) + loadImage({ + src: getBestImageUrlForLayer(layer, { + hiResMode, + crossOrigin: "anonymous", + }), + crossOrigin: "anonymous", + }) ); let images; diff --git a/src/app/components/OutfitMovieLayer.js b/src/app/components/OutfitMovieLayer.js index 61a2991..5ab45e8 100644 --- a/src/app/components/OutfitMovieLayer.js +++ b/src/app/components/OutfitMovieLayer.js @@ -290,7 +290,9 @@ export async function loadMovieLibrary(librarySrc) { library.properties.manifest.map(({ id, src }) => [ id, loadImage({ - src: safeImageUrl(librarySrcDir + "/" + src), + src: safeImageUrl(librarySrcDir + "/" + src, { + crossOrigin: "anonymous", + }), crossOrigin: "anonymous", }), ]) diff --git a/src/app/components/OutfitPreview.js b/src/app/components/OutfitPreview.js index 08af37d..ab12df8 100644 --- a/src/app/components/OutfitPreview.js +++ b/src/app/components/OutfitPreview.js @@ -262,16 +262,7 @@ export function OutfitLayers({ ) : ( tags are always allowed through CORS), but - // this means we make the same request that the Download - // button makes, so it can use the cached version of this - // image instead of requesting it again with crossOrigin! - crossOrigin={ - getBestImageUrlForLayer(layer, { hiResMode }) - .crossOrigin - } + src={getBestImageUrlForLayer(layer, { hiResMode })} alt="" objectFit="contain" maxWidth="100%" @@ -341,11 +332,14 @@ export function FullScreenCenter({ children, ...otherProps }) { ); } -export function getBestImageUrlForLayer(layer, { hiResMode = false } = {}) { +export function getBestImageUrlForLayer( + layer, + { hiResMode = false, crossOrigin = null } = {} +) { if (hiResMode && layer.svgUrl) { - return { src: safeImageUrl(layer.svgUrl), crossOrigin: "anonymous" }; + return safeImageUrl(layer.svgUrl, { crossOrigin }); } else { - return { src: safeImageUrl(layer.imageUrl), crossOrigin: "anonymous" }; + return safeImageUrl(layer.imageUrl, { crossOrigin }); } } @@ -392,12 +386,12 @@ export function usePreloadLayers(layers) { }) ); } else { - return loadImage(getBestImageUrlForLayer(layer, { hiResMode })).then( - (image) => ({ - type: "image", - image, - }) - ); + return loadImage({ + src: getBestImageUrlForLayer(layer, { hiResMode }), + }).then((image) => ({ + type: "image", + image, + })); } }); diff --git a/src/app/util.js b/src/app/util.js index c48fb1d..08466c8 100644 --- a/src/app/util.js +++ b/src/app/util.js @@ -115,7 +115,7 @@ export function useCommonStyles() { /** * safeImageUrl returns an HTTPS-safe image URL for Neopets assets! */ -export function safeImageUrl(urlString) { +export function safeImageUrl(urlString, { crossOrigin = null } = {}) { if (urlString == null) { return urlString; } @@ -141,12 +141,18 @@ export function safeImageUrl(urlString) { return "https://impress-2020.openneo.net/__error__URL-was-not-parseable__"; } + // Rewrite Neopets URLs to their HTTPS equivalents, or to our proxy if we + // need CORS headers. if (url.origin === "http://images.neopets.com") { url.protocol = "https:"; - url.host = "images.neopets-asset-proxy.openneo.net"; + if (crossOrigin) { + url.host = "images.neopets-asset-proxy.openneo.net"; + } } else if (url.origin === "http://pets.neopets.com") { url.protocol = "https:"; - url.host = "pets.neopets-asset-proxy.openneo.net"; + if (crossOrigin) { + url.host = "pets.neopets-asset-proxy.openneo.net"; + } } if (url.protocol !== "https:") {