Subject: END_SEGMENT and END_STREAM redundant

From: Adrian Cole <[email-masked]>
Date: Sat, 19 Apr 2014 08:06:09 -0700

Hi, all.

The combination of END_SEGMENT and END_STREAM seem redundant to me.
It seems like you wouldn't need to support both flags at the same
time, as ending a stream surely ends the segment.  Is it worthwhile
calling this out?

-A

---

From: Roberto Peon <[email-masked]>
Date: Sat, 19 Apr 2014 15:23:57 -0700

Arguably these are not redundant, since the message may not have been
completed when the stream was terminated.
-=R

---

From: David Krauss <[email-masked]>
Date: Sun, 20 Apr 2014 08:58:42 +0800

This is similar to my earlier question about duplicate segment codings. The spec needs to specify which is significant (observable by the application): the flag bits, or the segmentation.

On 2014–04–20, at 6:23 AM, Roberto Peon <[email-masked]> wrote:

> Arguably these are not redundant, since the message may not have been completed when the stream was terminated.
> -=R
>

---

From: Roberto Peon <[email-masked]>
Date: Sat, 19 Apr 2014 18:14:22 -0700

The implementation and API on top of that allows or disallows the
expression of such things to the application.
It is the duty of the protocol to make it possible to express these things,
not to mandate how it is used, unless it is necessary for interop.

The spec defines the minimum for interop: each bit has its own meaning.
Interpretation is up to the application.
-=R

On Sat, Apr 19, 2014 at 5:58 PM, David Krauss <[email-masked]> wrote:

> This is similar to my earlier question about duplicate segment codings.
> The spec needs to specify which is significant (observable by the
> application): the flag bits, or the segmentation.
>

---

From: David Krauss <[email-masked]>
Date: Sun, 20 Apr 2014 10:23:27 +0800

On 2014–04–20, at 9:14 AM, Roberto Peon <[email-masked]> wrote:

> The implementation and API on top of that allows or disallows the expression of such things to the application.
> It is the duty of the protocol to make it possible to express these things, not to mandate how it is used,

It’s probably just an editorial issue, but the spec wording currently doesn’t clarify the intent or motivating semantics of segmentation, as shown by K Morgan’s recent thread.

If there were a nice diagram of a segment-message or some BNF like you wrote in your earlier reply to me, then applications might be more likely to assign bits to their intended meanings and less likely to put excessive demands on the API.

> unless it is necessary for interop.
> 
> The spec defines the minimum for interop: each bit has its own meaning. Interpretation is up to the application.

There’s some circular reasoning here. Interoperability refers to what intermediaries may change, or to a lesser extent what synonymous bit-codings portable APIs (e.g. Javascript XHR) may merge. If an underlying representation may be changed according to the semantics it expresses, then relying on bits is not interoperable.

A different programmer sensibility is often applied to binary coding than to text, but it’s best to use the same approach either way. A format defines the expression of a variety of messages, and those messages comprise the only defined meaning.

---

From: Roberto Peon <[email-masked]>
Date: Sat, 19 Apr 2014 20:59:53 -0700

On Sat, Apr 19, 2014 at 7:23 PM, David Krauss <[email-masked]> wrote:

> It’s probably just an editorial issue, but the spec wording currently
> doesn’t clarify the intent or motivating semantics of segmentation, as
> shown by K Morgan’s recent thread.
>
> If there were a nice diagram of a segment-message or some BNF like you
> wrote in your earlier reply to me, then applications might be more likely
> to assign bits to their intended meanings and less likely to put excessive
> demands on the API.
>
>
No arguments there. It would be a good idea to add text to be clear that
END_STREAM and END_SEGMENT have different purposes.

> There’s some circular reasoning here. Interoperability refers to what
> intermediaries may change, or to a lesser extent what synonymous
> bit-codings portable APIs (e.g. Javascript XHR) may merge. If an underlying
> representation may be changed according to the semantics it expresses, then
> relying on bits is not interoperable.
>
>
Interoperable implementations of the protocol may not understand each other
at the application layer. That is not a problem the protocol can solve.

