diff --git a/internal/cmd/oohelperd/internal/websteps/generate.go b/internal/cmd/oohelperd/internal/websteps/generate.go index 99f2963..f67aa99 100644 --- a/internal/cmd/oohelperd/internal/websteps/generate.go +++ b/internal/cmd/oohelperd/internal/websteps/generate.go @@ -114,7 +114,7 @@ func (g *DefaultGenerator) GenerateHTTPEndpoint(ctx context.Context, rt *RoundTr Endpoint: endpoint, Resolver: g.resolver, }) - currentEndpoint.TCPConnectMeasurement = &TCPConnectMeasurement{ + currentEndpoint.TCPConnect = &TCPConnectMeasurement{ Failure: newfailure(err), } if err != nil { @@ -123,7 +123,7 @@ func (g *DefaultGenerator) GenerateHTTPEndpoint(ctx context.Context, rt *RoundTr defer tcpConn.Close() // prepare HTTPRoundTripMeasurement of this endpoint - currentEndpoint.HTTPRoundTripMeasurement = &HTTPRoundTripMeasurement{ + currentEndpoint.HTTPRoundTrip = &HTTPRoundTripMeasurement{ Request: &HTTPRequestMeasurement{ Headers: rt.Request.Header, Method: "GET", @@ -137,13 +137,13 @@ func (g *DefaultGenerator) GenerateHTTPEndpoint(ctx context.Context, rt *RoundTr resp, body, err := HTTPDo(rt.Request, transport) if err != nil { // failed Response - currentEndpoint.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + currentEndpoint.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ Failure: newfailure(err), } return currentEndpoint } // successful Response - currentEndpoint.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + currentEndpoint.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ BodyLength: int64(len(body)), Failure: nil, Headers: resp.Header, @@ -168,7 +168,7 @@ func (g *DefaultGenerator) GenerateHTTPSEndpoint(ctx context.Context, rt *RoundT Endpoint: endpoint, Resolver: g.resolver, }) - currentEndpoint.TCPConnectMeasurement = &TCPConnectMeasurement{ + currentEndpoint.TCPConnect = &TCPConnectMeasurement{ Failure: newfailure(err), } if err != nil { @@ -177,7 +177,7 @@ func (g *DefaultGenerator) GenerateHTTPSEndpoint(ctx context.Context, rt *RoundT defer tcpConn.Close() tlsConn, err = TLSDo(tcpConn, rt.Request.URL.Hostname()) - currentEndpoint.TLSHandshakeMeasurement = &TLSHandshakeMeasurement{ + currentEndpoint.TLSHandshake = &TLSHandshakeMeasurement{ Failure: newfailure(err), } if err != nil { @@ -186,7 +186,7 @@ func (g *DefaultGenerator) GenerateHTTPSEndpoint(ctx context.Context, rt *RoundT defer tlsConn.Close() // prepare HTTPRoundTripMeasurement of this endpoint - currentEndpoint.HTTPRoundTripMeasurement = &HTTPRoundTripMeasurement{ + currentEndpoint.HTTPRoundTrip = &HTTPRoundTripMeasurement{ Request: &HTTPRequestMeasurement{ Headers: rt.Request.Header, Method: "GET", @@ -200,13 +200,13 @@ func (g *DefaultGenerator) GenerateHTTPSEndpoint(ctx context.Context, rt *RoundT resp, body, err := HTTPDo(rt.Request, transport) if err != nil { // failed Response - currentEndpoint.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + currentEndpoint.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ Failure: newfailure(err), } return currentEndpoint } // successful Response - currentEndpoint.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + currentEndpoint.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ BodyLength: int64(len(body)), Failure: nil, Headers: resp.Header, @@ -234,14 +234,14 @@ func (g *DefaultGenerator) GenerateH3Endpoint(ctx context.Context, rt *RoundTrip TLSConf: tlsConf, Resolver: g.resolver, }) - currentEndpoint.QUICHandshakeMeasurement = &TLSHandshakeMeasurement{ + currentEndpoint.QUICHandshake = &TLSHandshakeMeasurement{ Failure: newfailure(err), } if err != nil { return currentEndpoint } // prepare HTTPRoundTripMeasurement of this endpoint - currentEndpoint.HTTPRoundTripMeasurement = &HTTPRoundTripMeasurement{ + currentEndpoint.HTTPRoundTrip = &HTTPRoundTripMeasurement{ Request: &HTTPRequestMeasurement{ Headers: rt.Request.Header, Method: "GET", @@ -255,13 +255,13 @@ func (g *DefaultGenerator) GenerateH3Endpoint(ctx context.Context, rt *RoundTrip resp, body, err := HTTPDo(rt.Request, transport) if err != nil { // failed Response - currentEndpoint.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + currentEndpoint.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ Failure: newfailure(err), } return currentEndpoint } // successful Response - currentEndpoint.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + currentEndpoint.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ BodyLength: int64(len(body)), Failure: nil, Headers: resp.Header, diff --git a/internal/cmd/oohelperd/internal/websteps/generate_test.go b/internal/cmd/oohelperd/internal/websteps/generate_test.go index 07bf4d1..8817d1f 100644 --- a/internal/cmd/oohelperd/internal/websteps/generate_test.go +++ b/internal/cmd/oohelperd/internal/websteps/generate_test.go @@ -220,10 +220,10 @@ func TestGenerateHTTP(t *testing.T) { if endpointMeasurement == nil { t.Fatal("unexpected nil urlMeasurement") } - if endpointMeasurement.TCPConnectMeasurement == nil { + if endpointMeasurement.TCPConnect == nil { t.Fatal("TCPConnectMeasurement should not be nil") } - if endpointMeasurement.HTTPRoundTripMeasurement == nil { + if endpointMeasurement.HTTPRoundTrip == nil { t.Fatal("HTTPRoundTripMeasurement should not be nil") } } @@ -248,13 +248,13 @@ func TestGenerateHTTPS(t *testing.T) { if endpointMeasurement == nil { t.Fatal("unexpected nil urlMeasurement") } - if endpointMeasurement.TCPConnectMeasurement == nil { + if endpointMeasurement.TCPConnect == nil { t.Fatal("TCPConnectMeasurement should not be nil") } - if endpointMeasurement.TLSHandshakeMeasurement == nil { + if endpointMeasurement.TLSHandshake == nil { t.Fatal("TCPConnectMeasurement should not be nil") } - if endpointMeasurement.HTTPRoundTripMeasurement == nil { + if endpointMeasurement.HTTPRoundTrip == nil { t.Fatal("HTTPRoundTripMeasurement should not be nil") } } @@ -279,13 +279,13 @@ func TestGenerateHTTPSTLSFailure(t *testing.T) { if endpointMeasurement == nil { t.Fatal("unexpected nil urlMeasurement") } - if endpointMeasurement.TCPConnectMeasurement == nil { + if endpointMeasurement.TCPConnect == nil { t.Fatal("TCPConnectMeasurement should not be nil") } - if endpointMeasurement.TLSHandshakeMeasurement == nil { + if endpointMeasurement.TLSHandshake == nil { t.Fatal("TCPConnectMeasurement should not be nil") } - if endpointMeasurement.HTTPRoundTripMeasurement != nil { + if endpointMeasurement.HTTPRoundTrip != nil { t.Fatal("HTTPRoundTripMeasurement should be nil") } } @@ -310,10 +310,10 @@ func TestGenerateH3(t *testing.T) { if endpointMeasurement == nil { t.Fatal("unexpected nil urlMeasurement") } - if endpointMeasurement.QUICHandshakeMeasurement == nil { + if endpointMeasurement.QUICHandshake == nil { t.Fatal("TCPConnectMeasurement should not be nil") } - if endpointMeasurement.HTTPRoundTripMeasurement == nil { + if endpointMeasurement.HTTPRoundTrip == nil { t.Fatal("HTTPRoundTripMeasurement should not be nil") } } @@ -340,13 +340,13 @@ func TestGenerateTCPDoFails(t *testing.T) { if err != nil { t.Fatal("unexpected err") } - if endpointMeasurement.TCPConnectMeasurement == nil { + if endpointMeasurement.TCPConnect == nil { t.Fatal("QUIC handshake should not be nil") } - if endpointMeasurement.TCPConnectMeasurement.Failure == nil { + if endpointMeasurement.TCPConnect.Failure == nil { t.Fatal("expected an error here") } - if *endpointMeasurement.TCPConnectMeasurement.Failure != *newfailure(expected) { + if *endpointMeasurement.TCPConnect.Failure != *newfailure(expected) { t.Fatal("unexpected error type") } } @@ -373,13 +373,13 @@ func TestGenerateQUICDoFails(t *testing.T) { if err != nil { t.Fatal("unexpected err") } - if endpointMeasurement.QUICHandshakeMeasurement == nil { + if endpointMeasurement.QUICHandshake == nil { t.Fatal("QUIC handshake should not be nil") } - if endpointMeasurement.QUICHandshakeMeasurement.Failure == nil { + if endpointMeasurement.QUICHandshake.Failure == nil { t.Fatal("expected an error here") } - if *endpointMeasurement.QUICHandshakeMeasurement.Failure != *newfailure(expected) { + if *endpointMeasurement.QUICHandshake.Failure != *newfailure(expected) { t.Fatal("unexpected error type") } } @@ -441,22 +441,22 @@ func TestGenerateHTTPDoFails(t *testing.T) { t.Fatal("unexpected number of endpoints", len(u.Endpoints)) } // this can occur when the network is unreachable, but it is irrelevant for checking HTTP behavior - if u.Endpoints[0].TCPConnectMeasurement != nil && u.Endpoints[0].TCPConnectMeasurement.Failure != nil { + if u.Endpoints[0].TCPConnect != nil && u.Endpoints[0].TCPConnect.Failure != nil { continue } - if u.Endpoints[0].QUICHandshakeMeasurement != nil && u.Endpoints[0].QUICHandshakeMeasurement.Failure != nil { + if u.Endpoints[0].QUICHandshake != nil && u.Endpoints[0].QUICHandshake.Failure != nil { continue } - if u.Endpoints[0].HTTPRoundTripMeasurement == nil { - t.Fatal("roundtrip should not be nil", u.Endpoints[0].TCPConnectMeasurement.Failure, "jaaaa") + if u.Endpoints[0].HTTPRoundTrip == nil { + t.Fatal("roundtrip should not be nil", u.Endpoints[0].TCPConnect.Failure, "jaaaa") } - if u.Endpoints[0].HTTPRoundTripMeasurement.Response == nil { + if u.Endpoints[0].HTTPRoundTrip.Response == nil { t.Fatal("roundtrip response should not be nil") } - if u.Endpoints[0].HTTPRoundTripMeasurement.Response.Failure == nil { + if u.Endpoints[0].HTTPRoundTrip.Response.Failure == nil { t.Fatal("expected an HTTP error") } - if !strings.HasSuffix(*u.Endpoints[0].HTTPRoundTripMeasurement.Response.Failure, expected.Error()) { + if !strings.HasSuffix(*u.Endpoints[0].HTTPRoundTrip.Response.Failure, expected.Error()) { t.Fatal("unexpected failure type") } } diff --git a/internal/cmd/oohelperd/internal/websteps/measure.go b/internal/cmd/oohelperd/internal/websteps/measure.go index c693fcd..5482bae 100644 --- a/internal/cmd/oohelperd/internal/websteps/measure.go +++ b/internal/cmd/oohelperd/internal/websteps/measure.go @@ -14,7 +14,7 @@ import ( type ( CtrlRequest = websteps.CtrlRequest - ControlResponse = websteps.ControlResponse + ControlResponse = websteps.CtrlResponse ) var ErrInternalServer = errors.New("internal server error") @@ -45,11 +45,11 @@ func Measure(ctx context.Context, creq *CtrlRequest, config *Config) (*ControlRe if checker == nil { checker = &DefaultInitChecker{resolver: resolver} } - URL, err = checker.InitialChecks(creq.HTTPRequest) + URL, err = checker.InitialChecks(creq.URL) if err != nil { // return a valid response in case of NXDOMAIN so the probe can compare the failure if err == ErrNoSuchHost { - return newDNSFailedResponse(err, creq.HTTPRequest), nil + return newDNSFailedResponse(err, creq.URL), nil } return nil, err } @@ -57,7 +57,7 @@ func Measure(ctx context.Context, creq *CtrlRequest, config *Config) (*ControlRe if explorer == nil { explorer = &DefaultExplorer{resolver: resolver} } - rts, err := explorer.Explore(URL, creq.HTTPRequestHeaders) + rts, err := explorer.Explore(URL, creq.Headers) if err != nil { return nil, ErrInternalServer } @@ -69,7 +69,7 @@ func Measure(ctx context.Context, creq *CtrlRequest, config *Config) (*ControlRe if err != nil { return nil, err } - return &ControlResponse{URLMeasurements: meas}, nil + return &ControlResponse{URLs: meas}, nil } // newDNSFailedResponse creates a new response with one URLMeasurement entry @@ -82,7 +82,7 @@ func newDNSFailedResponse(err error, URL string) *ControlResponse { Failure: newfailure(err), }, } - resp.URLMeasurements = append(resp.URLMeasurements, m) + resp.URLs = append(resp.URLs, m) return resp } diff --git a/internal/cmd/oohelperd/internal/websteps/measure_test.go b/internal/cmd/oohelperd/internal/websteps/measure_test.go index b9f61ea..9c0dc5f 100644 --- a/internal/cmd/oohelperd/internal/websteps/measure_test.go +++ b/internal/cmd/oohelperd/internal/websteps/measure_test.go @@ -11,7 +11,7 @@ import ( func TestMeasureSuccess(t *testing.T) { req := &CtrlRequest{ - HTTPRequest: "https://example.com", + URL: "https://example.com", } resp, err := Measure(context.Background(), req, &Config{}) if err != nil { @@ -48,7 +48,7 @@ var ErrExpectedGenerate error = errors.New("expected error generator") func TestMeasureInitialChecksFail(t *testing.T) { req := &CtrlRequest{ - HTTPRequest: "https://example.com", + URL: "https://example.com", } resp, err := Measure(context.Background(), req, &Config{checker: &MockChecker{err: ErrExpectedCheck}}) if err == nil { @@ -64,7 +64,7 @@ func TestMeasureInitialChecksFail(t *testing.T) { func TestMeasureInitialChecksFailWithNXDOMAIN(t *testing.T) { req := &CtrlRequest{ - HTTPRequest: "https://example.com", + URL: "https://example.com", } resp, err := Measure(context.Background(), req, &Config{checker: &MockChecker{err: ErrNoSuchHost}}) if err != nil { @@ -73,20 +73,20 @@ func TestMeasureInitialChecksFailWithNXDOMAIN(t *testing.T) { if resp == nil { t.Fatal("resp should not be nil") } - if len(resp.URLMeasurements) != 1 { + if len(resp.URLs) != 1 { t.Fatal("unexpected number of measurements") } - if resp.URLMeasurements[0].DNS == nil { + if resp.URLs[0].DNS == nil { t.Fatal("DNS entry should not be nil") } - if *resp.URLMeasurements[0].DNS.Failure != errorsx.FailureDNSNXDOMAINError { + if *resp.URLs[0].DNS.Failure != errorsx.FailureDNSNXDOMAINError { t.Fatal("unexpected failure") } } func TestMeasureExploreFails(t *testing.T) { req := &CtrlRequest{ - HTTPRequest: "https://example.com", + URL: "https://example.com", } resp, err := Measure(context.Background(), req, &Config{explorer: &MockExplorer{}}) if err == nil { @@ -102,7 +102,7 @@ func TestMeasureExploreFails(t *testing.T) { func TestMeasureGenerateFails(t *testing.T) { req := &CtrlRequest{ - HTTPRequest: "https://example.com", + URL: "https://example.com", } resp, err := Measure(context.Background(), req, &Config{generator: &MockGenerator{}}) if err == nil { diff --git a/internal/cmd/oohelperd/internal/websteps/model.go b/internal/cmd/oohelperd/internal/websteps/model.go index c0f14bd..fcaae26 100644 --- a/internal/cmd/oohelperd/internal/websteps/model.go +++ b/internal/cmd/oohelperd/internal/websteps/model.go @@ -13,5 +13,5 @@ type ( TLSHandshakeMeasurement = websteps.TLSHandshakeMeasurement HTTPRequestMeasurement = websteps.HTTPRequestMeasurement HTTPResponseMeasurement = websteps.HTTPResponseMeasurement - RoundTrip = websteps.RoundTrip + RoundTrip = websteps.RoundTripInfo ) diff --git a/internal/engine/experiment/websteps/control.go b/internal/engine/experiment/websteps/control.go index ab9386a..4db7d7b 100644 --- a/internal/engine/experiment/websteps/control.go +++ b/internal/engine/experiment/websteps/control.go @@ -8,22 +8,10 @@ import ( "github.com/ooni/probe-cli/v3/internal/errorsx" ) -// CtrlRequest is the request sent by the probe -type CtrlRequest struct { - HTTPRequest string `json:"url"` - HTTPRequestHeaders map[string][]string `json:"headers"` - Addrs []string `json:"addrs"` -} - -// ControlResponse is the response from the control service. -type ControlResponse struct { - URLMeasurements []*URLMeasurement `json:"urls"` -} - // Control performs the control request and returns the response. func Control( ctx context.Context, sess model.ExperimentSession, - thAddr string, creq CtrlRequest) (out ControlResponse, err error) { + thAddr string, creq CtrlRequest) (out CtrlResponse, err error) { clnt := httpx.Client{ BaseURL: thAddr, HTTPClient: sess.DefaultHTTPClient(), diff --git a/internal/engine/experiment/websteps/model.go b/internal/engine/experiment/websteps/model.go index 0fb7dc3..a276760 100644 --- a/internal/engine/experiment/websteps/model.go +++ b/internal/engine/experiment/websteps/model.go @@ -2,70 +2,104 @@ package websteps import "net/http" -// RoundTrip describes a specific round trip. -type RoundTrip struct { - // proto is the protocol used, it can be "h2", "http/1.1", "h3", "h3-29" +// Websteps test helper spec messages: + +// CtrlRequest is the request sent by the probe to the test helper. +type CtrlRequest struct { + // URL is the mandatory URL to measure. + URL string `json:"url"` + + // Headers contains optional headers. + Headers map[string][]string `json:"headers"` + + // Addrs contains the optional IP addresses resolved by the + // probe for the domain inside URL. + Addrs []string `json:"addrs"` +} + +// CtrlResponse is the response from the test helper. +type CtrlResponse struct { + // URLs contains the URLs we should measure. These URLs + // derive from CtrlRequest.URL. + URLs []*URLMeasurement `json:"urls"` +} + +// URLMeasurement contains all the URLs measured by the test helper. +type URLMeasurement struct { + // URL is the URL to which this measurement refers. + URL string `json:"url"` + + // DNS contains the domain names resolved by the test helper. + DNS *DNSMeasurement `json:"dns"` + + // Endpoints contains endpoint measurements. + Endpoints []*EndpointMeasurement `json:"endpoints"` + + // RoundTrip is the related round trip. This field MUST NOT be + // exported as JSON, since it's only used internally by the test + // helper and it's completely ignored by the probe. + RoundTrip *RoundTripInfo `json:"-"` +} + +// RoundTripInfo contains info on a specific round trip. This data +// structure is not part of the test helper protocol. We use it +// _inside_ the test helper to describe the discovery phase where +// we gather all the URLs that can derive from a given URL. +type RoundTripInfo struct { + // Proto is the protocol used, it can be "h2", "http/1.1", "h3", "h3-29". Proto string - // Request is the original HTTP request. The headers - // also include cookies. + // Request is the original HTTP request. Headers also include cookies. Request *http.Request // Response is the HTTP response. Response *http.Response - // sortIndex is an internal field using for sorting. + // SortIndex is the index using for sorting round trips. SortIndex int } -// URLMeasurement is a measurement of a given URL that -// includes connectivity measurement for each endpoint -// implied by the given URL. -type URLMeasurement struct { - // URL is the URL we're using - URL string `json:"url"` - - // DNS contains the domain names resolved by the helper. - DNS *DNSMeasurement `json:"dns"` - - // RoundTrip is the related round trip. - RoundTrip *RoundTrip `json:"-"` - - // Endpoints contains endpoint measurements. - Endpoints []*EndpointMeasurement `json:"endpoints"` -} - // DNSMeasurement is a DNS measurement. type DNSMeasurement struct { // Domain is the domain we wanted to resolve. Domain string `json:"domain"` - // Addrs contains the resolved addresses. - Addrs []string `json:"addrs"` - // Failure is the error that occurred. Failure *string `json:"failure"` + + // Addrs contains the resolved addresses. + Addrs []string `json:"addrs"` } -// HTTPSEndpointMeasurement is the measurement of requesting a specific endpoint via HTTPS. +// EndpointMeasurement is an HTTP measurement where we are using +// a specific TCP/TLS/QUIC endpoint to get the URL. +// +// The specification describes this data structure as the sum of +// three distinct types: HTTPEndpointMeasurement for "http", +// HTTPSEndpointMeasurement for "https", and H3EndpointMeasurement +// for "h3". We don't have sum types here, therefore we use the +// Protocol field to indicate which fields are meaningful. type EndpointMeasurement struct { // Endpoint is the endpoint we're measuring. Endpoint string `json:"endpoint"` - // Protocol is the used protocol. It can be "http", "https", "h3", "h3-29" or other supported QUIC protocols + // Protocol is one of "http", "https", "h3", and "h3-29". Protocol string `json:"protocol"` - // TCPConnectMeasurement is the related TCP connect measurement, if applicable (nil for h3 requests) - TCPConnectMeasurement *TCPConnectMeasurement `json:"tcp_connect"` + // TCPConnect is the TCP connect measurement. This field + // is only meaningful when protocol is "http" or "https." + TCPConnect *TCPConnectMeasurement `json:"tcp_connect"` - // QUICHandshakeMeasurement is the related QUIC(TLS 1.3) handshake measurement, if applicable (nil for http, https requests) - QUICHandshakeMeasurement *TLSHandshakeMeasurement `json:"quic_handshake"` + // QUICHandshake is the QUIC handshake measurement. This field + // is only meaningful when the protocol is one of "h3" and "h3-29". + QUICHandshake *QUICHandshakeMeasurement `json:"quic_handshake"` - // TLSHandshakeMeasurement is the related TLS handshake measurement, if applicable (nil for http, h3 requests) - TLSHandshakeMeasurement *TLSHandshakeMeasurement `json:"tls_handshake"` + // TLSHandshake is the TLS handshake measurement. This field + // is only meaningful when the protocol is "https". + TLSHandshake *TLSHandshakeMeasurement `json:"tls_handshake"` - // HTTPRoundTripMeasurement is the related HTTP GET measurement. - HTTPRoundTripMeasurement *HTTPRoundTripMeasurement `json:"http_round_trip"` + // HTTPRoundTrip is the related HTTP GET measurement. + HTTPRoundTrip *HTTPRoundTripMeasurement `json:"http_round_trip"` } // TCPConnectMeasurement is a TCP connect measurement. @@ -80,23 +114,42 @@ type TLSHandshakeMeasurement struct { Failure *string `json:"failure"` } -// HTTPRoundTripMeasurement contains a measured HTTP request and the corresponding response. +// QUICHandshakeMeasurement is a QUIC handshake measurement. +type QUICHandshakeMeasurement = TLSHandshakeMeasurement + +// HTTPRoundTripMeasurement contains a measured HTTP request and +// the corresponding response. type HTTPRoundTripMeasurement struct { - Request *HTTPRequestMeasurement `json:"request"` + // Request contains request data. + Request *HTTPRequestMeasurement `json:"request"` + + // Response contains response data. Response *HTTPResponseMeasurement `json:"response"` } -// HTTPRequestMeasurement contains the headers of the measured HTTP Get request. +// HTTPRequestMeasurement contains request data. type HTTPRequestMeasurement struct { + // Method is the request method. + Method string `json:"method"` + + // URL is the request URL. + URL string `json:"url"` + + // Headers contains request headers. Headers http.Header `json:"headers"` - Method string `json:"method"` - URL string `json:"url"` } -// HTTPResponseMeasurement contains the response of the measured HTTP Get request. +// HTTPResponseMeasurement contains response data. type HTTPResponseMeasurement struct { - BodyLength int64 `json:"body_length"` - Failure *string `json:"failure"` - Headers http.Header `json:"headers"` - StatusCode int64 `json:"status_code"` + // BodyLength contains the body length in bytes. + BodyLength int64 `json:"body_length"` + + // Failure is the error that occurred. + Failure *string `json:"failure"` + + // Headers contains response headers. + Headers http.Header `json:"headers"` + + // StatusCode is the response status code. + StatusCode int64 `json:"status_code"` } diff --git a/internal/engine/experiment/websteps/websteps.go b/internal/engine/experiment/websteps/websteps.go index 093223e..5622ac0 100644 --- a/internal/engine/experiment/websteps/websteps.go +++ b/internal/engine/experiment/websteps/websteps.go @@ -113,19 +113,19 @@ func (m Measurer) Run( } // 4. Query the testhelper resp, err := Control(ctx, sess, testhelper.Address, CtrlRequest{ - HTTPRequest: URL.String(), - HTTPRequestHeaders: map[string][]string{ + URL: URL.String(), + Headers: map[string][]string{ "Accept": {httpheader.Accept()}, "Accept-Language": {httpheader.AcceptLanguage()}, "User-Agent": {httpheader.UserAgent()}, }, Addrs: endpoints, }) - if err != nil || resp.URLMeasurements == nil { + if err != nil || resp.URLs == nil { return errors.New("no control response") } // 5. Go over the Control URL measurements and reproduce them without following redirects, one by one. - for _, controlURLMeasurement := range resp.URLMeasurements { + for _, controlURLMeasurement := range resp.URLs { urlMeasurement := &URLMeasurement{ URL: controlURLMeasurement.URL, Endpoints: []*EndpointMeasurement{}, @@ -145,7 +145,7 @@ func (m Measurer) Run( } // the testhelper tells us which endpoints to measure for _, controlEndpoint := range controlURLMeasurement.Endpoints { - rt := controlEndpoint.HTTPRoundTripMeasurement + rt := controlEndpoint.HTTPRoundTrip if rt == nil || rt.Request == nil { continue } @@ -176,7 +176,7 @@ func (m *Measurer) measureEndpointHTTP(ctx context.Context, URL *url.URL, endpoi } // TCP connect step conn, err := TCPDo(ctx, TCPConfig{Endpoint: endpoint}) - endpointMeasurement.TCPConnectMeasurement = &TCPConnectMeasurement{ + endpointMeasurement.TCPConnect = &TCPConnectMeasurement{ Failure: archival.NewFailure(err), } if err != nil { @@ -186,7 +186,7 @@ func (m *Measurer) measureEndpointHTTP(ctx context.Context, URL *url.URL, endpoi // HTTP roundtrip step request := NewRequest(ctx, URL, headers) - endpointMeasurement.HTTPRoundTripMeasurement = &HTTPRoundTripMeasurement{ + endpointMeasurement.HTTPRoundTrip = &HTTPRoundTripMeasurement{ Request: &HTTPRequestMeasurement{ Headers: request.Header, Method: "GET", @@ -197,13 +197,13 @@ func (m *Measurer) measureEndpointHTTP(ctx context.Context, URL *url.URL, endpoi resp, body, err := HTTPDo(request, transport) if err != nil { // failed Response - endpointMeasurement.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + endpointMeasurement.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ Failure: archival.NewFailure(err), } return endpointMeasurement } // successful Response - endpointMeasurement.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + endpointMeasurement.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ BodyLength: int64(len(body)), Failure: nil, Headers: resp.Header, @@ -219,7 +219,7 @@ func (m *Measurer) measureEndpointHTTPS(ctx context.Context, URL *url.URL, endpo } // TCP connect step conn, err := TCPDo(ctx, TCPConfig{Endpoint: endpoint}) - endpointMeasurement.TCPConnectMeasurement = &TCPConnectMeasurement{ + endpointMeasurement.TCPConnect = &TCPConnectMeasurement{ Failure: archival.NewFailure(err), } if err != nil { @@ -229,7 +229,7 @@ func (m *Measurer) measureEndpointHTTPS(ctx context.Context, URL *url.URL, endpo // TLS handshake step tlsconn, err := TLSDo(conn, URL.Hostname()) - endpointMeasurement.TLSHandshakeMeasurement = &TLSHandshakeMeasurement{ + endpointMeasurement.TLSHandshake = &TLSHandshakeMeasurement{ Failure: archival.NewFailure(err), } if err != nil { @@ -239,7 +239,7 @@ func (m *Measurer) measureEndpointHTTPS(ctx context.Context, URL *url.URL, endpo // HTTP roundtrip step request := NewRequest(ctx, URL, headers) - endpointMeasurement.HTTPRoundTripMeasurement = &HTTPRoundTripMeasurement{ + endpointMeasurement.HTTPRoundTrip = &HTTPRoundTripMeasurement{ Request: &HTTPRequestMeasurement{ Headers: request.Header, Method: "GET", @@ -250,13 +250,13 @@ func (m *Measurer) measureEndpointHTTPS(ctx context.Context, URL *url.URL, endpo resp, body, err := HTTPDo(request, transport) if err != nil { // failed Response - endpointMeasurement.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + endpointMeasurement.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ Failure: archival.NewFailure(err), } return endpointMeasurement } // successful Response - endpointMeasurement.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + endpointMeasurement.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ BodyLength: int64(len(body)), Failure: nil, Headers: resp.Header, @@ -279,7 +279,7 @@ func (m *Measurer) measureEndpointH3(ctx context.Context, URL *url.URL, endpoint Endpoint: endpoint, TLSConf: tlsConf, }) - endpointMeasurement.QUICHandshakeMeasurement = &TLSHandshakeMeasurement{ + endpointMeasurement.QUICHandshake = &TLSHandshakeMeasurement{ Failure: archival.NewFailure(err), } if err != nil { @@ -287,7 +287,7 @@ func (m *Measurer) measureEndpointH3(ctx context.Context, URL *url.URL, endpoint } // HTTP roundtrip step request := NewRequest(ctx, URL, headers) - endpointMeasurement.HTTPRoundTripMeasurement = &HTTPRoundTripMeasurement{ + endpointMeasurement.HTTPRoundTrip = &HTTPRoundTripMeasurement{ Request: &HTTPRequestMeasurement{ Headers: request.Header, Method: "GET", @@ -298,13 +298,13 @@ func (m *Measurer) measureEndpointH3(ctx context.Context, URL *url.URL, endpoint resp, body, err := HTTPDo(request, transport) if err != nil { // failed Response - endpointMeasurement.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + endpointMeasurement.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ Failure: archival.NewFailure(err), } return endpointMeasurement } // successful Response - endpointMeasurement.HTTPRoundTripMeasurement.Response = &HTTPResponseMeasurement{ + endpointMeasurement.HTTPRoundTrip.Response = &HTTPResponseMeasurement{ BodyLength: int64(len(body)), Failure: nil, Headers: resp.Header,