Re: Design Issue: Life-cycle of a Stream

Martin Thomson <> Wed, 01 May 2013 17:50 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 86E6A21F9A7D for <>; Wed, 1 May 2013 10:50:23 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -6.599
X-Spam-Status: No, score=-6.599 tagged_above=-999 required=5 tests=[AWL=4.000, BAYES_00=-2.599, RCVD_IN_DNSWL_HI=-8]
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id gkRaIEnfZDyd for <>; Wed, 1 May 2013 10:50:15 -0700 (PDT)
Received: from ( []) by (Postfix) with ESMTP id 477EA21F9A6B for <>; Wed, 1 May 2013 10:50:15 -0700 (PDT)
Received: from lists by with local (Exim 4.72) (envelope-from <>) id 1UXbAI-0004Bz-10 for; Wed, 01 May 2013 17:50:02 +0000
Resent-Date: Wed, 01 May 2013 17:50:02 +0000
Resent-Message-Id: <>
Received: from ([]) by with esmtp (Exim 4.72) (envelope-from <>) id 1UXbA7-0004An-9S for; Wed, 01 May 2013 17:49:51 +0000
Received: from ([]) by with esmtps (TLS1.0:RSA_ARCFOUR_SHA1:16) (Exim 4.72) (envelope-from <>) id 1UXbA6-0007nX-9M for; Wed, 01 May 2013 17:49:51 +0000
Received: by with SMTP id h11so5132920wiv.2 for <>; Wed, 01 May 2013 10:49:24 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20120113; h=mime-version:x-received:in-reply-to:references:date:message-id :subject:from:to:cc:content-type; bh=GMbt/KNrQ2zfqOvZn8Y22QstzqF6w8xmViltWrIIjMI=; b=hCKjzM2GMPhGevMyjIW+L0nnm6qahJ41mCg1/ZTgK/i6LiqRhDgsm/Z8d1J4wvqKUf cCFMknY80vk8lWnlQnZViU1y4VCVf8sSdVCkhfxUEuuoUVLf1jGJAL6oCkfU1o1hhhKR Rs+k116+StvgDUU1A4mWwiLUhH1/1G8+XIUctetAHHi0VSPefwXXIL9zduyQjjti6j2v w/Elpgboq2j8DNU4/Izahw5k3CeuwtO+FsjWsEqnmzgjJuqYikYq6sOwHr8/apoPlQQR YHF7y4OB/J2JM1z3NkPDC7PJY2X+8OEq3mWwqt2x8NdT8ksRbOTJxBlk0XoipGEl2zP6 vB6Q==
MIME-Version: 1.0
X-Received: by with SMTP id j15mr3850961wjs.30.1367430564179; Wed, 01 May 2013 10:49:24 -0700 (PDT)
Received: by with HTTP; Wed, 1 May 2013 10:49:24 -0700 (PDT)
In-Reply-To: <>
References: <>
Date: Wed, 1 May 2013 10:49:24 -0700
Message-ID: <>
From: Martin Thomson <>
To: James M Snell <>
Cc: "" <>
Content-Type: text/plain; charset=UTF-8
Received-SPF: pass client-ip=;;
X-W3C-Hub-Spam-Status: No, score=-3.5
X-W3C-Hub-Spam-Report: AWL=-2.697, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7, SPF_PASS=-0.001
X-W3C-Scan-Sig: 1UXbA6-0007nX-9M 58042023371a365f50bb04a0bd3cfa3f
Subject: Re: Design Issue: Life-cycle of a Stream
Archived-At: <>
X-Mailing-List: <> archive/latest/17767
Precedence: list
List-Id: <>
List-Help: <>
List-Post: <>
List-Unsubscribe: <>

On 1 May 2013 10:03, James M Snell <> wrote:
> 1. Streams are initialized or opened by sending a HEADERS or
> HEADERS+PRIORITY frame that uses a previously unused stream
> identifier.
> 2. Only HEADERS and HEADERS+PRIORITY frames are used to open a stream.

I don't see why 1 & 2 are necessary.  It's just arbitrary.  Sending
any frame should suffice.