> A different programmer sensibility is often applied to binary coding than
> to text, but it’s best to use the same approach either way. A format
> defines the expression of a variety of messages, and those messages
> comprise the only defined meaning.
>
>
I suspect we're arguing semantics at such a level at this point that it
doesn't matter, but the protocol cannot define a meaning: It defines a
grammar.

At this point I suspect we're mostly violently agreeing.
-=R

---

From: David Krauss <[email-masked]>
Date: Sun, 20 Apr 2014 18:58:47 +0800

On 2014–04–20, at 11:59 AM, Roberto Peon <[email-masked]> wrote:

> Interoperable implementations of the protocol may not understand each other at the application layer. That is not a problem the protocol can solve.

I’m talking about whether a proxy is allowed to internally use a generic END_SEGMENT symbol, or if it must distinguish the case of a bit in an empty DATA or HEADERS frame following a frame of the other type. In this way the application layer is creeping into transport.

Additional questions I still have are whether data may be coalesced across a headers block, and whether header blocks in the same segment may be coalesced. I don’t think there are many use-cases for either, and they could be surprising to applications that forget END_SEGMENT. However, the first case does seem to be allowed according to the current spec. (It says nothing about coalescing headers, but then again I can’t find where it explicitly describes coalescing data either. If headers are not guaranteed to arrive at any particular point, they could all get pushed to the beginning or end of the segment, anyway.)

These are protocol and transport issues.

> I suspect we're arguing semantics at such a level at this point that it doesn't matter, but the protocol cannot define a meaning: It defines a grammar.

Also restrictions and allowances on transporting messages in that grammar, including some degree of rearrangement. What the sender sees is not always what the receiver gets.

Mostly. It comes down to the grammar:

>             (HEADERS_WITH_END_SEGMENT | DATA_WITH_END_SEGMENT)

Having these two symbols allows for saving 8 bytes, but introduces possible application design confusion. Application designers need to decide whether the two symbols should (or should not) mean the same thing, and API designers whether to support the distinction. Such support actually requires three symbols, with an additional AUTOSELECT_END_SEGMENT to be used by the sender.

Given my suspicions about coalescing data across headers, right now I’m thinking that each HEADERS frame should start a new “message” and all of segmentation is redundant. Applications that want a sequence of data-only messages with no metadata can spend 8 bytes on an empty HEADERS frame. Header-only protocols see no overhead.

In any case, 8 bytes is only equal to the overhead of the extra DATA frame that any segmentation implicitly requires, and nothing compared to the total overhead of flushing which is also likely to happen. We shouldn’t sacrifice anything for the sake of 8 bytes.

—

The earlier BNF was imprecise, and it might help the big picture to definitively record the application-level view.

The current spec:

stream:
	header-block segment* unterminated-segment? (end-stream|rst-stream)

segment:
	unterminated-segment (headers-end-segment | data-end-segment)

unterminated-segment:
	header-block* data-octet*
(Transport may move the headers relative to the data, such that their order within a segment is insignificant.)

What we get by fixing the location of all header blocks in the data stream, sacrificing multiple header blocks within a segment (replaceable by a user-defined x-begin-message header), and adding 8 bytes per segment that doesn’t start with headers:

stream:
	segment+ (end-stream|rst-stream)

segment:
	header-block data-octet*

I think this better matches what application designers expect. I didn’t include use of END_SEGMENT as an abnormal termination indicator in the list of sacrifices, because RST_STREAM already does that.

It’s also much simpler to correctly specify, and describe usage.

---

From: Roberto Peon <[email-masked]>
Date: Sun, 20 Apr 2014 04:27:05 -0700

On Sun, Apr 20, 2014 at 3:58 AM, David Krauss <[email-masked]> wrote:

