Skip to content

Commit

Permalink
article: colouring TCP packets in diagrams
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandre Bonfitto committed Mar 12, 2024
1 parent 9bcff7c commit c5aac4f
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 42 deletions.
59 changes: 38 additions & 21 deletions src/posts/2024/03/http2-and-http3-explained.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,19 @@ sequenceDiagram

With HTTP/2, this problem is solved with *streams*, each stream corresponds to a message. Many streams can be interleaved in a single TCP packet. If a stream can't emit its data for some reason, other streams can take its place in the TCP packet.

HTTP/2 streams are composed by *frames*, each one containing: the frame type, the stream that it belongs to, and the length in bytes. In the diagram below, one ✉ is a HTTP/2 frame and each line is one TCP packet. The third TCP packet carries frames of two different streams.
HTTP/2 streams are composed by *frames*, each one containing: the frame type, the stream that it belongs to, and the length in bytes. In the diagram below, a coloured rectangle is a TCP packet and a ✉ is a HTTP/2 frame inside it. The first and third TCP packets carry frames of different streams.

```mermaid
sequenceDiagram
Client->>+Server: req1: #9993;1/1
Client->>+Server: req2: #9993;1/1
Server-->>-Client: res1: #9993;1/2 + res2: #9993;1/1
Server-->>-Client: res1: #9993;2/2
rect rgb(239,190,125)
Client->>+Server: req1: #9993;1/1<br>+<br>req2: #9993;1/1
end
rect rgb(197, 234, 189)
Server-->>Client: res1: #9993;1/2
end
rect rgb(197, 234, 189)
Server-->>-Client: res1: #9993;2/2<br>+<br>res2: #9993;1/1
end
```

The image below shows how frames go inside a TCP packet. Stream 1 carries a HTTP response for a JavaScript file and stream 2 carries a HTTP response for a CSS file.
Expand All @@ -147,16 +152,23 @@ HTTP/3 was born from a new transport protocol, QUIC, created by Google in 2012.

HTTP/2 solves the HTTP head-of-line blocking, but, this problem also happens with TCP and TLS. TCP understands that the data it needs to send is a contiguous sequence of packets, and if any packet is lost, it must be resent, in order to preserve information integrity. *With TCP, subsequent packets cannot be sent until the lost packet is successfully resent to the destination.*

The diagram below explains visually how this happens in HTTP/2, with each line corresponding to a TCP packet. The third packet had frames of both response 1 and response 2 and its loss delays both responses - that means that in this case, there is no parallelism.
The diagram below explains visually how this happens in HTTP/2. The third packet only had frames of response 1, but its loss delays both of responses - that means that in this case, there is no parallelism.

```mermaid
sequenceDiagram
Client->>+Server: req1: #9993;1/1
Client->>+Server: req2: #9993;1/1
Server--xClient: res1: #9993;1/2 + res2: #9993;1/1
Note over Client,Server: lost TCP packet delays res1 and res2
Server-->>-Client: res1: #9993;1/2 + res2: #9993;1/1
Server-->>-Client: res1: #9993;2/2
rect rgb(239,190,125)
Client->>+Server: req1: #9993;1/1<br>+<br>req2: #9993;1/1
end
rect rgb(197, 234, 189)
Server--xClient: res1: #9993;1/2
end
Note over Client,Server: lost TCP packet<br>must be resent.<br>delays res1 and res2
rect rgb(197, 234, 189)
Server-->>Client: res1: #9993;1/2
end
rect rgb(197, 234, 189)
Server-->>-Client: res1: #9993;2/2<br>+<br>res2: #9993;1/1
end
```

To solve TCP's head-of-line blocking, QUIC decided to use UDP for its transport protocol, because UDP does not care for guarantees of arrival. The data integrity responsibility, that in TCP is part of the transport layer, is moved in QUIC to the application layer, and the frames of a message can arrive out of order, without blocking unrelated streams.
Expand All @@ -165,15 +177,20 @@ To solve TCP's head-of-line blocking, QUIC decided to use UDP for its transport