> 3. PUSH_PROMISE frames reserve a stream identifier but do not open the stream.


> 4. Promised streams are only opened by sending a subsequent HEADERS or
> HEADERS+PRIORITY frame that uses the reserved stream identifier.

Again, promised streams are opened by sending frames.

> 5. All streams are either fully open, half-closed by the client,
> half-closed by the server, or fully closed.

I think of this differently.  Streams aren't bi-directional
constructs, they are a loose binding of two unidirectional constructs.
 Each unidirectional component has its own state.

FINAL ends a stream in the direction that it appears on.
RST_STREAM ends a stream in the direction that it appears on and
requests the closure of the opposite direction.

> 6. All promised streams are associated with an originating "parent" stream.

That binding is a problem.  The reason for the stream association in
push promise is to obtain request headers.  That relationship is a
function of the HTTP usage of streams, not an inherent function of the
streaming layer.

> 7. A stream is half-closed by sending a frame with the FINAL bit set.

See above, FINAL ends a stream for the direction in which it appears.

> 8. A stream becomes fully-closed when either: a) both endpoints send
> frames with the FINAL bit set or b) either endpoint sends an RST_FRAME
> with that streams identifier.


> 9*. Promised streams are automatically half-closed for the recipient.
> That is, when the server sends a PUSH_PROMISE to a client, that
> streams becomes automatically half-closed for the client. The only
> frame the client can send back to the server for that stream are
> RST_STREAM frames.
>   -- or --
> 9*. Promised streams are handled just like regular streams. The server
> opens the stream and half-closes. The stream does not fully close
> until either the client half-closes it or a RST_STREAM frame is sent
> for that stream.

Neither works particularly well for me.  If you consider independent
lifecycles for each direction of a stream, this sort of distinction is

> 10*. Sending an RST_FRAME for a parent stream fully closes that stream
> as well as all associated promised streams.
>   -- or --
> 10*. Sending an RST_FRAME for a parent stream only closes that stream
> and has no affect on associated promised streams.

The latter, please.

> 11*. Fully closing a parent stream using symmetric half-closes will
> fully close all associated promised streams.
>   -- or --
> 11*. Fully closing a parent stream using symmetric half-closes has no
> affect on associated promised streams.

The latter, please.

> 12. Half or fully closing a promised stream has no impact on the
> parent stream or any associated sibling promised streams.

Of course.

> Ok, so that covers the lifecycle.. let's talk about processing.. this
> is largely a reiteration of my previous note on this but I want to
> make sure I've hit all the major points in how this is categorized.
> Tier 1: Includes the following...
> A. Reception and basic parsing of frames. "Basic parsing" here means
> looking at the header bytes to determine the frame type, making note
> of the flags, frame size and stream identifier, and performing any
> necessary header block decompression and state management.
> B. Handling of all session frames (stream id #0). This means that
> upgrade negotiation, SETTINGS, PING, flow control and session level
> error handling are all Tier 1 processes.

You probably need more than one dimension in your taxonomy to get this
right.  I don't see this as a function of the thing that processes

> C. Basic lifecycle handling of stream frames. This includes
> determination of whether a stream is open or closed, whether or not
> additional frames are allowed for a stream, validation of proper
> stream id use, reservation of stream id's with push_promise, and
> handling of the FINAL flag.

I'm assuming that there is a stream controller that takes all
stream-related frames and does things to the stream state.  That
controller looks at FINAL and consumes things like RST_STREAM.  But
that operates at what I consider to be Tier 2.  It also has its own
layering because flow control exists here.

> Tier 2: Includes the following...
> A. Frame-type specific processing. Validation and handling of frame
> payloads and frame-specific flags, not including header block
> processing (which is tier 1)
> B. Additional stream-level error handling not related to stream-lifecycle
> Tier 3: Includes all application-level handling (HTTP Semantics)
> So, when we say things like "an endpoint must be prepared to continue
> receiving frames on a closed stream", we mean to say that "an endpoint
> must be prepared to do tier 1 processing on all frames, even those
> sent for closed streams."
> Please weigh in on whatever items y'all feel are controversial. I'll
> work up proposed spec edits based on whatever feedback is received.
> - James