-
Notifications
You must be signed in to change notification settings - Fork 22
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
Add method to sendBeacon #27
Conversation
@toddreifsteck overall, this looks reasonable. Couple of quick questions:
|
</section> | ||
<section class="appendix"> | ||
<h2>Acknowledgments</h2> | ||
<p>Sincere thanks to Jonas Sicking, Nick Doty, James Simonsen, William Chan, Jason Weber, Philippe Le Hegaret, Daniel Austin, Chase Douglas, and Anne van Kesteren for their helpful comments and contributions to this work.</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing the Oxford comma. Bold move.
With regard to other methods than GET, I informally reviewed adding GET with a few folks at Microsoft. 2 said "Why are you limiting to GET?" and thus I removed the restriction to prompt this conversation. It is a BIT odd to limit to only GET/POST rather than just passing the method through. Also.. HEAD is arguably a better choice than GET for all GET scenarios and I could argue that it should be allowed. I'm 100% open to feedback. With regard to missing method, I'm happy to default or to throw. No opinion. |
Given you get a CORS preflight if the beacon goes across origins it doesn't seem like a concern that you allow any method. Assuming it invokes Fetch in the right way which I guess it does. I'm a little concerned we now have three APIs that need to do the proper thing around method checking and normalization, as well as forbidding a body when the method is GET/HEAD, but we might be able to manage. |
@toddreifsteck fair enough. Although, I'm scratching my head over OPTIONS + sendBeacon. :) |
Per call on 2/3, I'll update PR to default to POST. |
Had a pre-TAG review with @travisleithead and per that review, I've got another update in process to move the object to the 2nd parameter rather than the first. Will discuss on the 2/17 call then ETA is 2/17 for updated PR based upon results of conversation. |
Beacon issues to address--
|
With regard to issue 2 from above: |
4375f06
to
98c4d8e
Compare
@travisleithead , please take a look! |
A lot of discussion has occurred internally on why we've chosen a type on parameter 1. There are 6 options for solving the addition of method that I’m aware of:
|
6 is interesting. If back-compat is such a large concern that you are weighing the developer ergonomics against it, I think you need to seriously consider bikeshedding a new [but perhaps related] name that doesn't have a pre-existing implementation burden. What would the API look like ideally (without worrying about existing back-compat or interop)? |
The entire purpose of this PR is to extend an existing API to overcome the main reason why adopters choose not to use it. I'm unsure whether it is worth the cognitive cost of adding a new API to achieve this goal for a better dev experience. However... I'll play along. If Navigator.sendBeacon did not already exist and my purpose were NOT to extend an existing API, I believe I would naively create a new type, Beacon, exposed in all contexts. I would initialize it with a dictionary containing url/method. I would have 1 method, send. That method would take a dictionary containing url, method and a nullable BodyInit. This API would be easier to extend if we found new ways to improve it. It could also be constructed once and re-used which could lead to simpler code in some scenarios. I'd also want to touch base with @annevk and @igrigorik and get their take as it is possible I'm missing something. |
4 is definitely ugly; the BeaconRequest new type must be instantiated by the implementation, only to be immediately thrown out after the API call in most cases :( I suppose my preference is the break (1) by just using the dictionary. |
Oh, I hadn't realized BeaconRequest was a class. Why is that not a dictionary? |
@annevk The reason it is not being added as a dictionary is to avoid potentially breaking code that depends on an object's .toString() that returns a url for the first parameter in Navigator.sendBeacon(url, body). I am happy to update it to be a dictionary if @annevk and @igrigorik give me the go ahead. (It is not clear there is a good way to know if this occurs today without one of the UAs adding telemetry to know for sure.) |
A real use case might be something that stringifies to url automatically. Like a location object. |
I see, or a URL object. I guess that's a reasonable concern. An alternative here is that we overload |
fetchBeacon would be very simple but.. sloppy as we have to add a new API to have a super clean dev-friendly interface. Based upon all data below, it is my belief that there is no "good" choice to upgrade sendBeacon or to upgrade fetch. Given that, I believe options 1 or 2 below are the choices we must choose between. (1 seems more similar to existing network APIs and would be my preferred choice for consistency but @DigiTec had strong arguments for option 2.) Restating Options: 2- new Beacon type with a .send(url, { method, body }) method. 3- Update Navigator.sendBeacon(url, body) to Navigator.sendBeacon(url or BeaconRequestType, body) 4- Update Navigator.sendBeacon(url, body) to Navigator.sendBeacon(url or BeaconRequestDictionary, body) 5- Update Navigator.sendBeacon(url, body) to Navigator.sendBeacon(url, BodyInit or NewThing) |
What about the earlier option of third parameter? Technically, GET is allowed to have a body - just sayin.
Or some such? |
Although HTTP allows for a GET with a body, the Request object in the Fetch spec currently does not. See line 31 of the processing algorithm for Request Class XHR ignores the body when 'GET' or 'HEAD' is set per Send method spec We could choose one of those behaviors and implement a 3rd parameter... which would work but.. is also kind of ugly.... |
@toddreifsteck yep, that was more tongue in cheek on my part :) That said, any reason why you omitted the third argument option in your earlier summary? |
Accidental :) In my head, I keep axing it as ugly but it may be a valid option... I'm going to circle with @travisleithead and @DigiTec tomorrow and will share our thinking by EOD. |
I think my current favorite is fetchBeacon() specified alongside fetch() (on the same object) with navigator.sendBeacon() as its legacy lesser alternative. fetchBeacon() basically matches fetch() except it doesn't have a return value and the user agent has some flexibility when it actually does the fetch. |
I agree that, in design, it matches fetch() otherwise but it would be confusing imho to call the method fetchBeacon when it isn't meant to fetch things. emitBeacon is an other alternative. |
New options breakdown time!! @annevk @igrigorik @DigiTec @plehegar @travisleithead I generally prefer option 2. It keeps the scope of the API small and has a well understood surface area for both developers and implementers. I'm going to spend Tuesday updating this PR to option 2 below unless I hear pushback.
|
I think |
I'm in the midst of updating to the following WebIDL. Please let me know if there is feedback on the following WebIDL. Full spec should come in next day or two.
|
|
I'm getting a lot of inbound interest from various folks for non-POST sendBeacon. Specifically, POST is a blocker and they need support for GET/HEAD to proceed - e.g., see #22 (comment) and ampproject/amphtml#2446 (comment). After re-reading the thread above, it seems that the PR is very close to a working solution. That said, a quick sanity check... What are the reasons for
Current Beacon implementations do not do any form of retries or request coalescing (which is why we dropped
Is keep-alive flag on fetch meant as a restricted flag for internal browser APIs and we can't or are not willing to expose it to applications? If we were to add some minimal extra logic to fetch to limit body size for requests with this flag set (reject promise if exceeds), and also don't fire the promise on response body (we can still fire it on reciept of headers, that would actually be very valuable as it provides explicit feedback that beacon was processed)... would that do the trick? @annevk wdyt? |
I think using |
@annevk what are your thoughts on exposing keep-alive flag in Fetch to application developers? I am a little uneasy about it as I don't think we want to give applications unrestricted access to continue requests as page terminates (e.g. "oh the user is navigating away, quick start the 10mb upload of this trace file"), as this is both unintuitive to the user and also hurts the next navigation due to resource contention. However, perhaps if we place the same restrictions around what type of requests are allowed when this flag is present, then we'd be ok? In Beacon our current solution is: check POST body size and reject if >~64KB (the limit is UA determined), also we don't allow access to response body. For fetch... If keep-alive flag is enabled:
|
What does that mean? Request headers? If we restrict what keep-alive can do it might be okay. Changing the semantics of the returned promise is a little icky, but not too bad I suppose. @domenic? |
No, response headers, just as we do today. This is actually a plus, in that it provides an explicit ACK to the application that the request was processed by the server (if the app is still around to process the ACK, of course). |
@igrigorik @annevk I'm 100% in agreement that we should solve this for the web. At the same time, I'm not yet convinced that adding this to the fetch options parameter is our best option, My thinking is that I want to understand how many footguns we are handing to web developers by adding this to fetch directly. The reason for my current fetchBeacon proposal was to attempt to avoid too many "contradictory" options members on fetch. (The obvious downside of adding a new function, fetchBeacon, is that it requires developers to learn of the existence of a 2nd networking API and effectively deprecates sendBeacon. I also believe we would not be having this entire discussion if sendBeacon were easily updated... but its signature not having an options parameters makes that impossible without potentially breaking sites). Do we understand the full list of restrictions that would need to be added to fetch when this option is set to 'true' and the default members on fetch that a telemetry dev would need to change to use fetch successfully? |
@toddreifsteck I think if we stick to to requiring mode: "no-cors" for now it should be okay. On the one hand I agree that |
I agree that there are tradeoffs here, my goal in the last few messages is to make them explicit and make sure that we're all on board with whatever route we take.
That's a thing already: #30. In my head...
I haven't heard any strong arguments for (1) so far. Yes, more flags on fetch is less pretty for developer ergonomics, but.. shrug, libraries and wrappers can help address that. On the other hand, giving unconditional access to (2) also doesn't seem wise, so then it becomes a question of whether limitations in (2.2) are seen as plausible and sufficient. /me looks at @annevk :-) |
It would be easier to evaluate if someone figured out what the delta would be. But in principle if we start with exposing what |
@annevk I think the MVP is actually pretty simple... Expose keep-alive on Request class in Fetch:
In the constructor...
Something like the above would expose the keep-alive flag to applications, but ensure that such requests, if they have a body, are limited in the amount of (body) data they can transmit. We can keep the limit soft and let the UA's set it as they wish. Also, on further thought, I don't think we need to touch the response processing.. If there is a response, it'll be subject to all the usual CORS bits, and there is no reason to neuter it just because keepAlive is set to true. With the above, I think we have enough to explain |
It'll be harder with streaming uploads. We should say something about the lifetime though, since UAs can keep the window alive if they have a bfcache or some such. Not sure if the promise should remain working that long. Also, a response from the server does not tell you the body made it all the way, unless the server guarantees that somehow out-of-band. |
@annevk yep, good points. That said, I'm also reading this as "we should be able to make this work"... I'm out for the next two weeks, but I can take a run at a PR for Fetch once I'm back, unless someone else beats me to it. |
Yeah, I was just trying to think of problems. It still seems reasonable given that keep-alive is something we expose today. |
Related discussion in [1]. This exposes keepAlive flag within Request constructor and adds guards for limiting the size of such requests. [1] w3c/beacon#27
Related discussion in [1]. This exposes keepAlive flag within Request constructor and adds guards for limiting the size of such requests. [1] w3c/beacon#27
See w3c/beacon#27 for related discussion. This basically pulls navigator.sendBeacon() functionality into fetch(). There's a new keepalive flag that when set puts a constraint on the request's body's size while also allowing the fetch to not be terminated when the environment in which it was created goes away. Fixes #124.
Fetch (spec) integration has landed: whatwg/fetch#388 (comment). Closing this pull request. Thanks everyone for the feedback and help! |
I realize I'm late to the party here, but IMO it would have been nicer to make |
See w3c/beacon#27 for related discussion. This basically pulls navigator.sendBeacon() functionality into fetch(). There's a new keepalive flag that when set puts a constraint on the request's body's size while also allowing the fetch to not be terminated when the environment in which it was created goes away. Fixes whatwg#124.
See w3c/beacon#27 for related discussion. This basically pulls navigator.sendBeacon() functionality into fetch(). There's a new keepalive flag that when set puts a constraint on the request's body's size while also allowing the fetch to not be terminated when the environment in which it was created goes away. Fixes whatwg#124.
See w3c/beacon#27 for related discussion. This basically pulls navigator.sendBeacon() functionality into fetch(). There's a new keepalive flag that when set puts a constraint on the request's body's size while also allowing the fetch to not be terminated when the environment in which it was created goes away. Fixes whatwg#124.
Following the trail of issues and PRs a few years later, I'm looking for some clarification on the most up-to-date recommendation. Please see this StackOverflow question. |
One blocker for adoption of sendBeacon has been that GET is missing which many analytics services depend on today. This PR removes that restriction by adding the ability to specify the method on the request.
This PR should satisfy #22