> Interoperable implementations of the protocol may not understand each
> other at the application layer. That is not a problem the protocol can
> solve.
>
>
> I’m talking about whether a proxy is allowed to internally use a generic
> END_SEGMENT symbol, or if it must distinguish the case of a bit in an empty
> DATA or HEADERS frame following a frame of the other type. In this way the
> application layer is creeping into transport.
>
> Additional questions I still have are whether data may be coalesced across
> a headers block, and whether header blocks in the same segment may be
> coalesced. I don’t think there are many use-cases for either, and they
> could be surprising to applications that forget END_SEGMENT. However, the
> first case does seem to be allowed according to the current spec. (It says
> nothing about coalescing headers, but then again I can’t find where it
> explicitly describes coalescing data either. If headers are not guaranteed
> to arrive at any particular point, they could all get pushed to the
> beginning or end of the segment, anyway.)
>

You're ascribing a semantic that I'm not thinking is the semantic of the
protocol.
The protocol ensures that any END_SEGMENT occurs at the same byte offset
from the first byte of a stream as when it was created.
Similarly, the protocol ensures that HEADERS are at the same byte offset.

> Also restrictions and allowances on transporting messages in that grammar,
> including some degree of rearrangement. What the sender sees is not always
> what the receiver gets.
>

That depends on how it is defined. As I state above, END_SEGMENT or HEADERS
are always at the same byte offset in the datastream.
Frames otherwise have zero semantic meaning, as they can be broken
up/coalesced at will.

> Having these two symbols allows for saving 8 bytes, but introduces
> possible application design confusion. Application designers need to decide
> whether the two symbols should (or should not) mean the same thing, and API
> designers whether to support the distinction. Such support actually
> requires *three* symbols, with an additional AUTOSELECT_END_SEGMENT to be
> used by the sender.
>

They always mean the same thing. Frame-level details should not be surfaced
at the application layer. HTTP2 is not a frame-oriented protocol. It is
either message (END_SEGMENT) or bytestream. Frames are subordinate to
either of these and form the building blocks for creating streams of
messages.

>
> Given my suspicions about coalescing data across headers, right now I’m
> thinking that each HEADERS frame should start a new “message” and all of
> segmentation is redundant. Applications that want a sequence of data-only
> messages with no metadata can spend 8 bytes on an empty HEADERS frame.
> Header-only protocols see no overhead.
>

No.

>
> In any case, 8 bytes is only equal to the overhead of the extra DATA frame
> that any segmentation implicitly requires, and nothing compared to the
> total overhead of flushing which is also likely to happen. We shouldn’t
> sacrifice anything for the sake of 8 bytes.
>

The flag bits are there to be used, there is no sacrifice here :)

>
> —
>
> The earlier BNF was imprecise, and it might help the big picture to
> definitively record the application-level view.
>
> The current spec:
>
> stream:
> (Transport may move the headers relative to the data, such that their
> order within a segment is insignificant.)
>
>
Applications should never see frames. They should probably get things like:
 Got headers on the stream. Got bytes on the stream. Got end of message.
Got end of stream.

> What we get by fixing the location of all header blocks in the data
> stream, sacrificing multiple header blocks within a segment (replaceable by
> a user-defined x-begin-message header), and adding 8 bytes per segment
> that doesn’t start with headers:
>
> stream:
> I think this better matches what application designers expect. I didn’t
> include use of END_SEGMENT as an abnormal termination indicator in the list
> of sacrifices, because RST_STREAM already does that.
>

Application designers should never see the frame-level stuff. If they're
ascribing semantic value to the frames, they're doing it wrong and their
application *will* break as it goes throug a proxy. The application-layer
grammar's atoms are: metadata, bytes, end-of-message, end-of-stream.

-=R

---

From: Adrian Cole <[email-masked]>
Date: Sun, 20 Apr 2014 09:18:54 -0700

Two really helpful sentences that could make it into the draft or
somewhere else to make this more clear.
"The message may not have been completed when the stream was
terminated." <- explains END_STREAM w/o END_END_SEGMENT
"The application-layer grammar's atoms are: metadata, bytes,
end-of-message, end-of-stream." <- message is more clear to me than
segment, spec could align these concepts in text and example.

