Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.7] Any client should be able to request any <hash>.{js,css,...} asset #9953

Closed
morloy opened this issue Jun 6, 2018 · 11 comments
Closed
Assignees
Labels
confirmed We want to fix or implement it Type:Feature
Milestone

Comments

@morloy
Copy link
Contributor

morloy commented Jun 6, 2018

Currently, Meteor returns different content based on the User-Agent. This breaks the setup with a CDN such as CloudFront (which uses "Amazon CloudFront" as UA).

Since the JS files are named by their hash, everything should fine. However, Meteor returns a 404 if you access the "modern" JS bundle using a "legacy" UA!

The easiest fix would be to include the modern JS files also in the legacy bundle.
Another option could be to use different public addresses for each bundle (e. g. /web.browser/5c8f3031f7e146f0a3d07cadb12848c04bb176b5.js?meteor_js_resource=true).

@benjamn
Copy link
Contributor

benjamn commented Jun 6, 2018

It surprises me that a CDN would not forward the end-user client UA when reading through from the origin? A CDN needs to be able to serve both modern and legacy assets, so forcing a single UA of its own choosing seems unacceptable…

@morloy
Copy link
Contributor Author

morloy commented Jun 6, 2018

The purpose of the CDN is to minimize interaction with the original server. But the CDN can’t decide between modern and legacy itself. So, either the CDN needs to cache a different file per each UA, or the content must be independent of the UA.

@benjamn
Copy link
Contributor

benjamn commented Jun 6, 2018

So, either the CDN needs to cache a different file per each UA

That's why we set the Vary: User-Agent header on all responses that could be either modern or legacy (182c426). A CDN that forwards the original UA and reads from the origin only when it hasn't previously cached the resource should be able to get this right.

@benjamn
Copy link
Contributor

benjamn commented Jun 6, 2018

Possibly helpful? Configuring CloudFront to Cache Objects Based on Request Headers

If that works, it's definitely something we should add to the 1.7 migration guide.

@morloy
Copy link
Contributor Author

morloy commented Jun 6, 2018

Mhh, even if it works, it would mean caching 1000s of files vs 2 on CloudFront.
Nevertheless, according to the link, CloudFront only sends 4 types of UAs: Desktop, Mobile, SmartTV, and Tablet. That obviously doesn’t help to distinguish between modern and legacy 😄

@benjamn
Copy link
Contributor

benjamn commented Jun 6, 2018

Ok, I see what you mean. This could be an good opportunity to put Lambda functions in front of CloudFront, to categorize user agents as modern or legacy before requesting resources from the CDN, but that's beginning to sound pretty complicated.

By the way, during the 1.7 beta process, we did try prefixing static asset URLs like you mentioned, but it ended up causing too many problems for resolving relative URLs, especially in CSS files (see #9849).

I agree making all web.* static resources available to any client in production would be a practical solution to this problem.

@benjamn benjamn added this to the Release 1.7.1 milestone Jun 6, 2018
@morloy
Copy link
Contributor Author

morloy commented Jun 6, 2018

What’s the difference between modern and legacy assets? It looks like, they’re the same, except the boilerplate pointing to different JS assets, right?
Then it would make sense to distinguish only in exactly that file and leave the rest the same.
Or, go even further, and make the distinction client side.

@benjamn
Copy link
Contributor

benjamn commented Jun 6, 2018

Sorry, I'm using the word "asset" in a loose generic sense here: any static resource served via HTTP, including JS, CSS, images, etc. Anything a CDN could host.

In Meteor, there's also a notion of assets added with api.addAssets or placed in the public/ folder. While it's possible to have a single asset path that has different modern and legacy versions, that's not recommended and probably doesn't make much sense, so I think for the most part we can assume there is no difference between modern and legacy assets (except for JS or CSS, where Meteor chooses the production filename hash).

@benjamn benjamn changed the title [1.7] Add "modern" JS files to "legacy" bundle [1.7] Any client should be able to request any <hash>.{js,css,...} asset Jun 6, 2018
@benjamn benjamn self-assigned this Jun 6, 2018
@morloy
Copy link
Contributor Author

morloy commented Jun 6, 2018

For the record, I found a reasonable workaround:

  1. In CloudFront, set the User-Agent to that of a modern browser (Distributions->Origins->Origin Custom Headers)
  2. Conditionally enable the CDN in the boilerplate only for modern browsers
WebAppInternals.registerBoilerplateDataCallback('cdn', (req, data, arch) => {
  if (arch === 'web.browser') {
    data.bundledJsCssUrlRewriteHook = url => `${cdnUrl}${url}`; // eslint-disable-line no-param-reassign
  }
  return true;
});

@benjamn
Copy link
Contributor

benjamn commented Jun 6, 2018

As long as that doesn't put too much load on your servers, it definitely works!

@morloy
Copy link
Contributor Author

morloy commented Jun 8, 2018

Wow, thanks for fixing this issue so fast, @benjamn!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed We want to fix or implement it Type:Feature
Projects
None yet
Development

No branches or pull requests

2 participants