At this point the client's receive window (65535) is totally consumed! So the client updates the windows after its flushed its data:
The server the sends just one more byte of data, and then trailers, to complete the response:
But the client isn't happy about this at all, and it fails incorrectly with a flow control error:
1 0.000000 55082 8888 TCP 88 55082 → 8888 [SYN, ECN, CWR] Seq=0 Win=65535 Len=0 MSS=16324 WS=32 TSval=1346047041 TSecr=0 SACK_PERM=1 ::1 → ::1
Frame 1: 88 bytes on wire (704 bits), 88 bytes captured (704 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 0, Len: 0
2 0.000083 8888 55082 TCP 88 8888 → 55082 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=16324 WS=32 TSval=1346047041 TSecr=1346047041 SACK_PERM=1 ::1 → ::1
Frame 2: 88 bytes on wire (704 bits), 88 bytes captured (704 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 0, Ack: 1, Len: 0
3 0.000096 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=1 Ack=1 Win=407776 Len=0 TSval=1346047041 TSecr=1346047041 ::1 → ::1
Frame 3: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 1, Ack: 1, Len: 0
4 0.000111 8888 55082 TCP 76 [TCP Window Update] 8888 → 55082 [ACK] Seq=1 Ack=1 Win=407776 Len=0 TSval=1346047041 TSecr=1346047041 ::1 → ::1
Frame 4: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 1, Ack: 1, Len: 0
5 0.000274 55082 8888 HTTP2 100 Magic ::1 → ::1
Frame 5: 100 bytes on wire (800 bits), 100 bytes captured (800 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 1, Ack: 1, Len: 24
HyperText Transfer Protocol 2
Stream: Magic
Magic: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
6 0.000295 8888 55082 TCP 76 8888 → 55082 [ACK] Seq=1 Ack=25 Win=407776 Len=0 TSval=1346047041 TSecr=1346047041 ::1 → ::1
Frame 6: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 1, Ack: 25, Len: 0
7 0.000314 8888 55082 HTTP2 85 SETTINGS ::1 → ::1
Frame 7: 85 bytes on wire (680 bits), 85 bytes captured (680 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 1, Ack: 25, Len: 9
HyperText Transfer Protocol 2
Stream: SETTINGS, Stream ID: 0, Length 0
Length: 0
Type: SETTINGS (4)
Flags: 0x00
.... ...0 = ACK: False
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
8 0.000331 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=25 Ack=10 Win=407776 Len=0 TSval=1346047041 TSecr=1346047041 ::1 → ::1
Frame 8: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 25, Ack: 10, Len: 0
9 0.000335 8888 55082 HTTP2 89 WINDOW_UPDATE ::1 → ::1
Frame 9: 89 bytes on wire (712 bits), 89 bytes captured (712 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 10, Ack: 25, Len: 13
HyperText Transfer Protocol 2
Stream: WINDOW_UPDATE, Stream ID: 0, Length 4
Length: 4
Type: WINDOW_UPDATE (8)
Flags: 0x00
0000 0000 = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 1110 1111 1111 1111 0001 = Window Size Increment: 983025
10 0.000343 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=25 Ack=23 Win=407776 Len=0 TSval=1346047041 TSecr=1346047041 ::1 → ::1
Frame 10: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 25, Ack: 23, Len: 0
11 0.000940 55082 8888 HTTP2 94 SETTINGS, SETTINGS ::1 → ::1
Frame 11: 94 bytes on wire (752 bits), 94 bytes captured (752 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 25, Ack: 23, Len: 18
HyperText Transfer Protocol 2
Stream: SETTINGS, Stream ID: 0, Length 0
Length: 0
Type: SETTINGS (4)
Flags: 0x00
.... ...0 = ACK: False
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
Stream: SETTINGS, Stream ID: 0, Length 0
Length: 0
Type: SETTINGS (4)
Flags: 0x01
.... ...1 = ACK: True
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
12 0.000957 8888 55082 TCP 76 8888 → 55082 [ACK] Seq=23 Ack=43 Win=407744 Len=0 TSval=1346047042 TSecr=1346047042 ::1 → ::1
Frame 12: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 23, Ack: 43, Len: 0
13 0.001033 8888 55082 HTTP2 85 SETTINGS ::1 → ::1
Frame 13: 85 bytes on wire (680 bits), 85 bytes captured (680 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 23, Ack: 43, Len: 9
HyperText Transfer Protocol 2
Stream: SETTINGS, Stream ID: 0, Length 0
Length: 0
Type: SETTINGS (4)
Flags: 0x01
.... ...1 = ACK: True
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
14 0.001056 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=43 Ack=32 Win=407744 Len=0 TSval=1346047042 TSecr=1346047042 ::1 → ::1
Frame 14: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 43, Ack: 32, Len: 0
15 2.021388 55082 8888 HTTP2 173 HEADERS, DATA ::1 → ::1
Frame 15: 173 bytes on wire (1384 bits), 173 bytes captured (1384 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 43, Ack: 32, Len: 97
HyperText Transfer Protocol 2
Stream: HEADERS, Stream ID: 1, Length 70
Length: 70
Type: HEADERS (1)
Flags: 0x04
.... ...0 = End Stream: False
.... .1.. = End Headers: True
.... 0... = Padded: False
..0. .... = Priority: False
00.0 ..0. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Header Block Fragment: 8387418aa0e41d139d09b8ebaebb048f6109b0a84afb4a8a...
[Header Length: 194]
[Header Count: 7]
Header: :method: POST
Name Length: 7
Name: :method
Value Length: 4
Value: POST
Representation: Indexed Header Field
Index: 3
Header: :scheme: https
Name Length: 7
Name: :scheme
Value Length: 5
Value: https
Representation: Indexed Header Field
Index: 7
Header: :authority: localhost:7777
Name Length: 10
Name: :authority
Value Length: 14
Value: localhost:7777
Representation: Literal Header Field with Incremental Indexing - Indexed Name
Index: 1
Header: :path: /strest.Responder/Get
Name Length: 5
Name: :path
Value Length: 21
Value: /strest.Responder/Get
Representation: Literal Header Field without Indexing - Indexed Name
Index: 4
Header: content-type: application/grpc
Name Length: 12
Name: content-type
Value Length: 16
Value: application/grpc
Representation: Literal Header Field with Incremental Indexing - Indexed Name
Index: 31
Header: user-agent: grpc-go/1.4.0-dev
Name Length: 10
Name: user-agent
Value Length: 17
Value: grpc-go/1.4.0-dev
Representation: Literal Header Field with Incremental Indexing - Indexed Name
Index: 58
Header: te: trailers
Name Length: 2
Name: te
Value Length: 8
Value: trailers
Representation: Literal Header Field with Incremental Indexing - New Name
Padding:
Stream: DATA, Stream ID: 1, Length 9
Length: 9
Type: DATA (0)
Flags: 0x01
.... ...1 = End Stream: True
.... 0... = Padded: False
0000 .00. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Data: 000000000408edff03
Padding:
16 2.021431 8888 55082 TCP 76 8888 → 55082 [ACK] Seq=32 Ack=140 Win=407648 Len=0 TSval=1346049051 TSecr=1346049051 ::1 → ::1
Frame 16: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 32, Ack: 140, Len: 0
17 2.023249 8888 55082 HTTP2 99 HEADERS ::1 → ::1
Frame 17: 99 bytes on wire (792 bits), 99 bytes captured (792 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 32, Ack: 140, Len: 23
HyperText Transfer Protocol 2
Stream: HEADERS, Stream ID: 1, Length 14
Length: 14
Type: HEADERS (1)
Flags: 0x04
.... ...0 = End Stream: False
.... .1.. = End Headers: True
.... 0... = Padded: False
..0. .... = Priority: False
00.0 ..0. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Header Block Fragment: 885f8b1d75d0620d263d4c4d6564
[Header Length: 54]
[Header Count: 2]
Header: :status: 200
Name Length: 7
Name: :status
Value Length: 3
Value: 200
Representation: Indexed Header Field
Index: 8
Header: content-type: application/grpc
Name Length: 12
Name: content-type
Value Length: 16
Value: application/grpc
Representation: Literal Header Field with Incremental Indexing - Indexed Name
Index: 31
Padding:
18 2.023281 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=140 Ack=55 Win=407744 Len=0 TSval=1346049052 TSecr=1346049052 ::1 → ::1
Frame 18: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 140, Ack: 55, Len: 0
19 2.023332 8888 55082 TCP 16388 [TCP segment of a reassembled PDU] ::1 → ::1
Frame 19: 16388 bytes on wire (131104 bits), 16388 bytes captured (131104 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 55, Ack: 140, Len: 16312
HyperText Transfer Protocol 2
20 2.023338 8888 55082 HTTP2 157 DATA ::1 → ::1
Frame 20: 157 bytes on wire (1256 bits), 157 bytes captured (1256 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 16367, Ack: 140, Len: 81
[2 Reassembled TCP Segments (16393 bytes): #19(16312), #20(81)]
HyperText Transfer Protocol 2
Stream: DATA, Stream ID: 1, Length 16384
Length: 16384
Type: DATA (0)
Flags: 0x00
.... ...0 = End Stream: False
.... 0... = Padded: False
0000 .00. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Data: 000000fffb0aedff0378415767756a574276774676636943...
Padding:
21 2.023371 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=140 Ack=16448 Win=391328 Len=0 TSval=1346049052 TSecr=1346049052 ::1 → ::1
Frame 21: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 140, Ack: 16448, Len: 0
22 2.023405 8888 55082 TCP 16388 [TCP segment of a reassembled PDU] ::1 → ::1
Frame 22: 16388 bytes on wire (131104 bits), 16388 bytes captured (131104 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 16448, Ack: 140, Len: 16312
HyperText Transfer Protocol 2
23 2.023411 8888 55082 HTTP2 157 DATA ::1 → ::1
Frame 23: 157 bytes on wire (1256 bits), 157 bytes captured (1256 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 32760, Ack: 140, Len: 81
[2 Reassembled TCP Segments (16393 bytes): #22(16312), #23(81)]
HyperText Transfer Protocol 2
Stream: DATA, Stream ID: 1, Length 16384
Length: 16384
Type: DATA (0)
Flags: 0x00
.... ...0 = End Stream: False
.... 0... = Padded: False
0000 .00. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Data: 614553766d48726d49775258424a454874515656536f6149...
Padding:
24 2.023446 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=140 Ack=32841 Win=374944 Len=0 TSval=1346049052 TSecr=1346049052 ::1 → ::1
Frame 24: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 140, Ack: 32841, Len: 0
25 2.023449 8888 55082 TCP 16388 [TCP segment of a reassembled PDU] ::1 → ::1
Frame 25: 16388 bytes on wire (131104 bits), 16388 bytes captured (131104 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 32841, Ack: 140, Len: 16312
HyperText Transfer Protocol 2
26 2.023456 8888 55082 HTTP2 157 DATA ::1 → ::1
Frame 26: 157 bytes on wire (1256 bits), 157 bytes captured (1256 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 49153, Ack: 140, Len: 81
[2 Reassembled TCP Segments (16393 bytes): #25(16312), #26(81)]
HyperText Transfer Protocol 2
Stream: DATA, Stream ID: 1, Length 16384
Length: 16384
Type: DATA (0)
Flags: 0x00
.... ...0 = End Stream: False
.... 0... = Padded: False
0000 .00. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Data: 63737a534d644f48656647434d784d524f42685879594f71...
Padding:
27 2.023477 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=140 Ack=49234 Win=358560 Len=0 TSval=1346049052 TSecr=1346049052 ::1 → ::1
Frame 27: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 140, Ack: 49234, Len: 0
28 2.023486 8888 55082 TCP 16388 [TCP segment of a reassembled PDU] ::1 → ::1
Frame 28: 16388 bytes on wire (131104 bits), 16388 bytes captured (131104 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 49234, Ack: 140, Len: 16312
HyperText Transfer Protocol 2
29 2.023491 8888 55082 HTTP2 156 DATA ::1 → ::1
Frame 29: 156 bytes on wire (1248 bits), 156 bytes captured (1248 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 65546, Ack: 140, Len: 80
[2 Reassembled TCP Segments (16392 bytes): #28(16312), #29(80)]
HyperText Transfer Protocol 2
Stream: DATA, Stream ID: 1, Length 16383
Length: 16383
Type: DATA (0)
Flags: 0x00
.... ...0 = End Stream: False
.... 0... = Padded: False
0000 .00. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Data: 4e6e465963446750796e7459796f71685964454171506542...
Padding:
30 2.023526 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=140 Ack=65626 Win=342144 Len=0 TSval=1346049052 TSecr=1346049052 ::1 → ::1
Frame 30: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 140, Ack: 65626, Len: 0
31 2.028577 55082 8888 HTTP2 102 WINDOW_UPDATE, WINDOW_UPDATE ::1 → ::1
Frame 31: 102 bytes on wire (816 bits), 102 bytes captured (816 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 140, Ack: 65626, Len: 26
HyperText Transfer Protocol 2
Stream: WINDOW_UPDATE, Stream ID: 0, Length 4
Length: 4
Type: WINDOW_UPDATE (8)
Flags: 0x00
0000 0000 = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 1111 1111 1111 1111 = Window Size Increment: 65535
Stream: WINDOW_UPDATE, Stream ID: 1, Length 4
Length: 4
Type: WINDOW_UPDATE (8)
Flags: 0x00
0000 0000 = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 1111 1111 1111 1111 = Window Size Increment: 65535
32 2.028616 8888 55082 TCP 76 8888 → 55082 [ACK] Seq=65626 Ack=166 Win=407616 Len=0 TSval=1346049057 TSecr=1346049056 ::1 → ::1
Frame 32: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 65626, Ack: 166, Len: 0
33 2.028713 8888 55082 HTTP2 86 DATA ::1 → ::1
Frame 33: 86 bytes on wire (688 bits), 86 bytes captured (688 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 65626, Ack: 166, Len: 10
HyperText Transfer Protocol 2
Stream: DATA, Stream ID: 1, Length 1
Length: 1
Type: DATA (0)
Flags: 0x00
.... ...0 = End Stream: False
.... 0... = Padded: False
0000 .00. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Data: 14
Padding:
34 2.028748 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=166 Ack=65636 Win=342144 Len=0 TSval=1346049057 TSecr=1346049057 ::1 → ::1
Frame 34: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 166, Ack: 65636, Len: 0
35 2.028943 8888 55082 HTTP2 109 HEADERS ::1 → ::1
Frame 35: 109 bytes on wire (872 bits), 109 bytes captured (872 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 65636, Ack: 166, Len: 33
HyperText Transfer Protocol 2
Stream: HEADERS, Stream ID: 1, Length 24
Length: 24
Type: HEADERS (1)
Flags: 0x05
.... ...1 = End Stream: True
.... .1.. = End Headers: True
.... 0... = Padded: False
..0. .... = Priority: False
00.0 ..0. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Header Block Fragment: 40889acac8b21234da8f013040899acac8b5254207317f00
[Header Length: 40]
[Header Count: 2]
Header: grpc-status: 0
Name Length: 11
Name: grpc-status
Value Length: 1
Value: 0
Representation: Literal Header Field with Incremental Indexing - New Name
Header: grpc-message:
Name Length: 12
Name: grpc-message
Value Length: 0
Value:
Representation: Literal Header Field with Incremental Indexing - New Name
Padding:
36 2.028982 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=166 Ack=65669 Win=342112 Len=0 TSval=1346049057 TSecr=1346049057 ::1 → ::1
Frame 36: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 166, Ack: 65669, Len: 0
37 2.029726 55082 8888 HTTP2 93 GOAWAY ::1 → ::1
Frame 37: 93 bytes on wire (744 bits), 93 bytes captured (744 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 166, Ack: 65669, Len: 17
HyperText Transfer Protocol 2
Stream: GOAWAY, Stream ID: 0, Length 8
Length: 8
Type: GOAWAY (7)
Flags: 0x00
0000 0000 = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Promised-Stream-ID: 0
Error: FLOW_CONTROL_ERROR (3)
38 2.029759 8888 55082 TCP 76 8888 → 55082 [ACK] Seq=65669 Ack=183 Win=407616 Len=0 TSval=1346049058 TSecr=1346049058 ::1 → ::1
Frame 38: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 65669, Ack: 183, Len: 0
39 2.032633 55082 8888 TCP 76 55082 → 8888 [FIN, ACK] Seq=183 Ack=65669 Win=342112 Len=0 TSval=1346049060 TSecr=1346049058 ::1 → ::1
Frame 39: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 183, Ack: 65669, Len: 0
40 2.032679 8888 55082 TCP 76 8888 → 55082 [ACK] Seq=65669 Ack=184 Win=407616 Len=0 TSval=1346049060 TSecr=1346049060 ::1 → ::1
Frame 40: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 65669, Ack: 184, Len: 0
41 2.032740 8888 55082 TCP 76 8888 → 55082 [FIN, ACK] Seq=65669 Ack=184 Win=407616 Len=0 TSval=1346049060 TSecr=1346049060 ::1 → ::1
Frame 41: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 8888, Dst Port: 55082, Seq: 65669, Ack: 184, Len: 0
42 2.032783 55082 8888 TCP 76 55082 → 8888 [ACK] Seq=184 Ack=65670 Win=342112 Len=0 TSval=1346049060 TSecr=1346049060 ::1 → ::1
Frame 42: 76 bytes on wire (608 bits), 76 bytes captured (608 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol, Src Port: 55082, Dst Port: 8888, Seq: 184, Ack: 65670, Len: 0
I'm running some preliminary load testing on a proxy implemented with h2. It appears that there is a substantial performance impact caused by small window updates.
In this test, a client sends small multi-frame requests (HEADERS, DATA=5B) to a server that replies with small multi-frame responses (HEADERS, DATA=15B, HEADERS).
When the application communicates directly (on port 8888), it sends infrequent, large window updates:
:; (config=noproxy ; sudo tshark -i lo -a duration:60 -O http2 -q -V -d 'tcp.port==7777,http2' -d 'tcp.port==8888,http2' 2>/dev/null |tee ${config}.tshark | sed -nEe 's/.* = Window Size Increment: //p' |sort -n |uniq -c )
19 262140
:; cat noproxy.tshark | sed -nEe 's/^.* Type: (\w+).*/\1/p' |sort |uniq -c|sort -rn
741369 HEADERS
494246 DATA
19 WINDOW_UPDATE
With an h2 proxy (on port 7777), we see that we are far too aggressive in sending window updates:
:; (config=proxy ; sudo tshark -i lo -a duration:60 -O http2 -q -V -d 'tcp.port==7777,http2' -d 'tcp.port==8888,http2' 2>/dev/null |tee ${config}.tshark | sed -nEe 's/.* = Window Size Increment: //p' |sort -n |uniq -c )
1349 5
1371 15
:; cat proxy.tshark | sed -nEe 's/^.* Type: (\w+).*/\1/p' |sort |uniq -c|sort -rn
6143 HEADERS
4095 DATA
2046 WINDOW_UPDATE
Other h2 implementations, like netty, support a configurable update ratio so that the receiver only sends window size increments greater than ratio * available window
When all handles related to a stream are dropped, interest has been canceled. If the stream has not yet been closed, this should result in a RST_STREAM
being sent out.
Originally from #75
Relates to #95
For example, the server receives a request, then an invalid sequence of window updates. The stream is then reset. In this case, the server app should never see the request.
It's a bit tricky / subtle. Some docs would be nice.
server::Builder
should support set_max_concurrent_streams(&mut self, u32)
so that a server can constrain the number of streams per client.
Similarly, client::Builder
should have this, though I think a client can only configure the number of concurrent server-initiated streams (i.e. Push Promises). While we're here, client::Builder
should probalby also have a set_push_promise_enabled(&mut self, bool)
I'm able to transmit many small (~15B) payloads, but when using payloads that exceed the max frame size, the sender panics. This can be reproduced when writing a single response of size 16398B.
Finished release [optimized] target(s) in 0.0 secs
starting gRPC server on :8888
1504792758.670191 TRACE tower_h2::client::new_service connecting to V6([::1]:8888)
1504792758.670497 TRACE tower_h2::client::new_service connected to TcpStream { sys: TcpStream { inner: TcpStream { addr: V6([::1]:49567), peer: V6([::1]:8888), fd: 7 } }, selector_id: SelectorId { id: AtomicUsize(1) } }; handshaking
1504792758.670537 DEBUG h2::client binding client connection
1504792758.670582 DEBUG h2::client client connection bound
1504792758.670646 DEBUG h2::codec::framed_write send; frame=Frame::Settings(Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None })
1504792758.670675 TRACE h2::frame::settings encoding SETTINGS; len=0
1504792758.670686 TRACE h2::codec::framed_write encoded settings; rem=9
1504792758.670697 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792758.670703 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792758.670710 TRACE h2::proto::streams::prioritize Prioritize::new; flow=FlowControl { window_size: 65535, available: 65535 }
1504792758.670796 TRACE h2::proto::settings send_pending_ack; pending=None
1504792758.670808 TRACE h2::codec::framed_read poll
1504792758.670852 TRACE h2::codec::framed_read poll; bytes=9B
1504792758.670857 TRACE h2::codec::framed_read decoding frame from 9B
1504792758.670862 TRACE h2::codec::framed_read -> kind=Settings
1504792758.670870 TRACE h2::proto::connection recv SETTINGS; frame=Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None }
1504792758.670876 TRACE h2::proto::settings send_pending_ack; pending=Some(Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None })
1504792758.670882 DEBUG h2::codec::framed_write send; frame=Frame::Settings(Settings { flags: SettingsFlags(1), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None })
1504792758.670892 TRACE h2::frame::settings encoding SETTINGS; len=0
1504792758.670896 TRACE h2::codec::framed_write encoded settings; rem=18
1504792758.670899 TRACE h2::proto::settings ACK sent; applying settings
1504792758.670910 TRACE h2::codec::framed_read poll
1504792758.670914 TRACE h2::codec::framed_read poll; bytes=13B
1504792758.670917 TRACE h2::codec::framed_read decoding frame from 13B
1504792758.670921 TRACE h2::codec::framed_read -> kind=WindowUpdate
1504792758.670926 TRACE h2::proto::connection recv WINDOW_UPDATE; frame=WindowUpdate { stream_id: StreamId(0), size_increment: 983025 }
1504792758.670932 TRACE h2::proto::streams::flow_control inc_window; sz=983025; old=65535; new=1048560
1504792758.670935 TRACE h2::proto::settings send_pending_ack; pending=None
1504792758.670938 TRACE h2::codec::framed_read poll
1504792758.670953 TRACE h2::proto::streams::prioritize try reclaim frame
1504792758.670956 TRACE h2::proto::streams::prioritize poll_complete
1504792758.670961 TRACE h2::proto::streams::prioritize pop_frame
1504792758.670967 TRACE h2::codec::framed_write flush
1504792758.670997 TRACE h2::codec::framed_write flushing buffer
1504792758.671001 TRACE h2::proto::streams::prioritize try reclaim frame
1504792758.671098 TRACE h2::proto::settings send_pending_ack; pending=None
1504792758.671106 TRACE h2::codec::framed_read poll
1504792758.671117 TRACE h2::codec::framed_read poll; bytes=9B
1504792758.671121 TRACE h2::codec::framed_read decoding frame from 9B
1504792758.671125 TRACE h2::codec::framed_read -> kind=Settings
1504792758.671130 TRACE h2::proto::connection recv SETTINGS; frame=Settings { flags: SettingsFlags(1), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None }
1504792758.671137 DEBUG h2::proto::settings received remote settings ack
1504792758.671142 TRACE h2::proto::settings send_pending_ack; pending=None
1504792758.671154 TRACE h2::codec::framed_read poll
1504792758.671165 TRACE h2::proto::streams::prioritize try reclaim frame
1504792758.671170 TRACE h2::proto::streams::prioritize poll_complete
1504792758.671174 TRACE h2::proto::streams::prioritize pop_frame
1504792758.671178 TRACE h2::codec::framed_write flush
1504792758.671182 TRACE h2::codec::framed_write flushing buffer
1504792758.671185 TRACE h2::proto::streams::prioritize try reclaim frame
1504792759.691449 DEBUG h2::codec::framed_write send; frame=Frame::Settings(Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None })
1504792759.691465 TRACE h2::frame::settings encoding SETTINGS; len=0
1504792759.691484 TRACE h2::codec::framed_write encoded settings; rem=9
1504792759.691530 TRACE h2::codec::framed_write flush
1504792759.691549 TRACE h2::codec::framed_write flushing buffer
1504792759.691639 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792759.691647 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792759.691651 TRACE h2::proto::streams::prioritize Prioritize::new; flow=FlowControl { window_size: 65535, available: 65535 }
1504792759.691679 TRACE h2::proto::settings send_pending_ack; pending=None
1504792759.691685 TRACE h2::codec::framed_read poll
1504792759.691709 TRACE h2::codec::framed_read poll; bytes=9B
1504792759.691714 TRACE h2::codec::framed_read decoding frame from 9B
1504792759.691718 TRACE h2::codec::framed_read -> kind=Settings
1504792759.691725 TRACE h2::proto::connection recv SETTINGS; frame=Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None }
1504792759.691739 TRACE h2::proto::settings send_pending_ack; pending=Some(Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None })
1504792759.691746 DEBUG h2::codec::framed_write send; frame=Frame::Settings(Settings { flags: SettingsFlags(1), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None })
1504792759.691755 TRACE h2::frame::settings encoding SETTINGS; len=0
1504792759.691759 TRACE h2::codec::framed_write encoded settings; rem=9
1504792759.691763 TRACE h2::proto::settings ACK sent; applying settings
1504792759.691775 TRACE h2::codec::framed_read poll
1504792759.691780 TRACE h2::codec::framed_read poll; bytes=13B
1504792759.691784 TRACE h2::codec::framed_read decoding frame from 13B
1504792759.691788 TRACE h2::codec::framed_read -> kind=WindowUpdate
1504792759.691801 TRACE h2::proto::connection recv WINDOW_UPDATE; frame=WindowUpdate { stream_id: StreamId(0), size_increment: 983025 }
1504792759.691810 TRACE h2::proto::streams::flow_control inc_window; sz=983025; old=65535; new=1048560
1504792759.691813 TRACE h2::proto::settings send_pending_ack; pending=None
1504792759.691817 TRACE h2::codec::framed_read poll
1504792759.691831 TRACE h2::codec::framed_read poll; bytes=9B
1504792759.691835 TRACE h2::codec::framed_read decoding frame from 9B
1504792759.691837 TRACE h2::codec::framed_read -> kind=Settings
1504792759.691840 TRACE h2::proto::connection recv SETTINGS; frame=Settings { flags: SettingsFlags(1), header_table_size: None, enable_push: None, max_concurrent_streams: None, initial_window_size: None, max_frame_size: None, max_header_list_size: None }
1504792759.691844 DEBUG h2::proto::settings received remote settings ack
1504792759.691847 TRACE h2::proto::settings send_pending_ack; pending=None
1504792759.691850 TRACE h2::codec::framed_read poll
1504792759.691857 TRACE h2::proto::streams::prioritize try reclaim frame
1504792759.691860 TRACE h2::proto::streams::prioritize poll_complete
1504792759.691865 TRACE h2::proto::streams::prioritize pop_frame
1504792759.691868 TRACE h2::codec::framed_write flush
1504792759.691882 TRACE h2::codec::framed_write flushing buffer
1504792759.691885 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.686162 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.686184 TRACE h2::codec::framed_read poll
1504792760.686220 TRACE h2::codec::framed_read poll; bytes=79B
1504792760.686241 TRACE h2::codec::framed_read decoding frame from 79B
1504792760.686249 TRACE h2::codec::framed_read -> kind=Headers
1504792760.686256 TRACE h2::frame::headers loading headers; flags=HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false }
1504792760.686274 TRACE h2::hpack::decoder decode
1504792760.686284 TRACE h2::hpack::decoder Indexed; rem=70
1504792760.686303 TRACE h2::hpack::decoder Indexed; rem=69
1504792760.686309 TRACE h2::hpack::decoder LiteralWithIndexing; rem=68
1504792760.686340 TRACE h2::hpack::decoder LiteralWithIndexing; rem=51
1504792760.686349 TRACE h2::hpack::decoder LiteralWithIndexing; rem=39
1504792760.686369 TRACE h2::hpack::decoder LiteralWithIndexing; rem=26
1504792760.686379 TRACE h2::hpack::decoder LiteralWithIndexing; rem=11
1504792760.686391 TRACE h2::proto::connection recv HEADERS; frame=Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } }
1504792760.686412 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792760.686419 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792760.686429 TRACE h2::proto::streams::recv opening stream; init_window=65535
1504792760.686476 TRACE h2::proto::streams::store Queue::push
1504792760.686483 TRACE h2::proto::streams::store -> first entry
1504792760.686492 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.686497 TRACE h2::codec::framed_read poll
1504792760.686503 TRACE h2::codec::framed_read poll; bytes=17B
1504792760.686508 TRACE h2::codec::framed_read decoding frame from 17B
1504792760.686513 TRACE h2::codec::framed_read -> kind=Data
1504792760.686519 TRACE h2::proto::connection recv DATA; frame=Data { stream_id: StreamId(1), flags: DataFlags { end_stream: true }, pad_len: None }
1504792760.686531 TRACE h2::proto::streams::recv recv_data; size=8; connection=65535; stream=65535
1504792760.686539 TRACE h2::proto::streams::flow_control send_data; sz=8; window=65535; available=65535
1504792760.686554 TRACE h2::proto::streams::flow_control send_data; sz=8; window=65535; available=65535
1504792760.686561 TRACE h2::proto::streams::state recv_close: Open => HalfClosedRemote(AwaitingHeaders)
1504792760.686576 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.686582 TRACE h2::codec::framed_read poll
1504792760.686603 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.686610 TRACE h2::proto::streams::prioritize poll_complete
1504792760.686614 TRACE h2::proto::streams::prioritize pop_frame
1504792760.686620 TRACE h2::codec::framed_write flush
1504792760.686624 TRACE h2::codec::framed_write flushing buffer
1504792760.686629 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.686635 TRACE h2::server received incoming
1504792760.686647 TRACE tower_h2::client::service request: POST https://localhost:7777/strest.Responder/Get
1504792760.686660 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.686667 TRACE h2::codec::framed_read poll
1504792760.686677 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.686683 TRACE h2::proto::streams::prioritize poll_complete
1504792760.686688 TRACE h2::proto::streams::prioritize pop_frame
1504792760.686692 TRACE h2::codec::framed_write flush
1504792760.686699 TRACE h2::codec::framed_write flushing buffer
1504792760.686705 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.686727 TRACE tower_h2::server ~> poll response
1504792760.686736 TRACE tower_h2::client::service GetResponse::poll
1504792760.686741 TRACE tower_h2::client::service send
1504792760.686769 TRACE tower_h2::client::service sending request: Parts { method: POST, uri: https://localhost:7777/strest.Responder/Get, version: HTTP/2.0, headers: {"content-type": "application/grpc", "user-agent": "grpc-go/1.4.0-dev", "te": "trailers"} } empty=false
1504792760.686799 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792760.686806 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1504792760.686825 TRACE h2::proto::streams::send send_headers; frame=Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } }; init_window=65535
1504792760.686838 TRACE h2::proto::streams::store Queue::push
1504792760.686843 TRACE h2::proto::streams::store -> first entry
1504792760.686851 TRACE tower_h2::client::service ~> recv
1504792760.686860 TRACE tower_h2::client::service response not ready
1504792760.686869 TRACE tower_h2::client::service sender: poll
1504792760.686876 TRACE tower_h2::recv_body poll_data
1504792760.686885 TRACE tower_h2::recv_body poll_data
1504792760.686890 TRACE tower_h2::client::service sender: poll_trailers
1504792760.686896 TRACE tower_h2::recv_body poll_trailers
1504792760.686904 TRACE tower_h2::client::service poll: send_data eos
1504792760.686913 TRACE h2::proto::streams::prioritize send_data; sz=8; buffered=8; requested=0
1504792760.686923 TRACE h2::proto::streams::prioritize try_assign_capacity; requested=8; additional=8; window=65535; conn=1048560
1504792760.686929 TRACE h2::proto::streams::prioritize try_assign_capacity; available=8; requested=8; buffered=8; has_unavailable=true
1504792760.686936 TRACE h2::proto::streams::store Queue::push
1504792760.686941 TRACE h2::proto::streams::store -> already queued
1504792760.686962 TRACE h2::proto::streams::state send_close: Open => HalfClosedLocal(AwaitingHeaders)
1504792760.686977 TRACE h2::proto::streams::prioritize send_data (2); available=8; buffered=8
1504792760.686985 TRACE h2::proto::streams::store Queue::push
1504792760.687002 TRACE h2::proto::streams::store -> already queued
1504792760.687025 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.687033 TRACE h2::codec::framed_read poll
1504792760.687044 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.687050 TRACE h2::proto::streams::prioritize poll_complete
1504792760.687055 TRACE h2::proto::streams::prioritize pop_frame
1504792760.687060 TRACE h2::proto::streams::prioritize pop_frame; stream=StreamId(1)
1504792760.687068 TRACE h2::proto::streams::prioritize pop_frame; frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } })
1504792760.687078 TRACE h2::proto::streams::store Queue::push
1504792760.687082 TRACE h2::proto::streams::store -> first entry
1504792760.687087 TRACE h2::proto::streams::prioritize writing frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } })
1504792760.687096 DEBUG h2::codec::framed_write send; frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } })
1504792760.687134 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.687140 TRACE h2::proto::streams::prioritize pop_frame
1504792760.687144 TRACE h2::proto::streams::prioritize pop_frame; stream=StreamId(1)
1504792760.687150 TRACE h2::proto::streams::prioritize --> data frame; stream=StreamId(1); sz=8; eos=true; window=8; available=8; requested=8
1504792760.687155 TRACE h2::proto::streams::prioritize -- updating stream flow --
1504792760.687160 TRACE h2::proto::streams::flow_control send_data; sz=8; window=65535; available=8
1504792760.687165 TRACE h2::proto::streams::prioritize -- updating connection flow --
1504792760.687169 TRACE h2::proto::streams::flow_control send_data; sz=8; window=1048560; available=1048560
1504792760.687174 TRACE h2::proto::streams::prioritize pop_frame; frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags { end_stream: true }, pad_len: None })
1504792760.687182 TRACE h2::proto::streams::prioritize writing frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags { end_stream: true }, pad_len: None })
1504792760.687191 DEBUG h2::codec::framed_write send; frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags { end_stream: true }, pad_len: None })
1504792760.687211 TRACE h2::proto::streams::store Queue::push
1504792760.687223 TRACE h2::proto::streams::store -> first entry
1504792760.687230 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.687236 TRACE h2::proto::streams::prioritize -> reclaimed; frame=Data { stream_id: StreamId(1), flags: DataFlags { end_stream: true }, pad_len: None }; sz=0
1504792760.687250 TRACE h2::proto::streams::prioritize pop_frame
1504792760.687280 TRACE h2::codec::framed_write flush
1504792760.687330 TRACE h2::codec::framed_write flushing buffer
1504792760.687339 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.687356 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.687364 TRACE h2::codec::framed_read poll
1504792760.687381 DEBUG h2::codec::framed_write send; frame=Frame::WindowUpdate(WindowUpdate { stream_id: StreamId(0), size_increment: 8 })
1504792760.687395 TRACE h2::frame::window_update encoding WINDOW_UPDATE; id=StreamId(0)
1504792760.687404 TRACE h2::codec::framed_write encoded window_update; rem=13
1504792760.687423 TRACE h2::proto::streams::flow_control inc_window; sz=8; old=65527; new=65535
1504792760.687432 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.687477 TRACE h2::proto::streams::prioritize poll_complete
1504792760.687494 TRACE h2::proto::streams::prioritize pop_frame
1504792760.687503 TRACE h2::codec::framed_write flush
1504792760.687549 TRACE h2::codec::framed_write flushing buffer
1504792760.687558 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.688528 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.688548 TRACE h2::codec::framed_read poll
1504792760.688592 TRACE h2::codec::framed_read poll; bytes=23B
1504792760.688603 TRACE h2::codec::framed_read decoding frame from 23B
1504792760.688612 TRACE h2::codec::framed_read -> kind=Headers
1504792760.688621 TRACE h2::frame::headers loading headers; flags=HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false }
1504792760.688635 TRACE h2::hpack::decoder decode
1504792760.688642 TRACE h2::hpack::decoder Indexed; rem=14
1504792760.688653 TRACE h2::hpack::decoder LiteralWithIndexing; rem=13
1504792760.688674 TRACE h2::proto::connection recv HEADERS; frame=Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } }
1504792760.688701 TRACE h2::proto::streams::recv opening stream; init_window=65535
1504792760.688719 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.688728 TRACE h2::codec::framed_read poll
1504792760.688816 TRACE h2::codec::framed_read poll; bytes=16393B
1504792760.688826 TRACE h2::codec::framed_read decoding frame from 16393B
1504792760.688831 TRACE h2::codec::framed_read -> kind=Data
1504792760.688837 TRACE h2::proto::connection recv DATA; frame=Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None }
1504792760.688845 TRACE h2::proto::streams::recv recv_data; size=16384; connection=65535; stream=65535
1504792760.688850 TRACE h2::proto::streams::flow_control send_data; sz=16384; window=65535; available=65535
1504792760.688855 TRACE h2::proto::streams::flow_control send_data; sz=16384; window=65535; available=65535
1504792760.688861 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.688875 TRACE h2::codec::framed_read poll
1504792760.688881 TRACE h2::codec::framed_read poll; bytes=26B
1504792760.688886 TRACE h2::codec::framed_read decoding frame from 26B
1504792760.688891 TRACE h2::codec::framed_read -> kind=Data
1504792760.688898 TRACE h2::proto::connection recv DATA; frame=Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None }
1504792760.688907 TRACE h2::proto::streams::recv recv_data; size=17; connection=49151; stream=49151
1504792760.688913 TRACE h2::proto::streams::flow_control send_data; sz=17; window=49151; available=49151
1504792760.688919 TRACE h2::proto::streams::flow_control send_data; sz=17; window=49151; available=49151
1504792760.688926 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.688932 TRACE h2::codec::framed_read poll
1504792760.688938 TRACE h2::codec::framed_read poll; bytes=33B
1504792760.688959 TRACE h2::codec::framed_read decoding frame from 33B
1504792760.688973 TRACE h2::codec::framed_read -> kind=Headers
1504792760.688980 TRACE h2::frame::headers loading headers; flags=HeadersFlag { end_stream: true, end_headers: true, padded: false, priority: false }
1504792760.689000 TRACE h2::hpack::decoder decode
1504792760.689006 TRACE h2::hpack::decoder LiteralWithIndexing; rem=24
1504792760.689030 TRACE h2::hpack::decoder LiteralWithIndexing; rem=12
1504792760.689039 TRACE h2::proto::connection recv HEADERS; frame=Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: true, end_headers: true, padded: false, priority: false } }
1504792760.689048 TRACE h2::proto::streams::state recv_close: HalfClosedLocal => Closed
1504792760.689055 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.689060 TRACE h2::codec::framed_read poll
1504792760.689076 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.689084 TRACE h2::proto::streams::prioritize poll_complete
1504792760.689088 TRACE h2::proto::streams::prioritize pop_frame
1504792760.689093 TRACE h2::codec::framed_write flush
1504792760.689101 TRACE h2::codec::framed_write flushing buffer
1504792760.689108 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.689120 TRACE tower_h2::server ~> poll response
1504792760.689126 TRACE tower_h2::client::service GetResponse::poll
1504792760.689131 TRACE tower_h2::client::service ~> recv
1504792760.689137 TRACE tower_h2::client::service ~> response: Parts { status: 200, version: HTTP/1.1, headers: {"content-type": "application/grpc"} }
1504792760.689155 TRACE tower_h2::server ~> response: Parts { status: 200, version: HTTP/1.1, headers: {"content-type": "application/grpc"} }
1504792760.689169 TRACE h2::proto::streams::send send_headers; frame=Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } }; init_window=65535
1504792760.689178 TRACE h2::proto::streams::store Queue::push
1504792760.689184 TRACE h2::proto::streams::store -> first entry
1504792760.689207 TRACE tower_h2::server sending response body
1504792760.689231 TRACE tower_h2::recv_body poll_data
1504792760.689238 TRACE tower_h2::recv_body poll_send
1504792760.689243 TRACE tower_h2::client::service sender: poll
1504792760.689249 TRACE tower_h2::server poll: send_data
1504792760.689255 TRACE h2::proto::streams::prioritize send_data; sz=16384; buffered=16384; requested=0
1504792760.689260 TRACE h2::proto::streams::prioritize try_assign_capacity; requested=16384; additional=16384; window=65535; conn=1048560
1504792760.689266 TRACE h2::proto::streams::prioritize try_assign_capacity; available=16384; requested=16384; buffered=16384; has_unavailable=true
1504792760.689271 TRACE h2::proto::streams::store Queue::push
1504792760.689276 TRACE h2::proto::streams::store -> already queued
1504792760.689280 TRACE h2::proto::streams::prioritize send_data (2); available=16384; buffered=16384
1504792760.689296 TRACE h2::proto::streams::store Queue::push
1504792760.689303 TRACE h2::proto::streams::store -> already queued
1504792760.689309 TRACE tower_h2::recv_body poll_data
1504792760.689314 TRACE tower_h2::server poll: send_data
1504792760.689319 TRACE h2::proto::streams::prioritize send_data; sz=17; buffered=16401; requested=16384
1504792760.689324 TRACE h2::proto::streams::prioritize try_assign_capacity; requested=16401; additional=17; window=65535; conn=1032176
1504792760.689329 TRACE h2::proto::streams::prioritize try_assign_capacity; available=16401; requested=16401; buffered=16401; has_unavailable=true
1504792760.689334 TRACE h2::proto::streams::store Queue::push
1504792760.689338 TRACE h2::proto::streams::store -> already queued
1504792760.689343 TRACE h2::proto::streams::prioritize send_data (2); available=16401; buffered=16401
1504792760.689348 TRACE h2::proto::streams::store Queue::push
1504792760.689352 TRACE h2::proto::streams::store -> already queued
1504792760.689356 TRACE tower_h2::recv_body poll_data
1504792760.689362 TRACE tower_h2::server poll: poll_trailers
1504792760.689368 TRACE tower_h2::recv_body poll_trailers
1504792760.689376 TRACE tower_h2::server poll: send_trailers
1504792760.689381 TRACE h2::proto::streams::state send_close: HalfClosedRemote => Closed
1504792760.689386 TRACE h2::proto::streams::send send_trailers -- queuing; frame=Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: true, end_headers: true, padded: false, priority: false } }
1504792760.689397 TRACE h2::proto::streams::store Queue::push
1504792760.689402 TRACE h2::proto::streams::store -> already queued
1504792760.689407 TRACE tower_h2::recv_body poll_close
1504792760.689430 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.689436 TRACE h2::codec::framed_read poll
1504792760.689449 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.689454 TRACE h2::proto::streams::prioritize poll_complete
1504792760.689458 TRACE h2::proto::streams::prioritize pop_frame
1504792760.689463 TRACE h2::proto::streams::prioritize pop_frame; stream=StreamId(1)
1504792760.689469 TRACE h2::proto::streams::prioritize pop_frame; frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } })
1504792760.689476 TRACE h2::proto::streams::store Queue::push
1504792760.689480 TRACE h2::proto::streams::store -> first entry
1504792760.689494 TRACE h2::proto::streams::prioritize writing frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } })
1504792760.689502 DEBUG h2::codec::framed_write send; frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: false, end_headers: true, padded: false, priority: false } })
1504792760.689516 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.689522 TRACE h2::proto::streams::prioritize pop_frame
1504792760.689527 TRACE h2::proto::streams::prioritize pop_frame; stream=StreamId(1)
1504792760.689532 TRACE h2::proto::streams::prioritize --> data frame; stream=StreamId(1); sz=16384; eos=false; window=16401; available=16401; requested=16401
1504792760.689540 TRACE h2::proto::streams::prioritize -- updating stream flow --
1504792760.689560 TRACE h2::proto::streams::flow_control send_data; sz=16384; window=65535; available=16401
1504792760.689586 TRACE h2::proto::streams::prioritize -- updating connection flow --
1504792760.689591 TRACE h2::proto::streams::flow_control send_data; sz=16384; window=1048560; available=1048543
1504792760.689604 TRACE h2::proto::streams::prioritize pop_frame; frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None })
1504792760.689613 TRACE h2::proto::streams::store Queue::push
1504792760.689620 TRACE h2::proto::streams::store -> first entry
1504792760.689624 TRACE h2::proto::streams::prioritize writing frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None })
1504792760.689632 DEBUG h2::codec::framed_write send; frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None })
1504792760.689648 TRACE h2::codec::framed_write flush
1504792760.689815 TRACE h2::proto::streams::store Queue::push
1504792760.689836 TRACE h2::proto::streams::store -> first entry
1504792760.689846 TRACE h2::codec::framed_write flushing buffer
1504792760.689854 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.689862 TRACE h2::proto::streams::prioritize -> reclaimed; frame=Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None }; sz=0
1504792760.689895 TRACE h2::proto::streams::prioritize pop_frame
1504792760.689905 TRACE h2::proto::streams::prioritize pop_frame; stream=StreamId(1)
1504792760.689914 TRACE h2::proto::streams::prioritize --> data frame; stream=StreamId(1); sz=17; eos=false; window=17; available=17; requested=16401
1504792760.689928 TRACE h2::proto::streams::prioritize -- updating stream flow --
1504792760.689934 TRACE h2::proto::streams::flow_control send_data; sz=17; window=49151; available=17
1504792760.689941 TRACE h2::proto::streams::prioritize -- updating connection flow --
1504792760.689947 TRACE h2::proto::streams::flow_control send_data; sz=17; window=1032176; available=1032176
1504792760.689956 TRACE h2::proto::streams::prioritize pop_frame; frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None })
1504792760.689965 TRACE h2::proto::streams::store Queue::push
1504792760.689971 TRACE h2::proto::streams::store -> first entry
1504792760.689978 TRACE h2::proto::streams::prioritize writing frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None })
1504792760.689986 DEBUG h2::codec::framed_write send; frame=Frame::Data(Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None })
1504792760.689995 TRACE h2::proto::streams::store Queue::push
1504792760.690000 TRACE h2::proto::streams::store -> already queued
1504792760.690007 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.690015 TRACE h2::proto::streams::prioritize -> reclaimed; frame=Data { stream_id: StreamId(1), flags: DataFlags, pad_len: None }; sz=0
1504792760.690023 TRACE h2::proto::streams::prioritize pop_frame
1504792760.690030 TRACE h2::proto::streams::prioritize pop_frame; stream=StreamId(1)
1504792760.690038 TRACE h2::proto::streams::prioritize pop_frame; frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: true, end_headers: true, padded: false, priority: false } })
1504792760.690050 TRACE h2::proto::streams::prioritize writing frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: true, end_headers: true, padded: false, priority: false } })
1504792760.690061 DEBUG h2::codec::framed_write send; frame=Frame::Headers(Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: true, end_headers: true, padded: false, priority: false } })
1504792760.690078 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.690091 TRACE h2::proto::streams::prioritize pop_frame
1504792760.690098 TRACE h2::codec::framed_write flush
1504792760.690127 TRACE h2::codec::framed_write flushing buffer
1504792760.690137 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.690158 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.690168 TRACE h2::codec::framed_read poll
1504792760.690180 DEBUG h2::codec::framed_write send; frame=Frame::WindowUpdate(WindowUpdate { stream_id: StreamId(0), size_increment: 16401 })
1504792760.690191 TRACE h2::frame::window_update encoding WINDOW_UPDATE; id=StreamId(0)
1504792760.690207 TRACE h2::codec::framed_write encoded window_update; rem=13
1504792760.690216 TRACE h2::proto::streams::flow_control inc_window; sz=16401; old=49134; new=65535
1504792760.690226 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.690233 TRACE h2::proto::streams::prioritize poll_complete
1504792760.690240 TRACE h2::proto::streams::prioritize pop_frame
1504792760.690247 TRACE h2::codec::framed_write flush
1504792760.690273 TRACE h2::codec::framed_write flushing buffer
1504792760.690282 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.690301 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.690311 TRACE h2::codec::framed_read poll
1504792760.690339 TRACE h2::codec::framed_read poll; bytes=13B
1504792760.690348 TRACE h2::codec::framed_read decoding frame from 13B
1504792760.690355 TRACE h2::codec::framed_read -> kind=WindowUpdate
1504792760.690363 TRACE h2::proto::connection recv WINDOW_UPDATE; frame=WindowUpdate { stream_id: StreamId(1), size_increment: 16384 }
1504792760.690380 TRACE h2::proto::streams::prioritize recv_stream_window_update; stream=StreamId(1); state=State { inner: Closed(None) }; inc=16384; flow=FlowControl { window_size: 49134, available: 0 }
1504792760.690398 TRACE h2::proto::streams::flow_control inc_window; sz=16384; old=49134; new=65518
1504792760.690407 TRACE h2::proto::streams::prioritize try_assign_capacity; requested=16401; additional=16401; window=65518; conn=1032159
1504792760.690417 TRACE h2::proto::streams::prioritize try_assign_capacity; available=16401; requested=16401; buffered=16401; has_unavailable=true
1504792760.690431 TRACE h2::proto::streams::store Queue::push
1504792760.690437 TRACE h2::proto::streams::store -> first entry
1504792760.690444 TRACE h2::proto::settings send_pending_ack; pending=None
1504792760.690452 TRACE h2::codec::framed_read poll
1504792760.690468 TRACE h2::proto::streams::prioritize try reclaim frame
1504792760.690476 TRACE h2::proto::streams::prioritize poll_complete
1504792760.690491 TRACE h2::proto::streams::prioritize pop_frame
1504792760.690496 TRACE h2::proto::streams::prioritize pop_frame; stream=StreamId(1)
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:335:20
2017-09-07T13:59:29Z 16.0KB 1/0 10s L: 0 [4947 4947 ] 4944 J: 0 0
stack backtrace:
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
1: std::panicking::default_hook::{{closure{
"good": 1,
"bad": 0,
"bytes": 16383,
"latency": {
"50": 4947,
"95": 4947,
"99": 4947,
"999": 4947
},
"jitter": {
"50": 0,
"95": 0,
"99": 0,
"999": 0
}
}
}}
2: std::panicking::default_hook
3: std::panicking::rust_panic_with_hook
4: std::panicking::begin_panic
5: std::panicking::begin_panic_fmt
6: rust_begin_unwind
7: core::panicking::panic_fmt
8: core::panicking::panic
9: >::pop_frame
10: >::poll
11: as futures::stream::Stream>::poll
12: as futures::future::Future>::poll
13: as futures::future::Future>::poll
14: as futures::future::Future>::poll
15: ::poll
16: ::poll
17: tokio_core::reactor::Core::poll
18: buoxy::main
19: __rust_maybe_catch_panic
20: std::rt::lang_start
./strest.sh: line 92: 89711 Terminated: 15 ./strest-grpc-server -address :8888
The server
equivalent already has this function.
Originally from #75
I noticed the following:
The first use of a new stream identifier implicitly closes all streams in the "idle" state that might have been initiated by that peer with a lower-valued stream identifier. For example, if a client sends a HEADERS frame on stream 7 without ever sending a frame on stream 5, then stream 5 transitions to the "closed" state when the first frame for stream 7 is sent or received.
We need to be careful that we don't initialize streams in the idle state such that they may be moved into reserved/open out of order. We should probably only ever initialize streams directly to a Reserved or Open (or HalfOpenRemote) state.
This test panics. This may be resolved by #79
#[test]
fn something_funky() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let h2 = Client::handshake(io).unwrap()
.and_then(|mut h2| {
let request = Request::builder()
.method(Method::POST)
.uri("https://http2.akamai.com/")
.body(()).unwrap();
let mut stream = h2.request(request, false).unwrap();
stream.reserve_capacity(11);
h2.drive(util::wait_for_capacity(stream, 11))
})
.and_then(|(h2, mut stream)| {
assert_eq!(stream.capacity(), 11);
stream.send_data("hello world".into(), true).unwrap();
h2.drive(GetResponse { stream: Some(stream) })
})
.and_then(|(h2, (response, _))| {
assert_eq!(response.status(), StatusCode::NO_CONTENT);
// Wait for the connection to close
h2.unwrap()
})
;
let mut settings = frame::Settings::default();
settings.set_initial_window_size(0);
let srv = srv.assert_client_handshake_with_settings(settings).unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("POST", "https://http2.akamai.com/")
)
.idle_ms(100)
.send_frame(
frames::window_update(0, 11))
.send_frame(
frames::window_update(1, 11))
.recv_frame(
frames::data(1, "hello world").eos())
.send_frame(
frames::headers(1)
.response(204)
.eos()
)
/*
.recv_frame(frames::data(1, "hello").eos())
.send_frame(frames::window_update(1, 5))
*/
.map(drop)
;
let _ = h2.join(srv)
.wait().unwrap();
}
When a HEADERS
frame is received that is malformed, the stream is reset. However, any continuation frame that is received after that must still be have HPACK decoding performed in order to maintain the hpack state.
All public API types should be audited to see if they need a Drop
impl to maintain correct state.
Current known issues:
When the value of :authority
is invalid, we get the following panic:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidUriBytes(InvalidUri(InvalidUriChar))', src/libcore/result.rs:906:4
9: core::result::unwrap_failed
10: <h2::server::Peer as h2::proto::peer::Peer>::convert_poll_message
11: <h2::proto::connection::Connection<T, P, B>>::poll2
12: <h2::server::Server<T, B> as futures::stream::Stream>::poll
This should probably be a User error and not a panic.
I started the code making everything public. This was just to get things to compile and because I wasn't sure what the public API would be yet.
We probably should fix this before going too much further as it might require some refactoring.
Roughly, the visibility should be:
Public
- client mod
- server mod
- error mod
Private
- hpack mod
- frame mod
- proto mod
Wrote a naive client like:
let done = h2.new_service()
.map_err(|e| e.into())
.and_then(move |h2| {
reactor.spawn(h2.clone().map_err(|e| println!("error: {:#?}", e)));
let futs = (0..500).map(move |n| {
let pfx = format!("{}", n);
h2.call(mkreq()).and_then(move |rsp| handle(pfx, rsp))
});
future::join_all(futs)
})
.map(|_| println!("done"))
.map_err(|e| println!("error: {:?}", e));
This causes the server to fail with
2.033] [INVALID; error=Protocol error] recv HEADERS frame <length=4, flags=0x05, stream_id=585>
; END_STREAM | END_HEADERS
(padlen=0)
; Open new stream
[id=1] [ 2.033] send GOAWAY frame <length=56, flags=0x00, stream_id=0>
(last_stream_id=199, error_code=PROTOCOL_ERROR(0x01), opaque_data(48)=[request HEADERS: max concurrent streams exceeded])
Additionally, go aways do not appear to trigger errors for pending requests.
Both the server and the client need a way to initiate a clean shutdown. The process should be:
- Stop accepting new streams
- Wait until all existing streams are complete
- Send a GO_AWAY frame
- Close the connection.
Client::request
should be renamed Client::send_request
send_trailers
and send_reset
should take self
Client
is missing send_reset
- Dropping the read body should reset the stream.
RFC7540§5.1.1 Stream Identifiers says:
Stream identifiers cannot be reused. Long-lived connections can result in an endpoint exhausting the available range of stream identifiers. A client that is unable to establish a new stream identifier can establish a new connection for new streams. A server that is unable to establish a new stream identifier can send a GOAWAY frame so that the client is forced to open a new connection for new streams.
On the client side, we should return some sort of refusal (either through a poll_ready()
with a dedicated failure type or with an error type that returns the Request<T>
to the application).
On the server side, we'll need to send a GOAWAY with a last stream id of the maximum stream id.
I'm running tests with varying stream concurrency and response payload size (to exercise flow control, etc).
With a stream concurrency of 32 (on one connection) and a response payload size of 16300 B, i'm able to process an arbitrary number of requests.
Changing the response payload to 16400 B, the test hangs after only a few hundred requests and no further work is done (and the CPU is totally idle). This behavior can reproduced with stream concurrencies as low as 4.
I have a guess that this has to do with send-side stream notification on connection-level window updates. I need more evidence to support that theory, though.
Currently, it just buffers all continuation frames. It would be nice if it did something different!
There is a lot logged, and while it's useful, it can make it hard to see slightly more important information in the logs. Perhaps we can promote many of the logs to debug level, especially those with useful arguments included. Some of the less important logs could stay at trace (things like poll
), or possibly be removed entirely.
I suspect @olix0r has noticed the noise :)
Right now, GOAWAY
is interpreted as a signal to reset all streams. This, however, is not correct behavior.
There are many potential handles to a single h2::Stream
and the value should not be freed until all the references are released.
It's possible to panic the stream-level flow controller:
thread 'main' panicked at 'assertion failed: sz <= (self.window_size as WindowSize)', /home/ver/.cargo/git/checkouts/h2-5627e19d2c95dbee/d56531f/src/proto/streams/flow_control.rs:136:8
stack backtrace:
This is because FlowController::available()
is used to determine stream capacity, but FlowController::send_data
uses FlowController::window_size()
as an upper bound. Because available is intended to be greater than window_size at times, this leads to a panic.
Changing prioritize to check window_size()
appears to resolve this issue:
--- a/src/proto/streams/prioritize.rs
+++ b/src/proto/streams/prioritize.rs
@@ -450,7 +450,7 @@ impl<B, P> Prioritize<B, P>
// window.
//
- // TODO: Is this the right thing to check?
- let stream_capacity = stream.send_flow.available();
+ let stream_capacity = stream.send_flow.window_size();
let sz = frame.payload().remaining();
It's unclear to me how available is to be used on the send-side, if at all.
Currently, if a RST_STREAM
is sent, the stream state transitions to closed and the state is dropped (once ref counts & queue counts go to zero).
However, the remote may not have acknowledged the RST_STREAM
and may have frames already sent on the wire. Those frames must be silently dropped for "some time" allowing the remote to ack the reset.
In the HPACK decoder peek_u8
panics when out of bound. Ensure ranges are checked before calling it.
It looks like this will cause the original HEADERS frame to be dropped and a RST_STREAM frame sent for that ID, which would be a connection level error.
Since stream IDs aren't exposed to the caller, there should be a way to signal that the stream ID would overflow if a new request is issued.
The easiest way to do this would be to have poll_ready
return an error in that case.
Relates to #68
The Reason
is exposed as the public API, so we don't want a show it as an enum
that may need to be extended later. (Bonus, this will also remove a byte for needing the discriminant.)
It should be like:
pub struct Reason(u32);
impl Reason {
pub const NO_ERROR: Reason = Reason(0);
// ...
}
There is a question of whether some of the reasons should be pub(crate)
, since users shouldn't need to signal some errors (they happen at the protocol layer), but if we keep the public From<u32> for Reason
, people can create any reason they want, so it may be moot.
Conflating the handle the advances the connection state with the handle that is used to start requests is limiting. These handles should be split into two types.
Open questions
- What should the two types be named
- Should the
start_request
handle impl Clone
.
Specifically, if StartRequest
(naming tbd) impls Clone
this would allow streams to be initialized from multiple tasks/threads, which seems like a good feature. The difficulty lies in coordinating access to a single resource across tasks. When the concurrent stream limit is reached, StartRequest::poll_ready
calls would race to get access to stream IDs. The solution to this could be to model how mpsc::Sender
behaves when cloned.
The encoder does some interesting stuff w/ splitting up encoding across continuation frames. This may be able to be simplified.
There is no API to change the target window size to be different from SETTINGS_INITIAL_WINDOW_SIZE. An endpoint may wish to this on either a connection or individual stream.
Clients, servers, and their streams should probably have window_target(&self) -> u32
and set_window_target(&mut self, sz: u32)
functions.
When a RST_STREAM
is received, the send half of a stream should be notified somehow in order to allow any data producers to cancel work before trying to write.
A receiver that receives a flow-controlled frame MUST always account for its contribution against the connection flow-control window, unless the receiver treats this as a connection error (Section 5.4.1). This is necessary even if the frame is in error. The sender counts the frame toward the flow-control window, but if the receiver does not, the flow-control window at the sender and receiver can become different.
If a RST_STREAM frame is sent, any data frames received on this stream must be accounted for against the connection level flow control window.
The crate needs docs so that people can figure out how to use it! This issue will be used to track specific points that should be documented.
It is currently updated in recv_headers
, which could result in receiving a HEADERS
frame that gets rejected (due to being malformed), then the same stream ID is received with a valid HEADERS
frame which gets accepted.
After a request, you get back a Response<Body<B>>
, where B
is the kind of Buf
a user can be sending for the request bodies. It is useless on the Body
, as the user will only get back Bytes
.
Technically it's currently there because the Body
owns a StreamRef
, which contains this generic.
Had h2spec been run, we would have avoided merging some bugs.
Projects that depend on h2 can no longer compile due to accessing an unstable feature from the stable API:
error[E0599]: no method named `set_max_frame_size` found for type `codec::framed_read::FramedRead<codec::framed_write::FramedWrite<T, _>>` in the current scope
--> /Users/ver/b/rs/h2/src/codec/mod.rs:53:15
|
53 | inner.set_max_frame_size(max_frame_size);
| ^^^^^^^^^^^^^^^^^^
Compiling ring v0.11.0
error[E0599]: no method named `set_max_recv_frame_size` found for type `codec::Codec<T, _>` in the current scope
--> /Users/ver/b/rs/h2/src/client.rs:213:19
|
213 | codec.set_max_recv_frame_size(max as usize);
| ^^^^^^^^^^^^^^^^^^^^^^^
error[E0599]: no method named `set_max_recv_frame_size` found for type `codec::Codec<T, _>` in the current scope
--> /Users/ver/b/rs/h2/src/server.rs:97:19
|
97 | codec.set_max_recv_frame_size(max as usize);
| ^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
error: Could not compile `h2`.
warning: build failed, waiting for other jobs to finish...
error: build failed
The public Client and Server interfaces attempt to call codec.set_max_recv_frame_size
:
impl<T, B: IntoBuf> Future for Handshake<T, B>
where
T: AsyncRead + AsyncWrite,
{
type Item = Client<T, B>;
type Error = ::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let (io, _) = try_ready!(self.inner.poll());
debug!("client connection bound");
// Create the codec
let mut codec = Codec::new(io);
if let Some(max) = self.settings.max_frame_size() {
codec.set_max_recv_frame_size(max as usize);
}
impl<T, B> Server<T, B>
where
T: AsyncRead + AsyncWrite + 'static,
B: IntoBuf + 'static,
{
fn handshake2(io: T, settings: Settings) -> Handshake<T, B> {
// Create the codec
let mut codec = Codec::new(io);
if let Some(max) = settings.max_frame_size() {
codec.set_max_recv_frame_size(max as usize);
}
The Codec::new
(which must be stable, since it's called from client and server stable APIs above) calls the unstable FramedRead::set_max_frame_size
.
#[derive(Debug)]
pub struct Codec<T, B> {
inner: FramedRead<FramedWrite<T, B>>,
}
impl<T, B> Codec<T, B>
where
T: AsyncRead + AsyncWrite,
B: Buf,
{
/// Returns a new `Codec` with the default max frame size
#[inline]
pub fn new(io: T) -> Self {
Self::with_max_recv_frame_size(io, frame::DEFAULT_MAX_FRAME_SIZE as usize)
}
/// Returns a new `Codec` with the given maximum frame size
pub fn with_max_recv_frame_size(io: T, max_frame_size: usize) -> Self {
...
let mut inner = FramedRead::new(delimited);
// Use FramedRead's method since it checks the value is within range.
inner.set_max_frame_size(max_frame_size);
Codec {
inner,
}
}
}
impl<T, B> Codec<T, B> {
/// Updates the max received frame size.
///
/// The change takes effect the next time a frame is decoded. In other
/// words, if a frame is currently in process of being decoded with a frame
/// size greater than `val` but less than the max frame size in effect
/// before calling this function, then the frame will be allowed.
#[cfg(feature = "unstable")]
#[inline]
pub fn set_max_recv_frame_size(&mut self, val: usize) {
self.inner.set_max_frame_size(val)
}
impl<T> FramedRead<T> {
...
/// Updates the max frame size setting.
///
/// Must be within 16,384 and 16,777,215.
#[cfg(feature = "unstable")]
#[inline]
pub fn set_max_frame_size(&mut self, val: usize) {
assert!(
frame::DEFAULT_MAX_FRAME_SIZE as usize <= val &&
val <= frame::MAX_MAX_FRAME_SIZE as usize
);
self.inner.set_max_frame_length(val)
}
}
HTTP/2 flow control applies on individual streams as well as on connections. Flow control
applies bidirectionally: receiving from the remote to the local peer, and sending from
local to remote peer.
Receiver Flow Control
- Track the local window size.
- Configured with local
SETTINGS_INITIAL_WINDOW_SIZE
(including updates)
- Count received DATA frames against flow control.
- Reset stream / fail connection on violation.
- Send
WINDOW_UPDATE
frames to the remote.
- When received DATA is dropped to allow effective memory constraints.
- With buffering (for small frames) -- nagle's algorithm?
Sender Flow Control
- Track the remote flow control window.
- Configured with local
SETTINGS_INITIAL_WINDOW_SIZE
(including updates)
- Count sent DATA frames against flow control.
- Ensure the outbound window is not violated.
- Apply backpressure when writing would violate window.
- Allow reset/data eos/trailers to be transmitted even when window is closed.
- Receive
WINDOW_UPDATE
frames from the remote.
Example: assymetric windows
Intermediary proxies may forward between endpoints with assymetric window sizes and must
buffer.
- A proxy is forwarding a stream from the src remote to the dst remote.
- The src local window size is 1000B. The dst remote window size is 100B.
- A 300B frame is received from sbrc and 100B is sent to dst.
- The src local window size is now 700B. The dst remote window is empty. And 200B are
buffered in memory.
- A 700B frame is received from src and buffered since the dst window is empty.
- The src window is now empty. 900B are buffered. The src receiver MAY send a 100B
window update.
- The dst sends a window update of 100B.
- 100B are sent to dst and now 800B are buffered.
- ...
Challenges
- Sender flow control should cause backpressure. A sender must not be able to violate
the flow control window. However, it's inappropriate to apply this backpressure on a
connection's frames, since resets/trailers/etc must proceed even when windows are empty.
- Receivers must not send window updates until the memory is released. This should
allow for the partial contents of a data frame to be released, and should not
necessarily cause an immediate window update (esp in the case of small frames).
- Flow controllers must receive settings updates.
Thoughts
This seems to suggest that, at the highest level, we want to model a stream's data as
something like AsyncRead
and AsyncWrite
. Specifically, we want to allow for partial
writes on a sender -- trying to send 1000B on a stream with a 300B window should write
300B and return the remaining 700B to be buffered.
Flow control cannot enforce backpressure on streams of frames, only on bytes themselves.
It seems natural, then, for flow control to be integrated directly with buffering so that
creation of a buffer can deduct from a local window and dropping part of a buffer can
deposit into a remote window.
Something like:
trait ReceiverStream {
fn read(&mut self) -> Poll<Data, StreamError>;
}
trait SenderStream {
/// Writes to the local side of a stream, to be read by the Remote.
fn write<D: Buf>(&mut self, data: D) -> LocalWrite<D>;
}
type LocalWrite<D> = Result<AsyncWrite<D>, StreamError>;
enum AsyncWrite<D> {
Complete,
Incomplete(D),
}
struct Data {
flow_controller: FlowControllerReleaser,
..
}
impl Buf for Data {
fn advance(&mut self, sz: usize) {
self.flow_controller.release(sz);
..
}
}
It seems like this API would have to sit on top of a muxer/demuxer -- if we're enforcing buffering/backpressure, we'd want that buffer to be associated with an Http message (and above the Frame based API).
Triggered by running the following against an h2 server:
/usr/local/opt/curl/bin/curl --http2 --http2-prior-knowledge http://localhost:7777
RUST_LOG=h2::proto=trace
1506195186.415549 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1506195186.415734 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1506195186.415819 TRACE h2::proto::streams::prioritize Prioritize::new; flow=FlowControl { window_size: 65535, available: 65535 }
1506195186.415898 TRACE h2::proto::settings send_pending_ack; pending=None
1506195186.416754 TRACE h2::proto::connection recv SETTINGS; frame=Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: Some(0), max_concurrent_streams: Some(100), initial_window_size: Some(1073741824), max_frame_size: None, max_header_list_size: None }
1506195186.416808 TRACE h2::proto::settings send_pending_ack; pending=Some(Settings { flags: SettingsFlags(0), header_table_size: None, enable_push: Some(0), max_concurrent_streams: Some(100), initial_window_size: Some(1073741824), max_frame_size: None, max_header_list_size: None })
1506195186.416850 TRACE h2::proto::settings ACK sent; applying settings
1506195186.416920 TRACE h2::proto::connection recv WINDOW_UPDATE; frame=WindowUpdate { stream_id: StreamId(0), size_increment: 1073676289 }
1506195186.416962 TRACE h2::proto::streams::flow_control inc_window; sz=1073676289; old=65535; new=1073741824
1506195186.417012 TRACE h2::proto::settings send_pending_ack; pending=None
1506195186.418192 TRACE h2::proto::connection recv HEADERS; frame=Headers { stream_id: StreamId(1), stream_dep: None, flags: HeadersFlag { end_stream: true, end_headers: true, padded: false, priority: false } }
1506195186.418316 TRACE h2::proto::streams::flow_control inc_window; sz=65535; old=0; new=65535
1506195186.418357 TRACE h2::proto::streams::flow_control inc_window; sz=1073741824; old=0; new=1073741824
1506195186.418484 TRACE h2::proto::streams::streams recv_headers; stream=StreamId(1); state=State { inner: Idle }
1506195186.418538 TRACE h2::proto::streams::recv opening stream; init_window=65535
1506195186.418589 DEBUG h2::proto::connection Connection::poll; err=ProtocolError
1506195186.418631 TRACE h2::proto::streams::state recv_err; err=Proto(ProtocolError)
thread 'main' panicked at 'attempt to subtract with overflow', /Users/ver/.cargo/git/checkouts/h2-5627e19d2c95dbee/a72a6bc/src/proto/streams/counts.rs:141:13
stack backtrace:
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
1: std::sys_common::backtrace::_print
2: std::panicking::default_hook::{{closure}}
3: std::panicking::default_hook
4: std::panicking::rust_panic_with_hook
5: std::panicking::begin_panic
6: std::panicking::begin_panic_fmt
7: rust_begin_unwind
8: core::panicking::panic_fmt
9: core::panicking::panic
10: <h2::proto::streams::counts::Counts<P>>::dec_num_streams
11: <h2::proto::streams::counts::Counts<P>>::transition_after
12: <h2::proto::streams::counts::Counts<P>>::transition
13: <h2::proto::streams::streams::Streams<B, P>>::recv_err::{{closure}}
14: <h2::proto::streams::store::Store<B, P>>::for_each
15: <h2::proto::streams::streams::Streams<B, P>>::recv_err
16: <h2::proto::connection::Connection<T, P, B>>::poll
17: <h2::server::Server<T, B>>::poll_close
18: <h2::server::Server<T, B> as futures::stream::Stream>::poll
19: <futures::stream::fold::Fold<S, F, Fut, T> as futures::future::Future>::poll
20: <futures::future::map_err::MapErr<A, F> as futures::future::Future>::poll
21: <futures::future::chain::Chain<A, B, C>>::poll
22: <futures::future::and_then::AndThen<A, B, F> as futures::future::Future>::poll
23: <futures::future::map::Map<A, F> as futures::future::Future>::poll
24: <futures::future::chain::Chain<A, B, C>>::poll
25: <futures::future::and_then::AndThen<A, B, F> as futures::future::Future>::poll
26: <alloc::boxed::Box<F> as futures::future::Future>::poll
27: <tower_h2::server::ServeConnection as futures::future::Future>::poll
28: <futures::future::map_err::MapErr<A, F> as futures::future::Future>::poll
29: <alloc::boxed::Box<F> as futures::future::Future>::poll
30: <futures::task_impl::Spawn<F>>::poll_future_notify::{{closure}}
31: <futures::task_impl::Spawn<T>>::enter::{{closure}}
32: futures::task_impl::std::set
33: <futures::task_impl::Spawn<T>>::enter
34: <futures::task_impl::Spawn<F>>::poll_future_notify
35: tokio_core::reactor::Core::dispatch_task::{{closure}}
36: <scoped_tls::ScopedKey<T>>::set
37: tokio_core::reactor::Core::dispatch_task
38: tokio_core::reactor::Core::dispatch
39: tokio_core::reactor::Core::poll
40: tokio_core::reactor::Core::run
41: buoxy::main
42: __rust_maybe_catch_panic
43: std::rt::lang_start
44: main
tshark output
Transmission Control Protocol, Src Port: 7777, Dst Port: 55059, Seq: 1, Ack: 1, Len: 9
HyperText Transfer Protocol 2
Stream: SETTINGS, Stream ID: 0, Length 0
Length: 0
Type: SETTINGS (4)
Flags: 0x00
.... ...0 = ACK: False
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
Transmission Control Protocol, Src Port: 55059, Dst Port: 7777, Seq: 25, Ack: 10, Len: 27
HyperText Transfer Protocol 2
Stream: SETTINGS, Stream ID: 0, Length 18
Length: 18
Type: SETTINGS (4)
Flags: 0x00
.... ...0 = ACK: False
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
Settings - Max concurrent streams : 100
Settings Identifier: Max concurrent streams (3)
Max concurrent streams: 100
Settings - Initial Windows size : 1073741824
Settings Identifier: Initial Windows size (4)
Initial Windows Size: 1073741824
Settings - Enable PUSH : 0
Settings Identifier: Enable PUSH (2)
Enable PUSH: 0
Transmission Control Protocol, Src Port: 55059, Dst Port: 7777, Seq: 52, Ack: 10, Len: 13
HyperText Transfer Protocol 2
Stream: WINDOW_UPDATE, Stream ID: 0, Length 4
Length: 4
Type: WINDOW_UPDATE (8)
Flags: 0x00
0000 0000 = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
0... .... .... .... .... .... .... .... = Reserved: 0x0
.011 1111 1111 1111 0000 0000 0000 0001 = Window Size Increment: 1073676289
Transmission Control Protocol, Src Port: 55059, Dst Port: 7777, Seq: 65, Ack: 10, Len: 39
HyperText Transfer Protocol 2
Stream: HEADERS, Stream ID: 1, Length 30
Length: 30
Type: HEADERS (1)
Flags: 0x05
.... ...1 = End Stream: True
.... .1.. = End Headers: True
.... 0... = Padded: False
..0. .... = Priority: False
00.0 ..0. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
[Pad Length: 0]
Header Block Fragment: 828486418aa0e41d139d09b8ebaebb7a8825b650c3abb6da...
[Header Length: 129]
[Header Count: 6]
Header: :method: GET
Name Length: 7
Name: :method
Value Length: 3
Value: GET
Representation: Indexed Header Field
Index: 2
Header: :path: /
Name Length: 5
Name: :path
Value Length: 1
Value: /
Representation: Indexed Header Field
Index: 4
Header: :scheme: http
Name Length: 7
Name: :scheme
Value Length: 4
Value: http
Representation: Indexed Header Field
Index: 6
Header: :authority: localhost:7777
Name Length: 10
Name: :authority
Value Length: 14
Value: localhost:7777
Representation: Literal Header Field with Incremental Indexing - Indexed Name
Index: 1
Header: user-agent: curl/7.55.1
Name Length: 10
Name: user-agent
Value Length: 11
Value: curl/7.55.1
Representation: Literal Header Field with Incremental Indexing - Indexed Name
Index: 58
Header: accept: /
Name Length: 6
Name: accept
Value Length: 3
Value: /
Representation: Literal Header Field with Incremental Indexing - Indexed Name
Index: 19
Padding:
Transmission Control Protocol, Src Port: 55059, Dst Port: 7777, Seq: 104, Ack: 10, Len: 9
HyperText Transfer Protocol 2
Stream: SETTINGS, Stream ID: 0, Length 0
Length: 0
Type: SETTINGS (4)
Flags: 0x01
.... ...1 = ACK: True
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
gRPC clients, for instance, set an initial window of ~1MB. We should have a way to configure window sizes on clients and servers (i.e. esp prior to handshake).
Such intermediaries SHOULD also remove other connection-specific header fields, such as Keep-Alive, Proxy-Connection, Transfer-Encoding, and Upgrade, even if they are not nominated by the Connection header field.
If a header frame is sent that includes a Connection
field, it should be stripped including all fields referenced by the Connection
field value.
Recommend Projects
-
-
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
An Open Source Machine Learning Framework for Everyone
-
The Web framework for perfectionists with deadlines.
-
A PHP framework for web artisans
-
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
Some thing interesting about web. New door for the world.
-
A server is a program made to process requests and deliver data to clients.
-
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Some thing interesting about visualization, use data art
-
Some thing interesting about game, make everyone happy.
-
Recommend Org
-
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Open source projects and samples from Microsoft.
-
Google ❤️ Open Source for everyone.
-
Alibaba Open Source for everyone
-
Data-Driven Documents codes.
-
China tencent open source team.
-