Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions defs/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ type Server struct {

NoICMP bool `json:"-"`
TLog TelemetryLog `json:"-"`

// What the connection to this server actually negotiated; nil over
// plain HTTP. Filled by IsUp, which runs before every measurement.
NegotiatedTLS *TLSInfo `json:"-"`
}

// TLSInfo names the negotiated TLS parameters of a connection.
type TLSInfo struct {
Version string
Cipher string
}

// IsUp checks the speed test backend is up by accessing the ping URL
Expand Down Expand Up @@ -73,9 +83,15 @@ func (s *Server) IsUp() bool {
// Two runs can therefore differ several-fold for a reason the numbers alone
// do not show, which is what this line is for.
if resp.TLS != nil {
// Kept for the report as well as logged: the cipher explains the
// number next to it, and a debug line is gone when the history
// entry it would explain is read.
s.NegotiatedTLS = &TLSInfo{
Version: tls.VersionName(resp.TLS.Version),
Cipher: tls.CipherSuiteName(resp.TLS.CipherSuite),
}
output.WriteDebug("Negotiated %s with %s\n",
tls.VersionName(resp.TLS.Version),
tls.CipherSuiteName(resp.TLS.CipherSuite))
s.NegotiatedTLS.Version, s.NegotiatedTLS.Cipher)
} else {
output.WriteDebug("Connection is not encrypted\n")
}
Expand Down
49 changes: 49 additions & 0 deletions defs/tls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package defs

import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)

// The negotiated parameters must reach the Server for the report, and stay
// absent over plain HTTP.
func TestIsUpCapturesNegotiatedTLS(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})

tlsSrv := httptest.NewTLSServer(handler)
defer tlsSrv.Close()

// IsUp uses http.DefaultClient; trust the test server's certificate
// for the duration of the test.
orig := http.DefaultClient
http.DefaultClient = tlsSrv.Client()
defer func() { http.DefaultClient = orig }()

s := Server{Name: "tls", Server: tlsSrv.URL, PingURL: "/empty"}

if !s.IsUp() {
t.Fatal("TLS test server reported down")
}
if s.NegotiatedTLS == nil {
t.Fatal("NegotiatedTLS not captured over HTTPS")
}
if !strings.HasPrefix(s.NegotiatedTLS.Version, "TLS") || s.NegotiatedTLS.Cipher == "" {
t.Fatalf("implausible negotiation: %+v", s.NegotiatedTLS)
}

plainSrv := httptest.NewServer(handler)
defer plainSrv.Close()

p := Server{Name: "plain", Server: plainSrv.URL, PingURL: "/empty"}

if !p.IsUp() {
t.Fatal("plain test server reported down")
}
if p.NegotiatedTLS != nil {
t.Fatalf("NegotiatedTLS set over plain HTTP: %+v", p.NegotiatedTLS)
}
}
13 changes: 13 additions & 0 deletions report/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ type JSONReport struct {
Upload float64 `json:"upload"`
Download float64 `json:"download"`
Share string `json:"share"`

// TLS is what the connection to the server negotiated, absent over
// plain HTTP. On hardware without AES acceleration the cipher, not
// the link, can bound the result, and under TLS 1.3 the server picks
// it -- so two otherwise identical runs can differ several-fold for a
// reason the numbers alone do not show.
TLS *TLSReport `json:"tls,omitempty"`
}

// TLSReport names the negotiated TLS parameters a measurement ran over.
type TLSReport struct {
Version string `json:"version"`
Cipher string `json:"cipher"`
}

// Server represents the speed test server's information
Expand Down
7 changes: 7 additions & 0 deletions speedtest/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ output.WriteDebug("IP info: %s\n", output.Sanitize(ispInfo.ProcessedString))
rep.Server.Name = currentServer.Name
rep.Server.URL = u.String()

if currentServer.NegotiatedTLS != nil {
rep.TLS = &report.TLSReport{
Version: currentServer.NegotiatedTLS.Version,
Cipher: currentServer.NegotiatedTLS.Cipher,
}
}

rep.Client = report.NewClient(ispInfo.RawISPInfo)
rep.Client.Readme = ""
// IP() falls back to processedString, so the report carries an
Expand Down