Implicitly opened streams and exposing stream IDs

Marten Seemann <> Mon, 02 April 2018 11:43 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 68CDC126BF7 for <>; Mon, 2 Apr 2018 04:43:37 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -2.699
X-Spam-Status: No, score=-2.699 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (2048-bit key)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id m8BfmbkQmBbn for <>; Mon, 2 Apr 2018 04:43:35 -0700 (PDT)
Received: from ( [IPv6:2607:f8b0:4001:c06::231]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by (Postfix) with ESMTPS id AB0BD124D37 for <>; Mon, 2 Apr 2018 04:43:35 -0700 (PDT)
Received: by with SMTP id v13so17617456iob.6 for <>; Mon, 02 Apr 2018 04:43:35 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=VSlU97dst+gJDVGf4GJnJnIb0BW2Zfp0vdQ7zB4CzW0=; b=JjBautpKN4MThiBANOS6N9eyI2FDxZFEZsBSHR8SbcuNohIwzeeDLQg5X9jWbDKbna d6XIcCD2PYugdb9ahpZ7cXlmSmg2Ag/3hcaaeB9nN6hzfQZtNPxoicQ5xoaZNT3iQImm 6YnD4pm8NYyaM4ADtgCB31y2w9zTdF+z+pWhvatgzgppkYDPSUeV41XpcJFVDqywX1zH lwVask5sX1QgASKzDPzvOC2Ye07Gf10givRjdNWNgTCUN1GYcSJKc8nAsV11fQFIQvOt v71C+g14pIMbyz+PdK1W43+OA2+ZeADKRsDigkLpM2yEwNnNwfdPtCvJQh0+bTjevek6 EaMw==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=VSlU97dst+gJDVGf4GJnJnIb0BW2Zfp0vdQ7zB4CzW0=; b=pmqG0Q2zk7mL3I+Hvu8XLcAdKSlc2GB183KSHdWQruagFfE8h81djYn6zDAyCrAruN EaHLHsLiqUWz9GLAFPHXOZ1SPj/dbGJhvnTdmhOsEBezirLvQNOHCuoBxLcl0Eu8sGyL F/rHH4IZ17bfZDzEGLB7xVkwU9hcUqiH+P784R77bewGwk2CH+3R3W4+BPTUJw1AKYPw v0i+em8TRUA01qVKDGseFpDXRophWK6l4FZUzCW5orR0ha6qrXESlJlP2WZa6+Vo5FO2 tIjpDCrcc6i5Wp321xLhLFY+2EfGI35OZ/E2IxevYWyaoMfsqMTWJaqgkaeE/YtmzJ+R NmLw==
X-Gm-Message-State: AElRT7F2WzZk9ZOWkkoyxGqlbJtjV7lxZ5CXYPAUEXq8yD4Ji1eEzNt4 S+YLDD7b1OMOGTQc57FbhfYE4UX1cjPDWE5gME00FA==
X-Google-Smtp-Source: AIpwx4/K2OzArZ6OLmUuo19CD/AO5t4sue50QrpClPphtNwLnHiyxu1oTvKHtqJbxxRblQlG68w1fcsjw+i3fWojSwU=
X-Received: by with SMTP id 32mr8366830iog.24.1522669414636; Mon, 02 Apr 2018 04:43:34 -0700 (PDT)
MIME-Version: 1.0
From: Marten Seemann <>
Date: Mon, 02 Apr 2018 11:43:23 +0000
Message-ID: <>
Subject: Implicitly opened streams and exposing stream IDs
To: QUIC WG <>
Content-Type: multipart/alternative; boundary="001a113fbf5c91e1b10568dc19a1"
Archived-At: <>
X-Mailman-Version: 2.1.22
Precedence: list
List-Id: Main mailing list of the IETF QUIC working group <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Mon, 02 Apr 2018 11:43:37 -0000

Recently, the implicit opening of streams (i.e. that when a receiver
receives a frame for stream N+4, it can assume that stream N was already
opened, but the packet might have been reordered) was removed from the
draft. I think this change has some consequences that we haven't discussed
so far.

For the purpose of writing some pseudocode, I'm assuming a QUIC API that
provides an *AcceptStream()* method, but the conclusions will be the same
for a callback-based API.

   - If QUIC has implicit stream opening, *AcceptStream()* would return the
   streams in order (and if a frame opening stream N+4 is received before
   stream n is opened, *AcceptStream()* will first return stream N and then
   stream N+4).
   - Without implicit stream opening, *AcceptStream()* just returns
   whatever stream is received first. Streams might be returned in arbitrary
   order, if the peer doesn't open streams consecutively or if packets are

Now imagine an application protocol where the first unidirectional stream
opened by the client is a control stream, and all higher unidirectional
streams are data streams. The application on the server side needs to find
out which stream is the control stream, because it needs to be handled

With implicit stream opening, the server code would be:

    control_stream = AcceptStream() // is guaranteed to open the first
    // handle the control stream
     while true:
         stream = AcceptStream()
         // handle the data stream

and without implicit stream opening:

    while true:
        stream = AcceptStream()
        if stream.ID() == kControlStreamID:
            // handle the control stream
            // handle the data stream

In this case, after accepting a stream, we first have to check the stream
ID, since there's no guarantee if the control stream will actually be
received first.

For this stream mapping, it seems like the removal of implicitly opened
streams implies that QUIC has to expose stream IDs to the application
layer. I'm not sure if this was intended when making the change, especially
since we're considering to change HQ such that it doesn't rely on QUIC
stream IDs any more.
We only manage to avoid the problem described here in our HTTP mapping,
because the HTTP control streams are unidirectional and the request streams
are bidirectional, and can therefore be told apart by their directionality.
However, as a general transport protocol, other applications built on top
of QUIC will have to find some way to deal with it.