igrigorik / resource-hints

Moved to...
https://github.com/w3c/resource-hints
32 stars 7 forks source link

Document emitted events #4

Closed igrigorik closed 9 years ago

igrigorik commented 9 years ago

Need to define which events should be emitted on resource hints - e.g. started loading, loaded, cancelled, or something like it.

igrigorik commented 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

annevk commented 9 years ago

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.

hexalys commented 9 years ago

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.

igrigorik commented 9 years ago

@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:

hexalys commented 9 years ago

@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>
bizzbyster commented 9 years ago

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.

igrigorik commented 9 years ago

@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.

hexalys commented 9 years ago

@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.

igrigorik commented 9 years ago

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.

bizzbyster commented 9 years ago

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.

igrigorik commented 9 years ago

http://w3c.github.io/webappsec/specs/subresourceintegrity/#the-noncanonical-src-attribute-todo-1

Ah, nice! That seems like a much better approach. /cc @mikewest

igrigorik commented 9 years ago

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:

@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?

annevk commented 9 years ago

Fetch has some callbacks to enable progress events. See http://fetch.spec.whatwg.org/#process-response

Nothing on the socket-level though.

igrigorik commented 9 years ago

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.

bizzbyster commented 9 years ago

Question: If a UA decides not to fetch a speculative hint, must it fire the error event?

igrigorik commented 9 years ago

@bizzbyster I don't think so, it's not a fetch error.

hexalys commented 9 years ago

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.

igrigorik commented 9 years ago

@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.

igrigorik commented 9 years ago

Closing. If current definition doesn't cover the necessary cases, let's continue discussion at https://github.com/w3c/resource-hints/issues