ooni-probe-cli/internal/measurexlite/quic.go
Simone Basso b78b9aca51
refactor(datafmt): use "udp" instead of "quic" (#946)
This diff changes the data format to prefer "udp" to "quic" everywhere we were previously using "quic".

Previously, the code inconsistently used "quic" for operations where we knew we were using "quic" and "udp" otherwise (e.g., for generic operations like ReadFrom).

While it would be more correct to say that a specific HTTP request used "quic" rather than "udp", using "udp" consistently allows one to see how distinct events such as ReadFrom and an handshake all refer to the same address, port, and protocol triple. Therefore, this change makes it easier to programmatically unpack a single measurement and create endpoint stats.

Before implementing this change, I discussed the problem with @hellais who mentioned that ooni/data is not currently using the "quic" string anywhere. I know that ooni/pipeline also doesn't rely on this string. The only users of this feature have been research-oriented experiments such as urlgetter, for which such a change would actually be acceptable.

See https://github.com/ooni/probe/issues/2238 and https://github.com/ooni/spec/pull/262.
2022-09-08 17:19:59 +02:00

103 lines
2.8 KiB
Go

package measurexlite
//
// QUIC tracing
//
import (
"context"
"crypto/tls"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite"
)
// NewQUICDialerWithoutResolver is equivalent to netxlite.NewQUICDialerWithoutResolver
// except that it returns a model.QUICDialer that uses this trace.
func (tx *Trace) NewQUICDialerWithoutResolver(listener model.QUICListener, dl model.DebugLogger) model.QUICDialer {
return &quicDialerTrace{
qd: tx.newQUICDialerWithoutResolver(listener, dl),
tx: tx,
}
}
// quicDialerTrace is a trace-aware QUIC dialer.
type quicDialerTrace struct {
qd model.QUICDialer
tx *Trace
}
var _ model.QUICDialer = &quicDialerTrace{}
// DialContext implements model.QUICDialer.DialContext.
func (qdx *quicDialerTrace) DialContext(ctx context.Context,
address string, tlsConfig *tls.Config, quicConfig *quic.Config) (
quic.EarlyConnection, error) {
return qdx.qd.DialContext(netxlite.ContextWithTrace(ctx, qdx.tx), address, tlsConfig, quicConfig)
}
// CloseIdleConnections implements model.QUICDialer.CloseIdleConnections.
func (qdx *quicDialerTrace) CloseIdleConnections() {
qdx.qd.CloseIdleConnections()
}
// OnQUICHandshakeStart implements model.Trace.OnQUICHandshakeStart
func (tx *Trace) OnQUICHandshakeStart(now time.Time, remoteAddr string, config *quic.Config) {
t := now.Sub(tx.ZeroTime)
select {
case tx.networkEvent <- NewAnnotationArchivalNetworkEvent(tx.Index, t, "quic_handshake_start"):
default:
}
}
// OnQUICHandshakeDone implements model.Trace.OnQUICHandshakeDone
func (tx *Trace) OnQUICHandshakeDone(started time.Time, remoteAddr string, qconn quic.EarlyConnection,
config *tls.Config, err error, finished time.Time) {
t := finished.Sub(tx.ZeroTime)
state := tls.ConnectionState{}
if qconn != nil {
state = qconn.ConnectionState().TLS.ConnectionState
}
select {
case tx.quicHandshake <- NewArchivalTLSOrQUICHandshakeResult(
tx.Index,
started.Sub(tx.ZeroTime),
"udp",
remoteAddr,
config,
state,
err,
t,
):
default: // buffer is full
}
select {
case tx.networkEvent <- NewAnnotationArchivalNetworkEvent(tx.Index, t, "quic_handshake_done"):
default: // buffer is full
}
}
// QUICHandshakes drains the network events buffered inside the QUICHandshake channel.
func (tx *Trace) QUICHandshakes() (out []*model.ArchivalTLSOrQUICHandshakeResult) {
for {
select {
case ev := <-tx.quicHandshake:
out = append(out, ev)
default:
return // done
}
}
}
// FirstQUICHandshakeOrNil drains the network events buffered inside the QUICHandshake channel
// and returns the first QUICHandshake, if any. Otherwise, it returns nil.
func (tx *Trace) FirstQUICHandshakeOrNil() *model.ArchivalTLSOrQUICHandshakeResult {
ev := tx.QUICHandshakes()
if len(ev) < 1 {
return nil
}
return ev[0]
}