cli: new testhelper and the websteps experiment prototype (#432)

This is the extension of https://github.com/ooni/probe-cli/pull/431, and my final deliverable for GSoC 2021.

The diff introduces:

1) The new `testhelper` which supports testing multiple IP endpoints per domain and introduces HTTP/3 control measurements. The specification of the `testhelper` can be found at https://github.com/ooni/spec/pull/219. The `testhelper` algorithm consists of three main steps:

   * `InitialChecks` verifies that the input URL can be parsed, has an expected scheme, and contains a valid domain name.

   * `Explore` enumerates all the URLs that it discovers by redirection from the original URL, or by detecting h3 support at the target host.

   * `Generate` performs a step-by-step measurement of each discovered URL.

2) A prototype of the corresponding new experiment `websteps` which uses the control measurement of the `testhelper` to know which URLs to measure, and what to expect. The prototype does not yet have:

   * unit and integration tests,

   * an analysis tool to compare the control and the probe measurement.

This PR is my final deliverable as it is the outcome of the trials, considerations and efforts of my GSoC weeks at OONI. 
It fully integrates HTTP/3 (QUIC) support which has been only used in the `urlgetter` experiment until now.

Related issues: https://github.com/ooni/probe/issues/1729 and https://github.com/ooni/probe/issues/1733.
This commit is contained in:
kelmenhorst 2021-08-17 10:29:06 +02:00 • committed by GitHub
commit c31591f298
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 2736 additions and 8 deletions

View file

@ -0,0 +1,67 @@
// Package nwcth implements the new web connectivity test helper.
//
// See https://github.com/ooni/spec/blob/master/backends/th-007-nwcth.md
package nwcth
import (
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/ooni/probe-cli/v3/internal/engine/netx/archival"
"github.com/ooni/probe-cli/v3/internal/iox"
"github.com/ooni/probe-cli/v3/internal/runtimex"
"github.com/ooni/probe-cli/v3/internal/version"
)
// newfailure is a convenience shortcut to save typing
var newfailure = archival.NewFailure
// maxAcceptableBody is _at the same time_ the maximum acceptable body for incoming
// API requests and the maximum acceptable body when fetching arbitrary URLs. See
// https://github.com/ooni/probe/issues/1727 for statistics regarding the test lists
// including the empirical CDF of the body size for test lists URLs.
const maxAcceptableBody = 1 << 24
// Handler implements the Web Connectivity test helper HTTP API.
type Handler struct {
Config *Config
}
// ServeHTTP implements http.Handler.ServeHTTP.
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Server", fmt.Sprintf(
"oohelperd/%s ooniprobe-engine/%s", version.Version, version.Version,
))
if req.Method != "POST" {
w.WriteHeader(400)
return
}
reader := &io.LimitedReader{R: req.Body, N: maxAcceptableBody}
data, err := iox.ReadAllContext(req.Context(), reader)
if err != nil {
w.WriteHeader(400)
return
}
var creq CtrlRequest
if err := json.Unmarshal(data, &creq); err != nil {
w.WriteHeader(400)
return
}
cresp, err := Measure(req.Context(), &creq, h.Config)
if err != nil {
if err == ErrInternalServer {
w.WriteHeader(500)
return
}
w.WriteHeader(400)
return
}
// We assume that the following call cannot fail because it's a
// clearly serializable data structure.
data, err = json.Marshal(cresp)
runtimex.PanicOnError(err, "json.Marshal failed")
w.Header().Add("Content-Type", "application/json")
w.Write(data)
}