Closed igrigorik closed 9 years ago
http://www.w3.org/html/wg/drafts/html/master/document-metadata.html#the-link-element
Once the attempts to obtain the resource and its critical subresources are complete, the user agent must, if the loads were successful, queue a task to fire a simple event named load at the link element, or, if the resource or one of its critical subresources failed to completely load for any reason (e.g. DNS error, HTTP 404 response, a connection being prematurely closed, unsupported Content-Type), queue a task to fire a simple event named error at the link element. Non-network errors in processing the resource or its subresources (e.g. CSS parse errors, PNG decoding errors) are not failures for the purposes of this paragraph.
I think this makes sense, and similar to CSS use case should be made available on hints:
<script>
function hintSuccess() {
// Do something interesting
}
function sheetError() {
// Hint was aborted or a network error occurred
}
</script>
<link rel="preconnect" href="https://widget-site.com/" onload="hintSuccess()" onerror="hintError()">
<link rel="preload" href="assets/app.js" onload="hintSuccess()" onerror="hintError()">
<link rel="prerender" href="/next" onload="hintSuccess()" onerror="hintError()">
One interesting "gotcha" with these events on hints is that neither is guaranteed to fire? If the UA defers hint execution due to resource constraints, and/or decides not to execute it at all, then neither load or error would fire.
Does this seem sane? /cc @annevk @willchan
Maybe. HTML uses a 404 status as failure, but e.g. <img>
does not. I'm not sure if that is actually a correct failure case even for stylesheets.
Hmm, the 'no guarantee' is a problem with things like JS libs fallbacks should a CDN hang (e.g. jquery).
I find the now deprecated IE 'readyState' much preferable to deal with this. And if not, an 'onstart' event which can be used to determine if the hints started. If 'onstart' is not fired, we can assume something may be wrong and plan for possible fallback measures.
@hexalys 'no guarantee' is a direct result of using a speculative hint for next navigation. If the hint is marked as 'required' than the UA will initiate the request, whereas a speculative fetch may or may not be executed due to resource constraints, etc. You can't and shouldn't rely on any start signals for speculative hints. Now, if you mark a resource as 'required', then that's a different story... The workflow is something like:
@igrigorik My use case would be to use hints with 'required' on the same page. No good fallback logic can work if your CDN hangs on a blocking script. Had this issue with CDNJS where all sites using it would simply block on jquery when the network was down. A preload hint could be useful for this.
Here is an example implementation with a preload
named event:
<script>
var CDN1;
function hintPreload() {
CDN1 = true; // CDN is up and download started
}
//preload jquery early
</script>
<link rel="preload" priority="required" onpreload="hintPreload()" href="//mycdn1.com/jquery.js">
<link href="/base.css" rel="stylesheet" type="text/css" media="all">
<script>
if (!CDN1){
//CDN1 is down, use CDN 2 fallback preload
var hint = document.createElement("link")
hint.setAttribute("rel", "preload")
hint.setAttribute("href", "//mycdn2.com/jquery.js")
document.getElementsByTagName("head")[0].appendChild(hint)
}
</script>
<body>
//docwrite jquery here from CDN 1, or CDN 2 if 1 is down
</body>
This feels like a lot of complexity to do what you want, which is essentially to provide an alternative URL for a sub-resource. I wonder if we can support this explicitly with a new “alt-href” attribute plus the sub-resource integrity attribute. The UA can implement this as it likes but one possibility it so use the alt-href after detecting the first content server is slow to respond. In that case you’d get the same behavior as your below with the following: <link rel="preload" priority="required" href="//mycdn1.com/jquery.js” alt-href="//mycdn2.com/jquery.js” integrity=XXXX>.
Peter
On Aug 9, 2014, at 1:00 AM, B notifications@github.com wrote:
@igrigorik My use case would be to use hints with 'required' on the same page. No good fallback logic can work if your CDN hangs on a blocking script. Had this issue with CDNJS where all sites using it would simply block on jquery when the network was down. A preload hint could be useful for this. Here is an example implementation with a preload named event:
//docwrite jquery here from CDN 1, or CDN 2 if 1 is down— Reply to this email directly or view it on GitHub.
@hexalys maybe I'm missing something but why is onerror
insufficient to achieve what you're describing? The resource load failed, event is fired, you trigger your logic, etc.
If we were to go down the route of turning this into native functionality (i.e. extra attribute, as suggested by @bizzbyster), then it shouldn't be restricted to hints either. But then we also open another can of worms: what should the timeout value be, should custom timeouts be allowed, error conditions, retries, and so on. This is a whole separate spec/discussion.
@igrigorik It's insufficient because you can't assume onerror
will fire fast enough or at all, unless you introduce a condition based timeout. Unless I am mistaken, a slow or pending request may not fail at all or take more than 10-30s to do so. I am looking for a reliable signal... Can a 'required' preconnect
event be used with onload
or onerror
providing at least a partial DNS handshake guarantee?
Because hints are partially up to the browser with no general guarantee of firing onerror
or onload
. I think it's worthwhile indicating to the author as to the whether the hints are started or executed at all, even if they are speculative. A loading
state or an onstart
event are useful fallback signals IMO.
A "reliable signal" seems like a bit of a misnomer in this case. If the fetch failed due to a failed DNS, TCP, or TLS handshake, then that will invoke onerror... But even any one of those can take an arbitrarily long period of time - e.g. slow DNS, huge TCP backlog, and so on. As a result, you should set a timer, wait, and then make a decision as to how you want to proceed.
Because hints are partially up to the browser with no general guarantee of firing onerror or onload.
Not for 'required' case. Those will always fire either onload or onerror.
I think it's worthwhile indicating to the author as to the whether the hints are started or executed at all, even if they are speculative. A loading state or an onstart event are useful fallback signals IMO.
Having a "started" signal seems reasonable. That said, it shouldn't be limited to hints either. Seems like a generally useful event.
http://w3c.github.io/webappsec/specs/subresourceintegrity/#the-noncanonical-src-attribute-todo-1
noncanonical-src is a similar idea to alt-href except that its purpose is as a fall back mechanism when a sub resource integrity check fails. We could expand that usage to include the case where the first URL is non-responsive.
Peter
On Aug 11, 2014, at 1:37 PM, Ilya Grigorik notifications@github.com wrote:
@hexalys maybe I'm missing something but why is onerror insufficient to achieve what you're describing? The resource load failed, event is fired, you trigger your logic, etc.
If we were to go down the route of turning this into native functionality (i.e. extra attribute, as suggested by @bizzbyster), then it shouldn't be restricted to hints either. But then we also open another can of worms: what should the timeout value be, should custom timeouts be allowed, error conditions, retries, and so on. This is a whole separate spec/discussion.
— Reply to this email directly or view it on GitHub.
http://w3c.github.io/webappsec/specs/subresourceintegrity/#the-noncanonical-src-attribute-todo-1
Ah, nice! That seems like a much better approach. /cc @mikewest
Thinking about this some more, the onerror
case seems relatively straight forward - i.e. we can use the same language as defined in the current HTML spec for subresource. The onload
case is much harder though:
onload
only fire when full processing is done, or should it fire if any processing is done? If its the latter, then it seems like you'd also want to know how far along you got... in which case, a better solution would be to emit progress events, not just {onerror, onload}.@annevk does Fetch expose progress events, and if so, how granular are they? For example, could you get a notification that socket has been opened (i.e. map that to preconnect success?). Perhaps instead of {onload, onerror} we should, instead, consider exposing an attribute that provides the name of a progress callback?
Fetch has some callbacks to enable progress events. See http://fetch.spec.whatwg.org/#process-response
Nothing on the socket-level though.
Took a first run at defining load/error events in https://github.com/igrigorik/resource-hints/commit/268b667114986bf5aef9e153fc6462a129d71465. How does this look: https://igrigorik.github.io/resource-hints/#hint-execution
To keep things simple and aligned to existing events on link
element I've only defined load
and error
, which works fine for required hints, but leads to somewhat relaxed guarantees for speculative hints - i.e. they may not fire at all, and when they do, only serve as an indicator that some work was done. This may or may not be enough... Thoughts?
As an aside, running a quick test in existing browsers... Chrome fires load
and error
events on all link
rel types, whereas Firefox and Safari fire load and error events on rel=stylesheet
but skip them for other types - see test page.
Also, stumbled across this handy CSS/JS loading capabilities grid.
Question: If a UA decides not to fetch a speculative hint, must it fire the error event?
@bizzbyster I don't think so, it's not a fetch error.
In regards to a 'started' signal the media element has loadstart
, progress
, suspend
and abort
already specced. Can you use those events beyond the media element?, broadening their scope.
@hexalys that would create an inconsistency with current link
definition, which would be painful.
That said, an even more important question is: what's are the use cases? Abort state seems identical to error; suspend seems unnecessary; progress is of limited use as well in the context of hints; we're left with loadstart, the use case for which is also unclear to me in the context of hints.
Closing. If current definition doesn't cover the necessary cases, let's continue discussion at https://github.com/w3c/resource-hints/issues
Need to define which events should be emitted on resource hints - e.g. started loading, loaded, cancelled, or something like it.