In RFC 9113, Section 6.10:
"Any number of CONTINUATION frames can be sent, as long as the preceding frame is on the same stream and is a HEADERS, PUSH_PROMISE, or CONTINUATION frame without the END_HEADERS flag set."
So, it means we can insert a frame from another stream between the HEADERS & CONTINUATION frame of one stream. But after testing with my code, I found that www.google.com does not support this spec.
With interleaving, google will reject requests. Without interleaving, Google will respond with two 404 pages.
Is there any other HTTP/2 server that supports HTTP/2 interleaving between HEADERS & CONTINUATION?
Thanks.
import hpack
import sys
import pdb
import h2.connection
def get_header_frame(sid=1, path="/", es=True):
initial_headers = [
(':method', 'GET'),
(':path', path),
(':scheme', 'https'),
(':authority', 'www.google.com')
]
encoder = hpack.Encoder()
initial_headers_encoded = encoder.encode(initial_headers)
res = b'\x00' + len(initial_headers_encoded).to_bytes(2, 'big') + b'\x01'
res += b'\x05' if es else b'\x00'
res += sid.to_bytes(length=4, byteorder="big")+initial_headers_encoded
return res
def get_cont_frame(sid=1, c=1, eh=True):
custom_headers = [
('x-%s' % (c), 'A'*1)
]
encoder = hpack.Encoder()
initial_headers_encoded = encoder.encode(custom_headers)
res = b'\x00' + len(initial_headers_encoded).to_bytes(2, 'big') + b'\x09'
res += b'\x04' if eh else b'\x00'
res += sid.to_bytes(length=4, byteorder="big")+initial_headers_encoded
return res
def get_data_frame(sid=1, es=True):
data = b''
res = b'\x00' + len(data).to_bytes(2, 'big') + b'\x00'
res += b'\x01' if res else b'\x00'
res += sid.to_bytes(length=4, byteorder="big")+data
return res
def create_http2_request(interleave=False):
conn = h2.connection.H2Connection()
conn.initiate_connection()
sys.stdout.buffer.write(conn.data_to_send())
settings_frame = conn.data_to_send()
sys.stdout.buffer.write(settings_frame)
f1_1 = get_header_frame(1, "/1", False)
f1_2 = get_cont_frame(1)
f1_3 = get_data_frame(1)
f3_1 = get_header_frame(3, "/3", False)
f3_2 = get_cont_frame(3)
f3_3 = get_data_frame(3)
if interleave:
frames = [f1_1, f3_1, f3_2, f1_2, f1_3, f3_3]
else:
frames = [f1_1, f1_2, f3_1, f3_2, f3_3, f1_3]
for x in frames:
sys.stdout.buffer.write(x)
sys.stdout.flush()
def main():
'''
Usage:
interleave: python3 good_h2_client.py 1 | openssl s_client -connect www.google.com:443 -alpn h2 -ign_eof
non-interl: python3 good_h2_client.py 0 | openssl s_client -connect www.google.com:443 -alpn h2 -ign_eof
'''
if len(sys.argv) > 1 and int(sys.argv[1]) == 1:
create_http2_request(True)
else:
create_http2_request(False)
if __name__ == '__main__':
main()
So, it means we can insert a frame from another stream between the HEADERS & CONTINUATION frame of one stream.
No you cannot. You misread the specification.
It says:
Any number of CONTINUATION frames can be sent, as long as the preceding frame is on the same stream
Emphasis is mine.