- From: Roberto Peon <grmocg@gmail.com>
- Date: Sun, 26 May 2013 00:10:38 -0700
- To: Jeff Pinner <jpinner@twitter.com>
- Cc: James M Snell <jasnell@gmail.com>, William Chan (陈智昌) <willchan@chromium.org>, Patrick McManus <pmcmanus@mozilla.com>, "ietf-http-wg@w3.org" <ietf-http-wg@w3.org>
- Message-ID: <CAP+FsNfBoh6wcqTUHfHPhbf8zZ4Vj=i2Lg4bEG5wcyuUwz60eQ@mail.gmail.com>
On Sat, May 25, 2013 at 8:44 PM, Jeff Pinner <jpinner@twitter.com> wrote: > I would say the server drops the HEADERS frame which I suppose means that > PRIORITY opens the stream (just as PUSH_PROMISE does) since the client > can't distinguish between: > > server receives PRIORITY > server generates RST_STREAM > server receives HEADERS > > and > > server receives PRIORITY > server receives HEADERS > server generates RST_STREAM > > so it would be equivalent to PRIORITY having created the stream, the > RST_STREAM closing it and the client not bothering to send the DATA frames. > > > It may be the case that we only want HEADERS frames to be able to create > streams, in which case yes that would prohibit sending a PRIORITY frame > before the HEADERS frame. But I don't know if that is being overly > restrictive. > This is what I favor, and it is at most 8 bytes of overhead (send an empty HEADERS frame before whatever else) in non-HTTP cases where metadata about the stream opening is not needed (nothing that I can think of would do this today). > > Another option is a flag on the HEADERS frame similar to the continuation > bit that says a PRIORITY frame is following and must be sent after the > continued headers are received. > This is basically what HEADERS+PRIORITY is, without the overhead of the frame-header. -=R > > > On Sat, May 25, 2013 at 2:32 PM, Roberto Peon <grmocg@gmail.com> wrote: > >> 1) I'm not worried about prioritization after stream start-- that race is >> unavoidable :) >> 2) Agreed. Happy to handle this complexity-- it requires that I >> allocation nothing new. At worst I remove the stream from the priority >> structure and re-add, or simply report that the stream is already dead. >> 3) This is what I'm unhappy about. PRIORITY before HEADERS means that I >> have an additional state transition and more state to track. >> I agree that it seems more simple on the surface, but I also think that a >> decoupled PRIORITY frame leads to more code complexity given the need to >> handle client-directed priority for responses at the server. >> >> >> It is actually more complex to decouple these at stream initiation time, >> since, at stream initiation time, I want to know both what I'm doing, and >> how important it is. A lack of one or the other leads to additional state >> tracking, additional lookups, races, ambiguity over when the proxy should >> begin to make connections, etc. >> >> As an example, in a world where PRIORITY is decoupled from HEADERS, what >> happens in the race where I've reset the stream after receiving the >> PRIORITY frame, but before receiving the HEADERS frame? >> >> client sends Priority(x),stream(3) >> client sends Headers(x),stream(3) >> >> server receives Priority(x), stream(3) >> server sends RST stream(3) >> server receives Headers(x), stream(3) >> ... now what? >> >> I see a few obvious outcomes, none of which seem appealing to me: >> 1) The server processes the request, but now without knowledge of the >> priority >> 2) The server keeps track of streams it has reset for some time period >> (this would have to be a map or similar) and times them out after some time >> period (e.g. sends a PING and awaits the PONG) >> 3) The server is disallowed from sending the RST before it has received >> HEADERS, and must maintain the additional data that is perhaps not ever >> useful >> >> In the case where we have HEADERS+PRIORITY, and where stream creation is >> accomplished ONLY via HEADERS or HEADERS+PRIORITY, this mess goes away. >> Yes, we still have reprioritization to deal with, but with the guarantee >> of the previous sentence, if I see a PRIORITY for a stream I don't know, I >> just ignore it (and generate a RST stream with appropriate error code) >> >> In a world where we agree that streams are only started by some explicit >> thing (HEADERS*), the code is also more simple, because I don't have to >> maintain state to see what streams have finished vs never created, etc. >> etc. and because there are fewer races and less ambiguity. >> >> -=R >> >> >> >> >> >> >> >> On Sat, May 25, 2013 at 1:09 PM, Jeff Pinner <jpinner@twitter.com> wrote: >> >>> You get the same race condition by introducing re-prioritization since >>> that is equivalent to sending the priority after initiating a stream with >>> default priority. >>> >>> I guess my argument is: >>> >>> 1. Once we introduce the PRIORITY frame and re-prioritization you have >>> to handle all the additional complexity of receiving PRIORITY after HEADERS. >>> 2. We already have to handle receiving PRIORITY before HEADERS (i.e. >>> empty continued header blocks). >>> 3. Clients have the option to always send PRIORITY first if they want to >>> ensure that it can be considered by the server. >>> >>> So introducing PRIORITY as a frame means you now have two ways to >>> accomplish the same thing and you have already taken the complexity hit, so >>> lets try to make it slightly simpler by only having one way. >>> >>> >>> >>> >>> >>> On Sat, May 25, 2013 at 12:52 PM, Roberto Peon <grmocg@gmail.com> wrote: >>> >>>> Nah-- I've been proposing that when the continuation bit is set, you >>>> may ONLY continue sending HEADERS frames for that stream until the set of >>>> headers is done (at which point you can send whatever again), basically >>>> because of both complexity and the DoS stuff inherent in incomplete state. >>>> :) >>>> >>>> I agree that the best way to handle the PRIORITY DoS is to treat the >>>> receipt of one on a stream which isn't known as a protocol error. This, >>>> however re-invokes the race condition where a stream has been created but >>>> we don't know the priority. We either need to know the priority before the >>>> headers are completed. >>>> >>>> Honestly, I'm not sure what the spec allows right now, but what I said >>>> in the previous email (you have to begin a stream with either HEADERS or >>>> HEADERS+PRIORITY) is what I understand is the proposal to be. >>>> It would be different from SYN_STREAM-- you could begin a stream with >>>> the equivalent of SYN_REPLY (HEADERS) >>>> -=R >>>> >>>> >>>> On Sat, May 25, 2013 at 12:44 PM, Jeff Pinner <jpinner@twitter.com>wrote: >>>> >>>>> A rebuttal of the DoS argument for the server case. Consider the >>>>> following: >>>>> >>>>> 1. We currently allow up to N concurrent streams on the server >>>>> (configured via settings). >>>>> 2. With reducing the frame size you have made the argument that >>>>> multiple HEADERS frame may be required on an individual stream to comprise >>>>> a HTTP request. >>>>> >>>>> This means that a malicious client could always send N >>>>> HEADERS+PRIORITY frames, all with some "continuation" bit set (and possibly >>>>> all without any headers), causing the server to allocate state for all N >>>>> streams. >>>>> >>>>> This would be identical to issuing N PRIORITY frames for the next N >>>>> open streams followed by N HEADERS frames. >>>>> >>>>> We can also protect the server against DoS by either requiring that >>>>> the receipt of a PRIORITY frame for a Stream ID that is invalid result in a >>>>> PROTOCOL_ERROR the same way that receipt of a HEADERS+PRIORITY frame would. >>>>> >>>>> As for the additional state transitions, the additional complexity in >>>>> the session management logic is introduced as soon as you allow more than >>>>> one HEADERS frame per request and unaffected by separating HEADERS and >>>>> PRIORITY (again since a HEADERS+PRIORITY frame with no headers is >>>>> equivalent). >>>>> >>>>> >>>>> As for issuing HEADERS from the client, does the spec now allowing >>>>> mapping HTTP requests to either HEADERS or HEADERS+PRIORITY frames? So this >>>>> is different than what was done with SynStream? >>>>> >>>>> >>>>> On Sat, May 25, 2013 at 12:24 PM, Roberto Peon <grmocg@gmail.com>wrote: >>>>> >>>>>> Right now, we've proposed the following frame types: >>>>>> >>>>>> HEADERS+PRIORITY >>>>>> HEADERS >>>>>> PRIORITY (to be added) >>>>>> >>>>>> While I'm strongly against removal for the HEADERS+PRIORITY frame, I >>>>>> do agree that the PRIORITY frame is probably reasonable. >>>>>> >>>>>> Playing devil's advocate on the PRIORTY frame, though, I ask myself >>>>>> the question: >>>>>> Since a HEADERS+PRIORITY frame with no payload would be the same as a >>>>>> PRIORTY frame (and we have to handle the empty-payload case for header >>>>>> blocks anyway), are we buying anything? >>>>>> I don't have a good answer except that the PRIORITY frame "smells" >>>>>> right, and it has a low implementation cost in terms of complexity if we're >>>>>> doing any reprioritization. >>>>>> >>>>>> So, onto why I hate the idea of *requiring* separate frames for >>>>>> PRIORITY and HEADERS: >>>>>> In the PUSH_PROMISE case, the client can have indicated that it will >>>>>> not accept these streams by sending a SETTINGS frame, and much of the time >>>>>> the client has orders of magnitude more memory to spend per connection that >>>>>> does the server. PUSH_PROMISE is a totally different beast from PRIORITY. >>>>>> PRIORITY, on the other hand is not optional and can't be disabled-- >>>>>> if we did remove/disallow it, then we'd have crippled multiplexing for the >>>>>> browser use-case. >>>>>> If we allow PRIORITY to perform allocations, then we've increased >>>>>> complexity in the servers (more state transitions and state to store) and >>>>>> thus increased the number of DoS vectors against the servers for >>>>>> essentially zero gain. >>>>>> >>>>>> In your (non browser) use case, you'd just use HEADERS to initiate a >>>>>> stream from the client when you don't care to set the priority. You don't >>>>>> have to use HEADERS+PRIORITY. Your overall complexity should be unchanged. >>>>>> In the browser use-case, HEADERS+PRIORITY would be used nearly all >>>>>> the time, since the communication of priority is of very high importance >>>>>> (else the browser must implement and play heuristic waiting games for >>>>>> requests). >>>>>> >>>>>> In either case a PRIORITY frame could be used to reprioritize a >>>>>> stream. >>>>>> >>>>>> -=R >>>>>> >>>>>> >>>>>> On Sat, May 25, 2013 at 11:58 AM, Jeff Pinner <jpinner@twitter.com>wrote: >>>>>> >>>>>>> The color of my shed: >>>>>>> >>>>>>> I would like to see us remove HEADERS+PRIORITY entirely and add a >>>>>>> separate PRIORITY frame. >>>>>>> >>>>>>> I don't agree that separating them will simply cause an additional 4 >>>>>>> bytes to be sent on every request. While it might be true that most >>>>>>> browsers will set the priority of a request, I don't think that all clients >>>>>>> necessarily will (I have a mobile client that uses HTTP for API requests >>>>>>> and have not found the priority mechanism necessary -- at least not yet). >>>>>>> >>>>>>> I could imagine that it would be acceptable to send the PRIORITY >>>>>>> frame before the HEADERS frame and still mandate that HEADERS frames (and >>>>>>> now only HEADERS frames) initiate streams. While this does cause some extra >>>>>>> state allocation, we already have to do something similar with PUSH_PROMISE >>>>>>> where we must track that the "stream identifier MUST be a valid choice for >>>>>>> the next stream sent" and then initiate the stream later. >>>>>>> >>>>>>> This would also give us a mechanism to send priority changes for >>>>>>> outstanding requests at the framing layer, and could allow priorities to be >>>>>>> set for pushed responses should it prove useful. >>>>>>> >>>>>>> - Jeff >>>>>>> >>>>>>> >>>>>>> On Tue, May 21, 2013 at 2:55 PM, James M Snell <jasnell@gmail.com>wrote: >>>>>>> >>>>>>>> On Tue, May 21, 2013 at 2:43 PM, Roberto Peon <grmocg@gmail.com> >>>>>>>> wrote: >>>>>>>> > Sending the PRIORITY frame *MUST* cause state allocation at the >>>>>>>> receiver, >>>>>>>> > else it was useless to send before the HEADERS frame. As you >>>>>>>> point out, at >>>>>>>> > minimum it must allocate a stream ID and priority field, and for >>>>>>>> most >>>>>>>> > implementations it will also need to include so mechanism of >>>>>>>> pointing out >>>>>>>> > that the headers don't exist, so, probably between 16 to 24 bytes >>>>>>>> worth of >>>>>>>> > allocation on a 64 bit machine. >>>>>>>> > >>>>>>>> >>>>>>>> Sorry, I wasn't clear in my initial response. Yes, there is some >>>>>>>> state >>>>>>>> that would need to be allocated but not the same as that when the >>>>>>>> initial HEADERS frame is received, for instance. >>>>>>>> >>>>>>>> > If the PRIORITY frame was renamed to CHANGE_PRIORITY, would that >>>>>>>> clarify >>>>>>>> > anything? Priority changing is the current intent of that frame >>>>>>>> type. >>>>>>>> > >>>>>>>> >>>>>>>> Not particularly, because we'd still have the question of when to >>>>>>>> use >>>>>>>> HEADERS+PRIORITY vs. the combination of HEADERS and a >>>>>>>> CHANGE_PRIORITY. >>>>>>>> Can HEADERS+PRIORITY be used any time? For instance, could I send an >>>>>>>> initial HEADERS frame on a stream then later send a HEADERS+PRIORITY >>>>>>>> on the same stream? I honestly don't care how it ultimately ends up >>>>>>>> so >>>>>>>> long as (a) It's the simplest thing that could possibly work and (b) >>>>>>>> Is easy to explain in the spec and easy for a developer to >>>>>>>> implement. >>>>>>>> >>>>>>>> > btw, I am not particularly partial to the "any frame opening up a >>>>>>>> stream" >>>>>>>> > thing. I'm not completely against it though :) >>>>>>>> > My reason for slightly preferring that streams must begin with >>>>>>>> HEADERS or >>>>>>>> > HEADERS+PRIORITY is that it is an explicit statement of intent, >>>>>>>> and thus >>>>>>>> > off-by-one, uninitialized var, etc. errors are more likely to be >>>>>>>> detectable >>>>>>>> > in a world where such is required. >>>>>>>> > >>>>>>>> >>>>>>>> I would very much like to see us mandate that streams always >>>>>>>> initiate >>>>>>>> with a HEADERS / HEADERS+PRIORITY frame. >>>>>>>> >>>>>>>> - James >>>>>>>> >>>>>>>> > >>>>>>>> > >>>>>>>> > -=R >>>>>>>> > >>>>>>>> > >>>>>>>> > On Tue, May 21, 2013 at 2:19 PM, James M Snell <jasnell@gmail.com> >>>>>>>> wrote: >>>>>>>> >> >>>>>>>> >> On Tue, May 21, 2013 at 10:30 AM, William Chan (陈智昌) >>>>>>>> >> <willchan@chromium.org> wrote: >>>>>>>> >> > Thanks for describing these cases now. I had not thought of >>>>>>>> them. >>>>>>>> >> > >>>>>>>> >> > If everyone's sold on reprioritization, then let's go ahead >>>>>>>> and do this >>>>>>>> >> > and >>>>>>>> >> > have the debate about ditching HEADERS+PRIORITY or not. I want >>>>>>>> to keep >>>>>>>> >> > it. I >>>>>>>> >> > don't like the idea of sending a PRIORITY frame first. Is >>>>>>>> sending a >>>>>>>> >> > PRIORITY >>>>>>>> >> > frame going to trigger stream state allocation at the >>>>>>>> receiver? What's >>>>>>>> >> > the >>>>>>>> >> > expectation? And if you don't have a priority for the HEADERS, >>>>>>>> then you >>>>>>>> >> > have >>>>>>>> >> > the race that Roberto described. >>>>>>>> >> > >>>>>>>> >> >>>>>>>> >> There is no reason to assume that sending a PRIORITY frame first >>>>>>>> would >>>>>>>> >> trigger stream state allocation at the receiver. At most, it >>>>>>>> would >>>>>>>> >> reserve the stream ID and store the priority value. The full >>>>>>>> state >>>>>>>> >> allocation would not occur until the HEADERS frame is received. >>>>>>>> That >>>>>>>> >> said, I'm not 100% dead set on removing HEADERS+PRIORITY, I >>>>>>>> would just >>>>>>>> >> like to simplify the protocol where it makes sense to, and even >>>>>>>> then >>>>>>>> >> only after it's been proven out in implementation. Having >>>>>>>> separate >>>>>>>> >> HEADERS, HEADERS+PRIORITY and PRIORITY frames is confusing, if >>>>>>>> we can >>>>>>>> >> do without separating them, we ought to do so. >>>>>>>> >> >>>>>>>> >> - James >>>>>>>> >> >>>>>>>> >> > >>>>>>>> >> > On Tue, May 21, 2013 at 2:09 PM, Patrick McManus < >>>>>>>> pmcmanus@mozilla.com> >>>>>>>> >> > wrote: >>>>>>>> >> >> >>>>>>>> >> >> >>>>>>>> >> >> On Tue, May 21, 2013 at 12:32 PM, William Chan (陈智昌) >>>>>>>> >> >> <willchan@chromium.org> wrote: >>>>>>>> >> >>> >>>>>>>> >> >>> >>>>>>>> >> >>> I support adding a new additional PRIORITY frame for stream >>>>>>>> >> >>> reprioritization. >>>>>>>> >> >> >>>>>>>> >> >> >>>>>>>> >> >> me too. Specifically I support this as a mechanism for the >>>>>>>> client to be >>>>>>>> >> >> able to explicitly prioritize an open pushed stream. I can >>>>>>>> wait for >>>>>>>> >> >> more >>>>>>>> >> >> evidence about re-prioritizing, but in cases where the client >>>>>>>> hasn't >>>>>>>> >> >> ever >>>>>>>> >> >> explicitly set the stream's priority I think we have evidence >>>>>>>> that its >>>>>>>> >> >> time >>>>>>>> >> >> to do something. >>>>>>>> >> >>> >>>>>>>> >> >>> >>>>>>>> >> >>> Unless there's a reason this needs to be in the current >>>>>>>> http/2 draft >>>>>>>> >> >>> sooner rather than later, I'd rather punt on this discussion >>>>>>>> until we >>>>>>>> >> >>> have >>>>>>>> >> >>> implementation experience that can guide this debate. >>>>>>>> >> >> >>>>>>>> >> >> >>>>>>>> >> >> I think there is experience here specifically related to push. >>>>>>>> >> >> >>>>>>>> >> >> e.g. You can easily configure mod_spdy to push images when >>>>>>>> html is >>>>>>>> >> >> pulled. >>>>>>>> >> >> but you can't effectively dictate the relative priorities of >>>>>>>> those two >>>>>>>> >> >> things. >>>>>>>> >> >> >>>>>>>> >> >> Sure, you can define an explicit priority for those images >>>>>>>> but priority >>>>>>>> >> >> implementations are all about relative levels and the client >>>>>>>> set the >>>>>>>> >> >> priority of the html. >>>>>>>> >> >> >>>>>>>> >> >> You can argue that mod_spdy should have defined relative >>>>>>>> priorities >>>>>>>> >> >> (+/- >>>>>>>> >> >> the associated stream) instead of constants.. that would be >>>>>>>> better but >>>>>>>> >> >> the >>>>>>>> >> >> client still has no way to make sure those streams are at a >>>>>>>> higher >>>>>>>> >> >> priority >>>>>>>> >> >> than a "save as" background stream (I've seen this one happen >>>>>>>> as >>>>>>>> >> >> mod_spdy >>>>>>>> >> >> defaults to lowest priority when pushing), or a lower >>>>>>>> priority than a >>>>>>>> >> >> real-time video stream.. >>>>>>>> >> >> >>>>>>>> >> >> plus there is no scale for the server to work with.. it might >>>>>>>> set a +2 >>>>>>>> >> >> priority for pushed images but the client might be using +3 >>>>>>>> for pulled >>>>>>>> >> >> images causing a mismatch in something that was intended to >>>>>>>> be equally >>>>>>>> >> >> weighted. >>>>>>>> >> >> >>>>>>>> >> >> at least with a priority frame the client can make those >>>>>>>> adjustments in >>>>>>>> >> >> a >>>>>>>> >> >> RTT. >>>>>>>> >> >> >>>>>>>> >> >> Cheefully, >>>>>>>> >> >> -Patrick >>>>>>>> >> >> >>>>>>>> >> >> >>>>>>>> >> > >>>>>>>> >> > >>>>>>>> >> >>>>>>>> > >>>>>>>> >>>>>>>> >>>>>>> >>>>>> >>>>> >>>> >>> >> >
Received on Sunday, 26 May 2013 07:11:07 UTC