Slashed URI
This is my kind of URL nerdery. Remy ponders all the permutations of URLs ending with slashes, ending without slashes, ending with with a file extension…
This is my kind of URL nerdery. Remy ponders all the permutations of URLs ending with slashes, ending without slashes, ending with with a file extension…
Jake’s blow-by-blow account of uncovering a serious browser vulnerability is fascinating. But if you don’t care for the technical details, skip ahead to to how different browser makers handled the issue—it’s very enlightening. (And if you do care for the technical details, make sure you click on the link to the PDF version of this post.)
Ooh, this is a tricky scenario. If you decide to redirect all URLs (from, say, a www
subdomain to no subdomain) and you have a service worker running, you’re going to have a bad time. But there’s a solution here to get the service worker to remove itself.
The server-side specifics are for NGINX but this is also doable with Apache.
When I wrote about implementing my first Service Worker, I finished with this plea:
And remember, please share your code and your gotchas: it’s early days for Service Workers so every implementation counts.
Well, I ran into a gotcha that was really frustrating but thanks to the generosity of others, I was able to sort it out.
It was all because of an issue in Chrome. Here’s the problem…
Let’s say you’ve got a Service Worker running that takes care of any requests to your site. Now on that site, you’ve got a URL that receives POST data, does something with it, and then redirects to another URL. That’s a fairly common situation—it’s how I handle webmentions here on adactio.com, and it’s how I handle most add/edit/delete actions over on The Session to help prevent duplicate form submissions.
Anyway, it turns out that Chrome’s Service Worker implementation would get confused by that. Instead of redirecting, it showed the offline page instead. The fetch
wasn’t resolving.
I described the situation to Jake, but rather than just try and explain it in 140 characters, I built a test case.
There’s a Chromium issue filed on this, and it will get fixed, but it in the meantime, it was really bugging me recently when I was rolling out a new feature on The Session. Matthew pointed out that the Chromium bug report also contained a workaround that he’s been using on traintimes.org.uk. Adrian also posted his expanded workaround in there too. That turned out to be exactly what I needed.
I think the problem is that the redirect means that a body is included in the GET request, which is what’s throwing the Service Worker. So I need to create a duplicate request without the body:
request = new Request(url, {
method: 'GET',
headers: request.headers,
mode: request.mode == 'navigate' ? 'cors' : request.mode,
credentials: request.credentials,
redirect: request.redirect
});
So here’s what I had in my Service Worker before:
// For HTML requests, try the network first, fall back to the cache, finally the offline page
if (request.headers.get('Accept').indexOf('text/html') !== -1) {
event.respondWith(
fetch(request)
.then( response => {
// NETWORK
// Stash a copy of this page in the pages cache
let copy = response.clone();
stashInCache(pagesCacheName, request, copy);
return response;
})
.catch( () => {
// CACHE or FALLBACK
return caches.match(request)
.then( response => response || caches.match('/offline') );
})
);
return;
}
// For HTML requests, try the network first, fall back to the cache, finally the offline page
if (request.headers.get('Accept').indexOf('text/html') !== -1) {
request = new Request(url, {
method: 'GET',
headers: request.headers,
mode: request.mode == 'navigate' ? 'cors' : request.mode,
credentials: request.credentials,
redirect: request.redirect
});
event.respondWith(
fetch(request)
.then( response => {
// NETWORK
// Stash a copy of this page in the pages cache
let copy = response.clone();
stashInCache(pagesCacheName, request, copy);
return response;
})
.catch( () => {
// CACHE or FALLBACK
return caches.match(request)
.then( response => response || caches.match('/offline') );
})
);
return;
}
Now the test case is working just fine in Chrome.
On the off-chance that someone out there is struggling with the same issue, I hope that this is useful.
Share what you learn.
Following on from a drunken evening in Brighton, there is now a LOLcat alternative to tinyurl.