I would like to reference things like above, or even better examples
from a spec draft when implementing seems to be best named
"end-of-message" semantics.  I think I'm getting close to
understanding how to expose this, so thanks!
https://github.com/square/okhttp/issues/725

-A

On Sun, Apr 20, 2014 at 4:27 AM, Roberto Peon <[email-masked]> wrote:
>>> intermediaries may change, or to a lesser extent what synonymous bit-codings
>>> portable APIs (e.g. Javascript XHR) may merge. If an underlying
>> coalesced. I don’t think there are many use-cases for either, and they could
>> be surprising to applications that forget END_SEGMENT. However, the first
>> case does seem to be allowed according to the current spec. (It says nothing
>> about coalescing headers, but then again I can’t find where it explicitly
>> describes coalescing data either. If headers are not guaranteed to arrive at
>> any particular point, they could all get pushed to the beginning or end of
>> the segment, anyway.)
>
>
>>> to text, but it’s best to use the same approach either way. A format defines
>>> the expression of a variety of messages, and those messages comprise the
>>> only defined meaning.
>>>
>>
>> designers whether to support the distinction. Such support actually requires
>> three symbols, with an additional AUTOSELECT_END_SEGMENT to be used by the
>> sender.
>
>
> either message (END_SEGMENT) or bytestream. Frames are subordinate to either
> of these and form the building blocks for creating streams of messages.
>
>>
>>
>> that any segmentation implicitly requires, and nothing compared to the total
>> overhead of flushing which is also likely to happen. We shouldn’t sacrifice
>> anything for the sake of 8 bytes.
>
>
> Got headers on the stream. Got bytes on the stream. Got end of message. Got
> end of stream.
>
>>
>> a user-defined x-begin-message header), and adding 8 bytes per segment that
>> doesn’t start with headers:
>>
>> stream:

---

From: David Krauss <[email-masked]>
Date: Mon, 21 Apr 2014 11:30:42 +0800

On 2014–04–21, at 4:34 AM, Roberto Peon <[email-masked]> wrote:

> FYI, you did't reply to the list, and you probably should :)

Argh, I’m always forgetting to “reply all”. I’ll just not delete anything from the quoted text here. My only response is at the very end.

