-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This diff adds support for running OONIRun v1 links. Run with `miniooni` using: ``` ./miniooni -i LINK oonirun ``` Part of ooni/probe#2184
- Loading branch information
1 parent
0b4a491
commit 1b63a87
Showing
4 changed files
with
259 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package oonirun | ||
|
||
// | ||
// OONI Run v1 and v2 links | ||
// | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
|
||
"github.com/ooni/probe-cli/v3/internal/model" | ||
) | ||
|
||
// LinkConfig contains config for an OONI Run link. You MUST fill all the fields that | ||
// are marked as MANDATORY, or the LinkConfig would cause crashes. | ||
type LinkConfig struct { | ||
// AcceptChanges is OPTIONAL and tells this library that the user is | ||
// okay with running a new or modified OONI Run link without previously | ||
// reviewing what it contains or what has changed. | ||
AcceptChanges bool | ||
|
||
// Annotations contains OPTIONAL Annotations for the experiment. | ||
Annotations map[string]string | ||
|
||
// KVStore is the MANDATORY key-value store to use to keep track of | ||
// OONI Run links and know when they are new or modified. | ||
KVStore model.KeyValueStore | ||
|
||
// MaxRuntime is the OPTIONAL maximum runtime in seconds. | ||
MaxRuntime int64 | ||
|
||
// NoCollector OPTIONALLY indicates we should not be using any collector. | ||
NoCollector bool | ||
|
||
// NoJSON OPTIONALLY indicates we don't want to save measurements to a JSON file. | ||
NoJSON bool | ||
|
||
// Random OPTIONALLY indicates we should randomize inputs. | ||
Random bool | ||
|
||
// ReportFile is the MANDATORY file in which to save reports, which is only | ||
// used when noJSON is set to false. | ||
ReportFile string | ||
|
||
// Session is the MANDATORY Session to use. | ||
Session Session | ||
} | ||
|
||
// LinkRunner knows how to run an OONI Run v1 or v2 link. | ||
type LinkRunner interface { | ||
Run(ctx context.Context) error | ||
} | ||
|
||
// linkRunner implements LinkRunner. | ||
type linkRunner struct { | ||
config *LinkConfig | ||
f func(ctx context.Context, config *LinkConfig, URL string) error | ||
url string | ||
} | ||
|
||
// Run implements LinkRunner.Rune. | ||
func (lr *linkRunner) Run(ctx context.Context) error { | ||
return lr.f(ctx, lr.config, lr.url) | ||
} | ||
|
||
// NewLinkRunner creates a suitable link runner for the current config | ||
// and the given URL, which is one of the following: | ||
// | ||
// 1. OONI Run v1 link with https scheme (e.g., https://run.ooni.io/nettest?...) | ||
// | ||
// 2. OONI Run v1 link with ooni scheme (e.g., ooni://nettest?...) | ||
// | ||
// 3. arbitrary URL of the OONI Run v2 descriptor. | ||
func NewLinkRunner(c *LinkConfig, URL string) LinkRunner { | ||
// TODO(bassosimone): add support for v2 deeplinks. | ||
out := &linkRunner{ | ||
config: c, | ||
f: nil, | ||
url: URL, | ||
} | ||
switch { | ||
case strings.HasPrefix(URL, "https://run.ooni.io/nettest"): | ||
out.f = v1Measure | ||
case strings.HasPrefix(URL, "ooni://nettest"): | ||
out.f = v1Measure | ||
default: | ||
// TODO(bassosimone): this panic will go away when we merge | ||
// the next patch which will implement v2. | ||
panic("unsupported OONI Run link") | ||
} | ||
return out | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
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 a 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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package oonirun | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/ooni/probe-cli/v3/internal/kvstore" | ||
) | ||
|
||
// TODO(bassosimone): it would be cool to write unit tests. However, to do that | ||
// we need to ~redesign the engine package for unit-testability. | ||
|
||
func TestOONIRunV1Link(t *testing.T) { | ||
ctx := context.Background() | ||
config := &LinkConfig{ | ||
AcceptChanges: false, | ||
Annotations: map[string]string{ | ||
"platform": "linux", | ||
}, | ||
KVStore: &kvstore.Memory{}, | ||
MaxRuntime: 0, | ||
NoCollector: true, | ||
NoJSON: true, | ||
Random: false, | ||
ReportFile: "", | ||
Session: newSession(ctx, t), | ||
} | ||
r := NewLinkRunner(config, "https://run.ooni.io/nettest?tn=example&mv=1.2.0") | ||
if err := r.Run(ctx); err != nil { | ||
t.Fatal(err) | ||
} | ||
r = NewLinkRunner(config, "ooni://nettest?tn=example&mv=1.2.0") | ||
if err := r.Run(ctx); err != nil { | ||
t.Fatal(err) | ||
} | ||
} |