Open jamesarosen opened 8 years ago
So the basic strategy is to copy the idea behind ember-cli-deploy-redis, which basically does the same thing, except ours will make a call to create/update an edge dictionary. So that seems to be straightforward, except for the character limiting.
Also, adding app.options.storeConfigInMeta = false;
to ember-cli-build.js should also help keep the index.html to a smaller size. But the pointer idea is a great one.. That could even be automated: if index exceeds 8000 chars, it automatically creates an index-abcdef.html/etc and configures the pointer correctly. That would be killer as we really wouldn't need the warning or build failure -- just a notification as to what's happening.
Also @jamesarosen do you know if it's possible to programmatically update custom vcl on Fastly? -- because then the plugin could actually create the correct vcl if it doesn't already exist, rather than going to the Fastly UI and doing the upload manually. It would be a great UX since this would "just work" without users having to worry about directly creating custom vcl.
do you know if it's possible to programmatically update custom vcl on Fastly?
It's definitely possible -- provided your account has custom VCL enabled. That's a request that has to go through Fastly customer service. See the custom VCL object API docs, Mixing and matching Fastly VCL with custom VCL, and Uploading custom VCL for more info.
You can do most of what you want with conditions and headers, which lets you avoid thorny custom VCL. The place where you need custom VCL is for routing requests to index.html
if they didn't match an asset. You want something akin to nginx's try_files
directive. Essentially, you would want (if it existed)
sub vcl_fetch {
tryFiles(req.http.url, "index.html");
}
Unfortunately, that does not. The equivalent in VCL is a bit complicated. You need to do the following:
vcl_recv
set a marker that you haven't looked for index.html
yet for this request/assets/app-abcdef.js
, it will get a 200 from the asset origin (e.g. S3).vcl_error
, check whether you got a 404 and haven't looked for index.html
yet; if so, set the URL to index.html
and restart the requestIn VCL, that looks like
sub vcl_recv {
if (req.restarts == 0) {
// clear at the beginning so attackers can't tamper with the routing:
unset req.http.x-ember-looked-for-index-html;
}
...
}
sub vcl_error {
if (obj.status == 404 && !req.http.x-ember-looked-for-index-html) {
set req.url = "/index.html";
return restart();
}
}
Of course, that doesn't do the actual handling of serving index.html
from an Edge Dictionary. It might be possible to do that without custom VCL, but custom VCL is the most straightforward and readable way of doing it. Something like
sub vcl_recv {
...
if (req.restarts == 0) {
// clear at the beginning so attackers can't tamper with the routing:
unset req.http.ember-index-version;
}
if (req.url = "/index.html") {
set req.http.ember-index-version = table.lookup("ember_index_versions", "current", "");
if (req.http.ember-index-version == "") {
error 404 "Not Found"; # no current version
} else {
// use errors for control-flow, because that's how Varnish works!
error 810 req.http.ember-index-version;
}
}
}
sub vcl_error {
if (obj.status == 810) {
if (table.lookup("ember_index_versions", obj.response, "") == "") {
# current version doesn't exist
set obj.status = 404;
set obj.response = "Not Found";
} else {
set obj.status = 200;
set obj.response = "OK";
synthetic table.lookup("ember_index_versions", obj.response, "");
}
return deliver;
}
}
The try_files
discussion is to support apps that use the History Location. There's no way in VCL to distinguish GET /robots.txt
(an asset served from origin) from GET /food/12
(an Ember route that runs in the HTML page). Without the retry-on-404, there's no way to serve the app for anything but /
Also, adding
app.options.storeConfigInMeta = false;
toember-cli-build.js
should also help keep the index.html to a smaller size.
Something I've been toying with is creating an ember-cli-esi-config addon that does the following:
app.options.storeConfigInMeta = false;
config.html
to the build that just has the <meta>
tag that would have otherwise been written to index.html
<esi:include src="/config.html">
to index.html
via contentFor('head')
ember serve
stack so ESI works in developmentYou could write to config.html
to change the configuration of your app without redeploying. Or you could have multiple configurations (config-staging.html
, config-production.html
) and use the same HTML and JS for each stage, but different configuration. (This would require conditionally routing /config.html
to /config-staging.html
or /config-production.html
based on the request in VCL or another reverse proxy, but that's easy.)
See also Using ESI, Part 1.
I haven't dug into ember-cli-deploy for about a year -- since when the v0.5.0 rewrite was in-progress -- so please forgive me if I'm off-track here. I want to help, but I'm not an expert on ember-cli-deploy.
Fastly Edge Dictionaries can't take a huge amount of data. Each item value can only be up to 8000 characters.
So we have two options for a Fastly index plugin:
index.html
is > 8000 characters, throw an error and stop the buildindex.html
is > 6000 characters (customizable?), print a warningabcdef
'sindex.html
withPUT /service/service_id/dictionary/index-html/item/abcdef
abcdef
, write the current pointer withPUT /service/service_id/dictionary/index-html/item/current
withabcdef
index.html
toindex-abcdef.html
index-abcdef.html
to S3 or whatever persistent store you're usingabcdef
, write the current pointer withPUT /service/service_id/dictionary/index-html/item/current
withabcdef
In the first case, it will also be important to prune the
/service/service_id/dictionary/index-html/item/*
entries regularly. If that dictionary gets large (MB), it could degrade the quality of service.