> On Sun, Apr 20, 2014 at 8:39 AM, David Krauss <[email-masked]> wrote:
> 
> On 2014–04–20, at 7:27 PM, Roberto Peon <[email-masked]> wrote:
> 
>> You're ascribing a semantic that I'm not thinking is the semantic of the protocol.
>> The protocol ensures that any END_SEGMENT occurs at the same byte offset from the first byte of a stream as when it was created.
> Okay, good to know. It should be clarified in the spec.
> 
>> That depends on how it is defined. As I state above, END_SEGMENT or HEADERS are always at the same byte offset in the datastream.
>> Frames otherwise have zero semantic meaning, as they can be broken up/coalesced at will.
> 
> Well, adjacent HEADERS also cannot be coalesced, regardless of END_SEGMENT, right? You mentioned a headers-only protocol.
> 
> true
>  
> 
>> They always mean the same thing. Frame-level details should not be surfaced at the application layer. HTTP2 is not a frame-oriented protocol. It is either message (END_SEGMENT) or bytestream. Frames are subordinate to either of these and form the building blocks for creating streams of messages.
> 
> That’s the gist of my question: what the application layer may access. If (and only if) these details are available to application layer protocols, they may be changed by an intermediary. It is an identical relationship because, as you said, the protocol is the grammar. HTTP/2 isn’t litigating application-level meaning.
> 
> Gotcha.
>  
> 
> Now it’s my turn to say we’re almost entirely “violently” agreeing :) . Headers cannot be coalesced, and they always have a fixed position in the data stream. The only thing left between your current intention and elimination of segmentation is representation of the “end message” metadata as just another header, or an optimized flag bit.
> 
> In any case, headers do implement segmentation semantics even without using the END_SEGMENT bit.
> 
> Of data in a data-stream, yes, but otherwise they currently do not (unless one declared some key-value which did) denote end of message (end_of_segment).
>  
> 
> I’m concerned with interface complexity, not wire overhead or room for future flag bits.
> 
> Gotcha.
>  
> 
>> Applications should never see frames. They should probably get things like:  Got headers on the stream. Got bytes on the stream. Got end of message. Got end of stream.
> 
> Agreed, although the above grammar does not have any frames (aside from rst-stream). It condenses what the stream protocol is guaranteed to transport from one end to the other. (Except for ordering of headers and data-octets, which I was unaware of.)
> 
>> Application designers should never see the frame-level stuff. If they're ascribing semantic value to the frames, they're doing it wrong
> 
> OK. That needs to be specified. Otherwise, they have no reason not to do so.
> 
> Good point. IF that isn't clear it *really* needs to be clear, else we will have interop problems (the spec explicitly allows for coalescing/breaking up frames).
>  
> 
>> and their application *will* break as it goes throug a proxy.
> 
> The reason I’ve been asking these crazy questions about coalescing is because I’m looking for a reason something might break when it goes through a proxy. But, I don’t see one, so headers-end-segment and data-end-segment are de-facto different application-level symbols.
> 
>> The application-layer grammar's atoms are: metadata, bytes, end-of-message, end-of-stream.
> 
> Isn’t it all much simpler if end-of-message is just another piece of metadata?
> 
> To prevent abuse by applications, the protocol needs to define some canonicalization which allows proxies to mangle “wrong” usage, and shuffle the headers-end-segment and data-end-segment symbols so they become indistinguishable.
> 
> One approach would be to define an end-segment header, and define the END_SEGMENT bit to be an optimized representation for it. Then an empty DATA frame or an empty HEADERS frame with END_SEGMENT set are both encoding the same thing, and proxies may forward either as the other. END_SEGMENT on a DATA frame encodes a header set with only the end-segment header, and END_SEGMENT on a non-empty HEADER frame adds it to the set.
> 
> The issue there is that then we're not presenting an arbitrary-metadata interface with HEADERS. It would be arbitrary metadata *except* the end-segment header, which seems annoying from an application-use point of view.

It’s only a framing-level difference. This one header may be encoded without a HEADERS frame. Vice-versa, an end-segment symbol may be encoded (validly, but inefficiently) without the END_SEGMENT bit as a normal header, unless there’s a special rule against doing so. Frames aren’t visible to the application, though, so this is just the sort of separation we want.

Application-level language bindings are strictly simplified by folding any special segmentation APIs into the general header handling. The complication is moved into into header encoding/decoding, where it’s an isolated special case. For the encoder, special handling is optional (although very desirable) unless the spec requires it.

Is there some property I’m missing that sets end-segment apart from (other) headers? Both require subsequent data to start a new DATA frame. Although that’s not directly visible to the application, it guarantees delivery at a particular byte offset. Both may also encourage a flush that might otherwise be deferred by too little buffered data. What else is there?

---

From: Roberto Peon <[email-masked]>
Date: Sun, 20 Apr 2014 21:34:11 -0700

An empty HEADERS frame may not actually convey zero metadata, if HPACK is
being used.

If one wishes to overload HEADERS to signal END_SEGMENT, then one must:
1) ensure that applications do not use the same keyspace as the reserved
key.
   - can be done, but "smells bad"
2) ensure that the compressor state is modified properly to convey *only*
the key
   - might hurt a bit, as this essentially renders the compressor useless...
3) ensure that an empty metadata frame does not convey some meaning.
   - Not guaranteed today: an empty METADATA frame might convey some
meaning at the application layer.

END_SEGMENT as a bit avoids these problems.

I agree that we might want more text in there to clarify what should be
visible to the application (i.e. *not* frames, but rather metadata, data,
end-segment, end-stream), subject to editor approval.

-=R

On Sun, Apr 20, 2014 at 8:30 PM, David Krauss <[email-masked]> wrote:

