-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add UDP, Time, & Duration to Connectivity Report #327
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ type connectivityReport struct { | |
Test testReport `json:"test"` | ||
DNSQueries []dnsReport `json:"dns_queries,omitempty"` | ||
TCPConnections []tcpReport `json:"tcp_connections,omitempty"` | ||
UDPConnections []udpReport `json:"udp_connections,omitempty"` | ||
} | ||
|
||
type testReport struct { | ||
|
@@ -68,10 +69,21 @@ type dnsReport struct { | |
} | ||
|
||
type tcpReport struct { | ||
Hostname string `json:"hostname"` | ||
IP string `json:"ip"` | ||
Port string `json:"port"` | ||
Error string `json:"error"` | ||
Hostname string `json:"hostname"` | ||
IP string `json:"ip"` | ||
Port string `json:"port"` | ||
Error string `json:"error"` | ||
Time time.Time `json:"time"` | ||
Duration int64 `json:"duration_ms"` | ||
} | ||
|
||
type udpReport struct { | ||
Hostname string `json:"hostname"` | ||
IP string `json:"ip"` | ||
Port string `json:"port"` | ||
Error string `json:"error"` | ||
Time time.Time `json:"time"` | ||
Duration int64 `json:"duration_ms"` | ||
} | ||
|
||
type errorJSON struct { | ||
|
@@ -80,7 +92,8 @@ type errorJSON struct { | |
// Posix error, when available | ||
PosixError string `json:"posix_error,omitempty"` | ||
// TODO: remove IP addresses | ||
Msg string `json:"msg,omitempty"` | ||
Msg string `json:"msg,omitempty"` | ||
MsgFull string `json:"msg_full,omitempty"` | ||
} | ||
|
||
func makeErrorRecord(result *connectivity.ConnectivityError) *errorJSON { | ||
|
@@ -91,6 +104,7 @@ func makeErrorRecord(result *connectivity.ConnectivityError) *errorJSON { | |
record.Op = result.Op | ||
record.PosixError = result.PosixError | ||
record.Msg = unwrapAll(result.Err).Error() | ||
record.MsgFull = result.Err.Error() | ||
return record | ||
} | ||
|
||
|
@@ -120,7 +134,9 @@ func init() { | |
} | ||
func newTCPTraceDialer( | ||
onDNS func(ctx context.Context, domain string) func(di httptrace.DNSDoneInfo), | ||
onDial func(ctx context.Context, network, addr string, connErr error)) transport.StreamDialer { | ||
onDial func(ctx context.Context, network, addr string, connErr error), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This design puts the onus on the user to match dial start and end. That also complicates the testing code. Please change the design so that we have a single onDial that is called on the beginning of the dial, and returns the onDialDone handler, like we do for DNS. In the case of TCP/UDP, you will need a map (net, addr) -> done handler that you can create here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @fortuna I am going to iterate on the proposed design. Ideally the user should just run the test and get report with a clean API that is part of the SDK (/x) and |
||
onDialStart func(ctx context.Context, network, addr string), | ||
) transport.StreamDialer { | ||
dialer := &transport.TCPDialer{} | ||
var onDNSDone func(di httptrace.DNSDoneInfo) | ||
return transport.FuncStreamDialer(func(ctx context.Context, addr string) (transport.StreamConn, error) { | ||
|
@@ -134,6 +150,9 @@ func newTCPTraceDialer( | |
onDNSDone = nil | ||
} | ||
}, | ||
ConnectStart: func(network, addr string) { | ||
onDialStart(ctx, network, addr) | ||
}, | ||
ConnectDone: func(network, addr string, connErr error) { | ||
onDial(ctx, network, addr, connErr) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should lookup the table to retrieve and call the onDone handler. |
||
}, | ||
|
@@ -143,7 +162,10 @@ func newTCPTraceDialer( | |
} | ||
|
||
func newUDPTraceDialer( | ||
onDNS func(ctx context.Context, domain string) func(di httptrace.DNSDoneInfo)) transport.PacketDialer { | ||
onDNS func(ctx context.Context, domain string) func(di httptrace.DNSDoneInfo), | ||
onDial func(ctx context.Context, network, addr string, connErr error), | ||
onDialStart func(ctx context.Context, network, addr string), | ||
) transport.PacketDialer { | ||
dialer := &transport.UDPDialer{} | ||
var onDNSDone func(di httptrace.DNSDoneInfo) | ||
return transport.FuncPacketDialer(func(ctx context.Context, addr string) (net.Conn, error) { | ||
|
@@ -157,6 +179,12 @@ func newUDPTraceDialer( | |
onDNSDone = nil | ||
} | ||
}, | ||
ConnectStart: func(network, addr string) { | ||
onDialStart(ctx, network, addr) | ||
}, | ||
ConnectDone: func(network, addr string, connErr error) { | ||
onDial(ctx, network, addr, connErr) | ||
}, | ||
}) | ||
return dialer.DialPacket(ctx, addr) | ||
}) | ||
|
@@ -240,6 +268,8 @@ func main() { | |
var mu sync.Mutex | ||
dnsReports := make([]dnsReport, 0) | ||
tcpReports := make([]tcpReport, 0) | ||
udpReports := make([]udpReport, 0) | ||
var connectStart = make(map[string]time.Time) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to the newTraceDialers |
||
providers := configurl.NewDefaultProviders() | ||
onDNS := func(ctx context.Context, domain string) func(di httptrace.DNSDoneInfo) { | ||
dnsStart := time.Now() | ||
|
@@ -274,6 +304,8 @@ func main() { | |
Hostname: hostname, | ||
IP: ip, | ||
Port: port, | ||
Time: connectStart[network+"|"+addr].UTC().Truncate(time.Second), | ||
Duration: time.Since(connectStart[network+"|"+addr]).Milliseconds(), | ||
} | ||
if connErr != nil { | ||
report.Error = connErr.Error() | ||
|
@@ -282,10 +314,46 @@ func main() { | |
tcpReports = append(tcpReports, report) | ||
mu.Unlock() | ||
} | ||
return newTCPTraceDialer(onDNS, onDial).DialStream(ctx, addr) | ||
onDialStart := func(ctx context.Context, network, addr string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should save the start time, and return the onDone handler, which can refer to the start time directly. |
||
mu.Lock() | ||
connectStart[network+"|"+addr] = time.Now() | ||
mu.Unlock() | ||
} | ||
|
||
return newTCPTraceDialer(onDNS, onDial, onDialStart).DialStream(ctx, addr) | ||
}) | ||
|
||
providers.PacketDialers.BaseInstance = transport.FuncPacketDialer(func(ctx context.Context, addr string) (net.Conn, error) { | ||
return newUDPTraceDialer(onDNS).DialPacket(ctx, addr) | ||
hostname, _, err := net.SplitHostPort(addr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
onDialStart := func(ctx context.Context, network, addr string) { | ||
mu.Lock() | ||
connectStart[network+"|"+addr] = time.Now() | ||
mu.Unlock() | ||
} | ||
onDial := func(ctx context.Context, network, addr string, connErr error) { | ||
ip, port, err := net.SplitHostPort(addr) | ||
if err != nil { | ||
return | ||
} | ||
report := udpReport{ | ||
Hostname: hostname, | ||
IP: ip, | ||
Port: port, | ||
Time: connectStart[network+"|"+addr].UTC().Truncate(time.Second), | ||
Duration: time.Since(connectStart[network+"|"+addr]).Milliseconds(), | ||
} | ||
if connErr != nil { | ||
report.Error = connErr.Error() | ||
} | ||
mu.Lock() | ||
udpReports = append(udpReports, report) | ||
mu.Unlock() | ||
} | ||
|
||
return newUDPTraceDialer(onDNS, onDial, onDialStart).DialPacket(ctx, addr) | ||
}) | ||
|
||
switch proto { | ||
|
@@ -336,6 +404,7 @@ func main() { | |
}, | ||
DNSQueries: dnsReports, | ||
TCPConnections: tcpReports, | ||
UDPConnections: udpReports, | ||
} | ||
if reportCollector != nil { | ||
err = reportCollector.Collect(context.Background(), r) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to note that these two fields may contain Personally Identifiable Information (PII). We need to figure out a way to not leak that before this is actually used in production with user probes.
We should probably allowlist specific errors as we learn about them. Though we need the full message to learn about them.