ooni-probe-cli/internal/oonirun/v1.go
Simone Basso ebb78c2848
feat(oonirun): implement OONIRun v1 (#843)
This diff adds support for running OONIRun v1 links.

Run with `miniooni` using:

```
./miniooni -i LINK oonirun
```

Part of https://github.com/ooni/probe/issues/2184
2022-07-08 15:17:52 +02:00

91 lines
2.3 KiB
Go

package oonirun
//
// OONI Run v1 implementation
//
import (
"context"
"encoding/json"
"errors"
"net/url"
)
var (
// ErrInvalidV1URLScheme indicates a v1 OONI Run URL has an invalid scheme.
ErrInvalidV1URLScheme = errors.New("oonirun: invalid v1 URL scheme")
// ErrInvalidV1URLHost indicates a v1 OONI Run URL has an invalid host.
ErrInvalidV1URLHost = errors.New("oonirun: invalid v1 URL host")
// ErrInvalidV1URLPath indicates a v1 OONI Run URL has an invalid path.
ErrInvalidV1URLPath = errors.New("oonirun: invalid v1 URL path")
// ErrInvalidV1URLQueryArgument indicates a v1 OONI Run URL query argument is invalid.
ErrInvalidV1URLQueryArgument = errors.New("oonirun: invalid v1 URL query argument")
)
// v1Arguments contains arguments for a v1 OONI Run URL. These arguments are
// always encoded inside of the "ta" field, which is optional.
type v1Arguments struct {
URLs []string `json:"urls"`
}
// v1Measure performs a measurement using the given v1 OONI Run URL.
func v1Measure(ctx context.Context, config *LinkConfig, URL string) error {
config.Session.Logger().Infof("oonirun/v1: running %s", URL)
pu, err := url.Parse(URL)
if err != nil {
return err
}
switch pu.Scheme {
case "https":
if pu.Host != "run.ooni.io" {
return ErrInvalidV1URLHost
}
if pu.Path != "/nettest" {
return ErrInvalidV1URLPath
}
case "ooni":
if pu.Host != "nettest" {
return ErrInvalidV1URLHost
}
if pu.Path != "" {
return ErrInvalidV1URLPath
}
default:
return ErrInvalidV1URLScheme
}
name := pu.Query().Get("tn")
if name == "" {
return ErrInvalidV1URLQueryArgument
}
var inputs []string
if ra := pu.Query().Get("ta"); ra != "" {
pa, err := url.QueryUnescape(ra)
if err != nil {
return err
}
var arguments v1Arguments
if err := json.Unmarshal([]byte(pa), &arguments); err != nil {
return err
}
inputs = arguments.URLs
}
// TODO(bassosimone): reject mv < 1.2.0
exp := &Experiment{
Annotations: config.Annotations,
ExtraOptions: nil, // no way to specify with v1 URLs
Inputs: inputs,
InputFilePaths: nil,
MaxRuntime: config.MaxRuntime,
Name: name,
NoCollector: config.NoCollector,
NoJSON: config.NoJSON,
Random: config.Random,
ReportFile: config.ReportFile,
Session: config.Session,
}
return exp.Run(ctx)
}