> Argh, I’m always forgetting to “reply all”. I’ll just not delete anything
> from the quoted text here. My only response is at the very end.
>
>
>> That depends on how it is defined. As I state above, END_SEGMENT or
>> HEADERS are always at the same byte offset in the datastream.
>> Well, adjacent HEADERS also cannot be coalesced, regardless of
>> END_SEGMENT, right? You mentioned a headers-only protocol.
>>
>
> true
>
>
>>
>> They always mean the same thing. Frame-level details should not be
>> surfaced at the application layer. HTTP2 is not a frame-oriented protocol.
>> It is either message (END_SEGMENT) or bytestream. Frames are subordinate to
>> That’s the gist of my question: what the application layer may access. If
>> (and only if) these details are available to application layer protocols,
>> they may be changed by an intermediary. It is an identical relationship
>> because, as you said, the protocol is the grammar. HTTP/2 isn’t litigating
>> application-level meaning.
>>
>
> Gotcha.
>
>
>>
>> Now it’s my turn to say we’re almost entirely “violently” agreeing :) .
>> Headers cannot be coalesced, and they always have a fixed position in the
>> data stream. The only thing left between your current intention and
>> elimination of segmentation is representation of the “end message” metadata
>> as just another header, or an optimized flag bit.
>>
> Of data in a data-stream, yes, but otherwise they currently do not (unless
> one declared some key-value which did) denote end of message
> (end_of_segment).
>
>
>>
>>  In any case, 8 bytes is only equal to the overhead of the extra DATA
>>> frame that any segmentation implicitly requires, and nothing compared to
>>> the total overhead of flushing which is also likely to happen. We shouldn’t
>> Applications should never see frames. They should probably get things
>> like:  Got headers on the stream. Got bytes on the stream. Got end of
>> message. Got end of stream.
>>
>>
>> Agreed, although the above grammar does not have any frames (aside from
>> rst-stream). It condenses what the stream protocol is guaranteed to
>> transport from one end to the other. (Except for ordering of headers and
>> data-octets, which I was unaware of.)
>>
>> ascribing semantic value to the frames, they're doing it wrong
>>
>>
> Good point. IF that isn't clear it *really* needs to be clear, else we
> will have interop problems (the spec explicitly allows for
> coalescing/breaking up frames).
>
>
>>
>> The reason I’ve been asking these crazy questions about coalescing is
>> because I’m looking for a reason something might break when it goes through
>> a proxy. But, I don’t see one, so headers-end-segment and
>> data-end-segment are de-facto different application-level symbols.
>>
>> To prevent abuse by applications, the protocol needs to define some
>> canonicalization which allows proxies to mangle “wrong” usage, and shuffle
>> the headers-end-segment and data-end-segment symbols so they become
>> indistinguishable.
>>
>> One approach would be to define an end-segment header, and define the
>> END_SEGMENT bit to be an optimized representation for it. Then an empty
>> DATA frame or an empty HEADERS frame with END_SEGMENT set are both encoding
>> the same thing, and proxies may forward either as the other. END_SEGMENT on
>> a DATA frame encodes a header set with only the end-segment header, and
>> END_SEGMENT on a non-empty HEADER frame adds it to the set.
>>
>
> The issue there is that then we're not presenting an arbitrary-metadata
> interface with HEADERS. It would be arbitrary metadata *except* the
> end-segment header, which seems annoying from an application-use point of
> view.
>
>
> It’s only a framing-level difference. This one header may be encoded
> without a HEADERS frame. Vice-versa, an end-segment symbol may be encoded
> (validly, but inefficiently) without the END_SEGMENT bit as a normal
> header, unless there’s a special rule against doing so. Frames aren’t
> visible to the application, though, so this is just the sort of separation
> we want.
>
> Application-level language bindings are strictly simplified by folding any
> special segmentation APIs into the general header handling. The
> complication is moved into into header encoding/decoding, where it’s an
> isolated special case. For the encoder, special handling is optional
> (although very desirable) unless the spec requires it.
>
> Is there some property I’m missing that sets end-segment apart from
> (other) headers? Both require subsequent data to start a new DATA frame.
> Although that’s not directly visible to the application, it guarantees
> delivery at a particular byte offset. Both may also encourage a flush that
> might otherwise be deferred by too little buffered data. What else is there?
>
>