```mermaid
sequenceDiagram
Client->>+Server: req1: #9993;1/1
Client->>+Server: req2: #9993;1/1
Client->>+Server: req3: #9993;1/1
Server--xClient: res1: #9993;1/1 + res2: #9993;1/2
Note over Client,Server: res2: #9993;2/2 can be sent even <br>without res2: #9993;1/2 arriving
Server-->>Client: res2: #9993;2/2 + res3: #9993;1/2
Server-->>-Client: res3: #9993;2/2
Note over Client,Server: resent of the lost packet.<br> res3 was unharmed
Server-->>-Client: res1: #9993;1/1 + res2: #9993;1/2
rect rgb(253, 213, 224)
Client->>Server: req1: #9993;1/1<br>+<br>req2: #9993;1/1<br>+<br>req3: #9993;1/1
end
rect rgb(179, 205, 230)
Server--xClient: res1: #9993;1/2<br>+<br>res2: #9993;1/2
end
Note over Client,Server: lost QUIC packet<br>doesn't block sending<br>other packets
rect rgb(179, 205, 230)
Server-->>Client: res1: #9993;2/2<br>+<br>res2: #9993;2/2<br>+<br>res3: #9993;1/1
end
Note over Client,Server: resending lost packet.<br>res3 wasn't delayed
rect rgb(179, 205, 230)
Server-->>Client: res1: #9993;1/2<br>+<br>res2: #9993;1/2
end
```

The head-of-line blocking related to TLS (SSL) happens on TCP because the cryptography is usually applied over the entire message content, meaning that all data (all packets) needs to be received for the decryption to happen. With QUIC, the cryptography is individual for each QUIC packet, that is decrypted on arrival, without having to receive all packets beforehand.
Expand Down
59 changes: 38 additions & 21 deletions src/posts/2024/03/http2-e-http3-explicados.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,19 @@ sequenceDiagram

Com o HTTP/2, esse problema é resolvido através de *streams*: cada *stream* corresponde a uma mensagem. Vários *streams* podem estar entremeados dentro de um mesmo pacote TCP. Se um *stream* não puder emitir dados por algum motivo, outros podem aproveitar e entrar em seu lugar no pacote TCP.

Os *streams* são divididos em *frames*, cada um contendo: o tipo do *frame*, o *stream* ao qual pertence, e o comprimento em bytes. No diagrama abaixo, um é um *frame* HTTP/2 e cada linha é um pacote TCP. O terceiro pacote TCP carrega *frames* de dois *streams* diferentes.
Os *streams* são divididos em *frames*, cada um contendo: o tipo do *frame*, o *stream* ao qual pertence, e o comprimento em bytes. No diagrama abaixo, um retângulo colorido é um pacote TCP e um ✉ é um *frame* HTTP/2. O primeiro e o terceiro pacotes TCP carregam *frames* de *streams* diferentes.

```mermaid
sequenceDiagram
Cliente->>+Servidor: req1: #9993;1/1
Cliente->>+Servidor: req2: #9993;1/1
Servidor-->>-Cliente: res1: #9993;1/2 + res2: #9993;1/1
Servidor-->>-Cliente: res1: #9993;2/2
rect rgb(239,190,125)
Cliente->>+Servidor: req1: #9993;1/1<br>+<br>req2: #9993;1/1
end
rect rgb(197, 234, 189)
Servidor-->>Cliente: res1: #9993;1/2
end
rect rgb(197, 234, 189)
Servidor-->>-Cliente: res1: #9993;2/2<br>+<br>res2: #9993;1/1
end
```

A imagem abaixo mostra como os *frames* entram em pacotes TCP. O *stream* 1 representa uma resposta HTTP de um arquivo JavaScript e o *stream* 2 representa uma resposta HTTP de um arquivo CSS, transmitidos via HTTP/2.
Expand All @@ -147,16 +152,23 @@ O HTTP/3 surgiu diante de um novo protocolo de transporte proposto pelo Google,

O HTTP/2 consegue resolver o bloqueio de cabeça de fila relacionado ao HTTP, porém, esse tipo de bloqueio também existe no protocolo TCP e no TLS. O TCP entende que os dados que deve enviar fazem parte de uma seqüência de pacotes contígüos, e se um desses pacotes for perdido, ele deve ser reenviado para o destinatário, a fim de que se preserve a integridade da informação. *No TCP, pacotes subseqüentes não podem ser enviados enquanto o pacote perdido não chegar com sucesso no destino.*

