On Wed, Mar 25, 2026 at 02:26:16PM +1100, Martin Thomson wrote: > If you send X bytes, that will be split into segments. That means that you > will get either a complete TLS record or not, because some segments are > missing or delayed. At the TLS layer, it's all or nothing: you can't release > bytes until the entire record is present. > > In that case, if you have a 1:1:1:1 write:frame:QMux record:TLS record ratio, > there is zero difference between the options. Where things break down is on > the last pairing. > > If you have QMux records split across TLS records, if you are going to > realize the benefits you are touting, you need to wait until the next TLS > record is present. However, if you are willing to incrementally process, you > can process the partial STREAM frame in either model. The record-based > model, which encourages implementations to wait for entire records, remains > blocked if they take that option.
It's also important to consider lower layers (TCP). In haproxy we have implemented dynamic TLS record sizing a while ago because it improves page load time. The principle is that you do not want to send full TLS records at the beginning of a connection because they can be larger than the TCP congestion window. If you send more than 10 MSS (was even 2 in the past), the peer won't be able to decrypt them at all before waiting for another round trip to get the remaining pieces that allow to decrypt the record and start processing the frames it contains. A typical TLS record of an HTTP response would contain a HEADERS frame and the beginning of a DATA frame. The client would start to parse the partial DATA frame that probably contains references to CSS to load that can be instantly fetched. Now the question is whether or not a QMux implementation would be able to process a partial STREAM frame to pass it to the H3 layer that will extract the HEADERS frame then the beginning of a DATA frame, or if it will need to wait for the second RTT to get the remaining parts (I don't know how it's done in haproxy). Because here what matters to me is that we make sure that it is possible to send a short enough TLS record during the initial RTTs, that contain processable data to make certain the client doesn't have to wait for an extra RTT to start processing a partial payload. But it cannot start at 16kB initially, so if we need to send full frames, the QMux layer would need to adapt to the dynamic TLS record size. Willy