---

From: David Krauss <[email-masked]>
Date: Mon, 21 Apr 2014 17:41:37 +0800

There might be some misunderstanding here… the paragraph beginning “One approach would be…” in my earlier message was intended to be separate from other suggestions I’ve made. (Sorry there have been so many, but this one seems like a good compromise.)

Fleshing out the idea:

1) The end-segment symbol is transported by an end-segment header.

2) The end-segment header may be encoded (with an empty or default value) by the END_SEGMENT bit. Applications don’t even need to know about this. Otherwise it’s just a normal header.

3) The END_SEGMENT bit is decoded after other HPACK decoding, equivalent to a literal encoding not added to the header table. (Perhaps I’m missing some details here… in implementation terms, it’s simply not part of the decoder but tacked on afterward.)

In practice, regular HPACK encoding of end-segment would just be monkey business. The user could give it a value, put it in the reference set, etc. It would be weird but I see no ill side-effects. It’s not so different from forgoing any other compression method of HPACK.

4) The end-segment symbol (as treated distinctly from the header) should be processed after any other headers or data in the frame, although this is only an application-level notion.

5) If empty HEADERS frames aren’t allowed, an end-segment header alone would (optimally) be sent as an empty DATA frame. It should be simple to specify that END_SEGMENT doesn’t render a frame “non-empty” per HPACK restrictions.

The reasons to do it are:

1) General header-oriented APIs can provide the end-segment symbol. Programs can make one less API call and avoid adding a special case for the special end-segment metadata, which after all isn’t actually special except in its wire representation. Of course, this doesn’t preclude having a dedicated interface for end-segment as well.

Put simply, “conceptual uniformity and canonicalization.”

2) Remove the unintended reliability of the framing-layer protocol to prohibit application usage. Separate it from the application layer without any “thou shalt not” language.

3) Easy to retrofit onto existing implementations. No new intermediary processing is required. An HTTP/2 response maps more cleanly to a sequence of HTTP/1.1 responses.

On 2014–04–21, at 12:34 PM, Roberto Peon <[email-masked]> wrote:

> An empty HEADERS frame may not actually convey zero metadata, if HPACK is being used.
> 
> 1) ensure that applications do not use the same keyspace as the reserved key.
> 2) ensure that the compressor state is modified properly to convey *only* the key
>    - Not guaranteed today: an empty METADATA frame might convey some meaning at the application layer.
> 
> I agree that we might want more text in there to clarify what should be visible to the application (i.e. *not* frames, but rather metadata, data, end-segment, end-stream), subject to editor approval.
> 
> -=R

---

From: Roberto Peon <[email-masked]>
Date: Mon, 21 Apr 2014 11:14:51 -0700

On Mon, Apr 21, 2014 at 2:41 AM, David Krauss <[email-masked]> wrote:

> There might be some misunderstanding here… the paragraph beginning “One
> approach would be…” in my earlier message was intended to be separate from
> other suggestions I’ve made. (Sorry there have been so many, but this one
> seems like a good compromise.)
>
> Fleshing out the idea:
>
> 2) The end-segment header may be encoded (with an empty or default value)
> by the END_SEGMENT bit. Applications don’t even need to know about this.
> Otherwise it’s just a normal header.
>
>
I believe you're saying that, at the application-layer the application is
notified of the end of a segment by the transformation of an END_SEGMENT
bit in the protocol to an END_SEGMENT header visible to the application?

> 3) The END_SEGMENT bit is decoded after other HPACK decoding, equivalent
> to a literal encoding not added to the header table. (Perhaps I’m missing
> some details here… in implementation terms, it’s simply not part of the
> decoder but tacked on afterward.)
>
>
In practice, regular HPACK encoding of end-segment would just be monkey
> business. The user could give it a value, put it in the reference set, etc.
> It would be weird but I see no ill side-effects. It’s not so different from
> forgoing any other compression method of HPACK.
>