O diagrama abaixo explica visualmente como isso ocorre no HTTP/2, com cada linha correspondendo a um pacote TCP enviado. O terceiro pacote tinha *frames* tanto da resposta 1 como resposta 2 e a perda desse pacote atrasa ambas - ou seja, não há paralelismo nesse caso.
O diagrama abaixo explica visualmente como isso ocorre no HTTP/2. O terceiro pacote tinha *frames* apenas da resposta 1, porém a perda dele atrasa ambas as respostas - ou seja, não há paralelismo nesse caso.

```mermaid
sequenceDiagram
Cliente->>+Servidor: req1: #9993;1/1
Cliente->>+Servidor: req2: #9993;1/1
Servidor--xCliente: res1: #9993;1/2 + res2: #9993;1/1
Note over Cliente,Servidor: pacote TCP perdido atrasa res1 e res2
Servidor-->>-Cliente: res1: #9993;1/2 + res2: #9993;1/1
Servidor-->>-Cliente: res1: #9993;2/2
rect rgb(239,190,125)
Cliente->>+Servidor: req1: #9993;1/1<br>+<br>req2: #9993;1/1
end
rect rgb(197, 234, 189)
Servidor--xCliente: res1: #9993;1/2
end
Note over Cliente,Servidor: pacote TCP perdido<br>precisa ser reenviado.<br>atrasa res1 e res2
rect rgb(197, 234, 189)
Servidor-->>Cliente: res1: #9993;1/2
end
rect rgb(197, 234, 189)
Servidor-->>-Cliente: res1: #9993;2/2<br>+<br>res2: #9993;1/1
end
```

Para resolver o bloqueio de cabeça de fila do TCP, o QUIC opta por utilizar o UDP como protocolo de transporte, pois este é um protocolo sem garantias de recebimento. A responsabilidade de garantia de integridade, que no TCP fica na camada de transporte, passa no QUIC para a camada de aplicação, de modo que os *frames* de uma mensagem podem chegar fora de ordem, sem bloquear *streams* não-relacionados.
Expand All @@ -165,15 +177,20 @@ Para resolver o bloqueio de cabeça de fila do TCP, o QUIC opta por utilizar o U

```mermaid
sequenceDiagram
Cliente->>+Servidor: req1: #9993;1/1
Cliente->>+Servidor: req2: #9993;1/1
Cliente->>+Servidor: req3: #9993;1/1
Servidor--xCliente: res1: #9993;1/1 + res2: #9993;1/2
Note over Cliente,Servidor: res2: #9993;2/2 pode ser enviado mesmo <br> sem res2: #9993;1/2 ter chegado
Servidor-->>Cliente: res2: #9993;2/2 + res3: #9993;1/2
Servidor-->>-Cliente: res3: #9993;2/2
Note over Cliente,Servidor: reenvio do pacote perdido. <br> res3 não foi afetado
Servidor-->>-Cliente: res1: #9993;1/1 + res2: #9993;1/2
rect rgb(253, 213, 224)
Client->>Server: req1: #9993;1/1<br>+<br>req2: #9993;1/1<br>+<br>req3: #9993;1/1
end
rect rgb(179, 205, 230)
Server--xClient: res1: #9993;1/2<br>+<br>res2: #9993;1/2
end
Note over Client,Server: lost QUIC packet<br>doesn't block other packets
rect rgb(179, 205, 230)
Server-->>Client: res1: #9993;2/2<br>+<br>res2: #9993;2/2<br>+<br>res3: #9993;1/1
end
Note over Client,Server: resending lost packet.<br>res3 unaffected
rect rgb(179, 205, 230)
Server-->>Client: res1: #9993;1/2<br>+<br>res2: #9993;1/2
end
```

O bloqueio de cabeça de fila relacionado ao TLS (criptografia SSL) ocorre no TCP porque a criptografia é geralmente aplicada sobre a mensagem inteira, de modo que todos os seus pacotes precisam chegar ao destino para então ocorrer a decriptação. No caso do QUIC, a criptografia é individual para cada pacote QUIC, que é decriptado na chegada, sem haver a necessidade de receber todos os pacotes primeiro.
Expand Down

0 comments on commit c5aac4f

Please sign in to comment.