
# Chapter V: QUIC handshaking

This chapter describes measuring QUIC handshakes. Conceptually,
and code wise, this is very similar to the previous chapter.
The API call, in fact, has exactly the same structure, though
under the hood QUIC is different because there are no
separate connection establishment and handshake primitives.
For this reason, we will not see a connect event, but we
will only see a "QUIC handshake event".

Having said that, let us now move on and see the code of
the simple program that uses this functionality.

(This file is auto-generated. Do not edit it directly! To apply
changes you need to modify `./internal/tutorial/measure/chapter05/main.go`.)

## main.go

The initial part of the program is pretty much the same as the one
used in previous chapters, so I will not add further comments.

```Go
package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"time"

	"github.com/ooni/probe-cli/v3/internal/measurex"
	"github.com/ooni/probe-cli/v3/internal/netxlite"
	"github.com/ooni/probe-cli/v3/internal/runtimex"
)

func main() {
	sni := flag.String("sni", "dns.google", "value for SNI extension")
	address := flag.String("address", "8.8.4.4:443", "remote endpoint address")
	timeout := flag.Duration("timeout", 60*time.Second, "timeout to use")
	flag.Parse()
	ctx, cancel := context.WithTimeout(context.Background(), *timeout)
	defer cancel()
	mx := measurex.NewMeasurerWithDefaultSettings()
```

### Handshaking with QUIC

The API signature is indeed the same as the previous chapter,
except that here we call the `QUICHandshake` function.

```Go
	m := mx.QUICHandshake(ctx, *address, &tls.Config{
		ServerName: *sni,
		NextProtos: []string{"h3"},
		RootCAs:    netxlite.NewDefaultCertPool(),
	})
```

The same remarks mentioned in the previous chapter regarding
the arguments for the TLS config also apply here. We need
to specify the SNI (`ServerName`), the ALPN (`NextProtos`),
and the CA pool we want to use. Here, again, we're using
the CA pool from cURL that we bundle with OONI Probe.

As we did in the previous chapters, here's the usual three
lines of code for printing the resulting measurement.

```
	data, err := json.Marshal(measurex.NewArchivalEndpointMeasurement(m))
	runtimex.PanicOnError(err, "json.Marshal failed")
	fmt.Printf("%s\n", string(data))
}

```

## Running the example program

As before, let us start off with a vanilla run:

```bash
go run -race ./internal/tutorial/measurex/chapter05 | jq
```

Produces this JSON:

```JavaScript
{
  // In chapter02 these two fields were similar but
  // the network was "tcp" as opposed to "quic"
  "network": "quic",
  "address": "8.8.4.4:443",

  // This block contains I/O operations. Note that
  // the protocol is "quic" and that the syscalls
  // are "read_from" and "write_to" because QUIC does
  // not bind/connect sockets. (The real syscalls
  // are actually `recvfrom` and `sendto` but here
  // we follow the Go convention of using read/write
  // more frequently than send/recv.)
  "network_events": [
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 1252,
      "operation": "write_to",
      "proto": "quic",
      "t": 0.027184208,
      "started": 0.027127208,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 1252,
      "operation": "read_from",
      "proto": "quic",
      "t": 0.053116458,
      "started": 0.025626583,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 1252,
      "operation": "write_to",
      "proto": "quic",
      "t": 0.054538792,
      "started": 0.054517542,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 1252,
      "operation": "read_from",
      "proto": "quic",
      "t": 0.069144958,
      "started": 0.053194208,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 1252,
      "operation": "read_from",
      "proto": "quic",
      "t": 0.069183458,
      "started": 0.069173292,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 1252,
      "operation": "read_from",
      "proto": "quic",
      "t": 0.06920225,
      "started": 0.069197875,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 1216,
      "operation": "read_from",
      "proto": "quic",
      "t": 0.069210958,
      "started": 0.069206875,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 65,
      "operation": "read_from",
      "proto": "quic",
      "t": 0.069220667,
      "started": 0.069217375,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 44,
      "operation": "write_to",
      "proto": "quic",
      "t": 0.069433417,
      "started": 0.069417625,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 44,
      "operation": "write_to",
      "proto": "quic",
      "t": 0.069677625,
      "started": 0.069647458,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 83,
      "operation": "write_to",
      "proto": "quic",
      "t": 0.073461917,
      "started": 0.073432875,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 33,
      "operation": "write_to",
      "proto": "quic",
      "t": 0.073559417,
      "started": 0.073542542,
      "oddity": ""
    }
  ],

  // This section describes the QUIC handshake and it has
  // basically the same fields as the TLS handshake.
  "quic_handshakes": [
    {
      "cipher_suite": "TLS_CHACHA20_POLY1305_SHA256",
      "failure": null,
      "negotiated_proto": "h3",
      "tls_version": "TLSv1.3",
      "peer_certificates": [
        {
          "data": " ",
          "format": "base64"
        },
        {
          "data": " ",
          "format": "base64"
        },
        {
          "data": " ",
          "format": "base64"
        }
      ],
      "t": 0.073469208,
      "address": "8.8.4.4:443",
      "server_name": "dns.google",
      "alpn": [
        "h3"
      ],
      "no_tls_verify": false,
      "oddity": "",
      "proto": "quic",
      "started": 0.025061583
    }
  ]
}
```

Here are some suggestions for other experiments to run:

1. obtain a timeout by connecting to a port that is not
actually listening for QUIC;

2. obtain a certificate validation error by forcing
a different SNI;

3. use a different ALPN (by changing the code), and see
how the error and the oddity are handled. Can we do
anything about this by changing `./internal/netxlite/errorx`
to better support this specific error condition?

## Conclusion

We have seen how to perform QUIC handshake and
collect measurements.