I think you mean that end-segment is represented as a header key with a
special value in the reference set, and thus the index is special/becomes
an opcode.

>
> 4) The end-segment symbol (as treated distinctly from the header) should
> be processed after any other headers or data in the frame, although this is
> only an application-level notion.
>
> 5) If empty HEADERS frames aren’t allowed, an end-segment header alone
> would (optimally) be sent as an empty DATA frame. It should be simple to
> specify that END_SEGMENT doesn’t render a frame “non-empty” per HPACK
> restrictions.
>

They are currently allowed-- with HACK, this would imply the same request
was sent again, at least for HTTP.

> 1) General header-oriented APIs can provide the end-segment symbol.
> Programs can make one less API call and avoid adding a special case for the
> special end-segment metadata, which after all isn’t actually special except
> in its wire representation. Of course, this doesn’t preclude having a
> dedicated interface for end-segment as well.
>
> 2) Remove the unintended reliability of the framing-layer protocol to
> prohibit application usage. Separate it from the application layer without
> any “thou shalt not” language.
>
> 3) Easy to retrofit onto existing implementations. No new intermediary
> processing is required. An HTTP/2 response maps more cleanly to a sequence
> of HTTP/1.1 responses.
>

Apis could indicate end-of-whatever however they wish, e.g. by the method
you propose here. The current specification would be adaptable to your API
without changes.

Here is some pseudo-code which would implement your API:

# Sets END_SEGMENT on an empty DATA frame if there is no metadata to send,
# else sets END_SEGMENT on a HEADERS frame if there is metadata to send.
SendHeaders(headers):

  if len(headers) == 1 and ":END_SEGMENT" in headers.keys():
    df = NewDataFrame()
    df.SetBit(END_SEGMENT)
    outputStream.append(df)
    return

  hf = NewHeaderFrame()
  for k,v in headers:
    if ":END_SEGMENT" == k:
      hf.setBit(END_SEGMENT)

    hf.appendEncodedHeader(HPACK.encodeHeader(k,v))
  outputStream.append(hf)
-=R

---

From: David Krauss <[email-masked]>
Date: Tue, 22 Apr 2014 08:31:08 +0800

On 2014–04–22, at 2:14 AM, Roberto Peon <[email-masked]> wrote:

> I believe you're saying that, at the application-layer the application is notified of the end of a segment by the transformation of an END_SEGMENT bit in the protocol to an END_SEGMENT header visible to the application?

Yes.

> I think you mean that end-segment is represented as a header key with a special value in the reference set, and thus the index is special/becomes an opcode.

I’m less qualified to design this at such a level of specificity. But, I would think that there is no need for an index or ever purposely putting it in the header table or reference set. Under normal circumstances the bit is the only determinant of whether the header/symbol/event is delivered to the user. There’s no need to modify the HPACK algorithm or tables.

> They are currently allowed-- with HPACK, this would imply the same request was sent again, at least for HTTP.

OK, now I see the “Reference Set Emptying” flag… so, extrapolating my rule, an empty HEADERS frame with END_SEGMENT would repeat the previous headers but with the addition of end-segment (and no change to the table). Doesn’t sound very useful, but still harmless.

>     hf.appendEncodedHeader(HPACK.encodeHeader(k,v)) 

I would do this a bit differently. The bit encodes an empty (or perhaps a default) value, and HPACK should only be involved otherwise.

if “:end-segment” == k and “” == v:
  hf.setBit( END_SEGMENT )
else
  hf.appendEncodedHeader(HPACK.encodeHeader(k,v)) 

A similar change needs to branch around the data frame special case.

>   outputStream.append(hf)

Yep, the only real changes I’m proposing are:

1) The :end-segment key is reserved to HTTP. (Your pseudocode reminds me that the prefix colon already reserves a name from the application, but is :end-segment actually available to generalized APIs?)

2) An intermediary may translate the bit to an actual header field representation or vice versa, just as a convenient technicality.