feat: introduce the tcpping experiment (#696)
See https://github.com/ooni/probe/issues/2030 (reference issue) and https://github.com/ooni/spec/pull/235 (spec).
This commit is contained in:
		
							parent
							
								
									8010e9783a
								
							
						
					
					
						commit
						e983a5cffb
					
				@ -18,6 +18,7 @@ import (
 | 
				
			|||||||
	"github.com/ooni/probe-cli/v3/internal/engine/experiment/signal"
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/experiment/signal"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/engine/experiment/sniblocking"
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/experiment/sniblocking"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/engine/experiment/stunreachability"
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/experiment/stunreachability"
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/experiment/tcpping"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/engine/experiment/telegram"
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/experiment/telegram"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/engine/experiment/tlstool"
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/experiment/tlstool"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/engine/experiment/tor"
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/experiment/tor"
 | 
				
			||||||
@ -215,6 +216,18 @@ var experimentsByName = map[string]func(*Session) *ExperimentBuilder{
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"tcpping": func(session *Session) *ExperimentBuilder {
 | 
				
			||||||
 | 
							return &ExperimentBuilder{
 | 
				
			||||||
 | 
								build: func(config interface{}) *Experiment {
 | 
				
			||||||
 | 
									return NewExperiment(session, tcpping.NewExperimentMeasurer(
 | 
				
			||||||
 | 
										*config.(*tcpping.Config),
 | 
				
			||||||
 | 
									))
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								config:      &tcpping.Config{},
 | 
				
			||||||
 | 
								inputPolicy: InputStrictlyRequired,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"telegram": func(session *Session) *ExperimentBuilder {
 | 
						"telegram": func(session *Session) *ExperimentBuilder {
 | 
				
			||||||
		return &ExperimentBuilder{
 | 
							return &ExperimentBuilder{
 | 
				
			||||||
			build: func(config interface{}) *Experiment {
 | 
								build: func(config interface{}) *Experiment {
 | 
				
			||||||
 | 
				
			|||||||
@ -289,7 +289,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	Latency   float64 `json:"connect_latency"`
 | 
						Latency   float64 `json:"connect_latency"`
 | 
				
			||||||
 | 
				
			|||||||
@ -317,7 +317,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -87,7 +87,7 @@ func NewExperimentMeasurer(config Config, testName string) model.ExperimentMeasu
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -203,7 +203,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	DNSBlocking bool `json:"facebook_dns_blocking"`
 | 
						DNSBlocking bool `json:"facebook_dns_blocking"`
 | 
				
			||||||
 | 
				
			|||||||
@ -347,7 +347,7 @@ func (c Conn) Write(b []byte) (int, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -305,7 +305,7 @@ func RunMethod(ctx context.Context, config RunMethodConfig) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -82,7 +82,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -260,7 +260,7 @@ func failureFromError(err error) (failure *string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	Upload         float64 `json:"upload"`
 | 
						Upload         float64 `json:"upload"`
 | 
				
			||||||
 | 
				
			|||||||
@ -109,7 +109,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	BootstrapTime float64 `json:"bootstrap_time"`
 | 
						BootstrapTime float64 `json:"bootstrap_time"`
 | 
				
			||||||
 | 
				
			|||||||
@ -345,7 +345,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -327,7 +327,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	APIBlocked      bool              `json:"api_blocked"`
 | 
						APIBlocked      bool              `json:"api_blocked"`
 | 
				
			||||||
 | 
				
			|||||||
@ -172,7 +172,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	SignalBackendStatus  string  `json:"signal_backend_status"`
 | 
						SignalBackendStatus  string  `json:"signal_backend_status"`
 | 
				
			||||||
 | 
				
			|||||||
@ -294,7 +294,7 @@ func asString(failure *string) (result string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -177,7 +177,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										159
									
								
								internal/engine/experiment/tcpping/tcpping.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								internal/engine/experiment/tcpping/tcpping.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,159 @@
 | 
				
			|||||||
 | 
					// Package tcpping is the experimental tcpping experiment.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// See https://github.com/ooni/spec/blob/master/nettests/ts-032-tcpping.md.
 | 
				
			||||||
 | 
					package tcpping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/measurex"
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						testName    = "tcpping"
 | 
				
			||||||
 | 
						testVersion = "0.1.0"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Config contains the experiment configuration.
 | 
				
			||||||
 | 
					type Config struct {
 | 
				
			||||||
 | 
						// Delay is the delay between each repetition (in milliseconds).
 | 
				
			||||||
 | 
						Delay int64 `ooni:"number of milliseconds to wait before sending each ping"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Repetitions is the number of repetitions for each ping.
 | 
				
			||||||
 | 
						Repetitions int64 `ooni:"number of times to repeat the measurement"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) delay() time.Duration {
 | 
				
			||||||
 | 
						if c.Delay > 0 {
 | 
				
			||||||
 | 
							return time.Duration(c.Delay) * time.Millisecond
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return time.Second
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) repetitions() int64 {
 | 
				
			||||||
 | 
						if c.Repetitions > 0 {
 | 
				
			||||||
 | 
							return c.Repetitions
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 10
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestKeys contains the experiment results.
 | 
				
			||||||
 | 
					type TestKeys struct {
 | 
				
			||||||
 | 
						Pings []*SinglePing `json:"pings"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SinglePing contains the results of a single ping.
 | 
				
			||||||
 | 
					type SinglePing struct {
 | 
				
			||||||
 | 
						TCPConnect []*measurex.ArchivalTCPConnect `json:"tcp_connect"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Measurer performs the measurement.
 | 
				
			||||||
 | 
					type Measurer struct {
 | 
				
			||||||
 | 
						config Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ExperimentName implements ExperimentMeasurer.ExperiExperimentName.
 | 
				
			||||||
 | 
					func (m *Measurer) ExperimentName() string {
 | 
				
			||||||
 | 
						return testName
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ExperimentVersion implements ExperimentMeasurer.ExperimentVersion.
 | 
				
			||||||
 | 
					func (m *Measurer) ExperimentVersion() string {
 | 
				
			||||||
 | 
						return testVersion
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// errNoInputProvided indicates you didn't provide any input
 | 
				
			||||||
 | 
						errNoInputProvided = errors.New("not input provided")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// errInputIsNotAnURL indicates that input is not an URL
 | 
				
			||||||
 | 
						errInputIsNotAnURL = errors.New("input is not an URL")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// errInvalidScheme indicates that the scheme is invalid
 | 
				
			||||||
 | 
						errInvalidScheme = errors.New("scheme must be tcpconnect")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// errMissingPort indicates that there is no port.
 | 
				
			||||||
 | 
						errMissingPort = errors.New("the URL must include a port")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Run implements ExperimentMeasurer.Run.
 | 
				
			||||||
 | 
					func (m *Measurer) Run(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						sess model.ExperimentSession,
 | 
				
			||||||
 | 
						measurement *model.Measurement,
 | 
				
			||||||
 | 
						callbacks model.ExperimentCallbacks,
 | 
				
			||||||
 | 
					) error {
 | 
				
			||||||
 | 
						if measurement.Input == "" {
 | 
				
			||||||
 | 
							return errNoInputProvided
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						parsed, err := url.Parse(string(measurement.Input))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("%w: %s", errInputIsNotAnURL, err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if parsed.Scheme != "tcpconnect" {
 | 
				
			||||||
 | 
							return errInvalidScheme
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if parsed.Port() == "" {
 | 
				
			||||||
 | 
							return errMissingPort
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tk := new(TestKeys)
 | 
				
			||||||
 | 
						measurement.TestKeys = tk
 | 
				
			||||||
 | 
						out := make(chan *measurex.EndpointMeasurement)
 | 
				
			||||||
 | 
						mxmx := measurex.NewMeasurerWithDefaultSettings()
 | 
				
			||||||
 | 
						go m.tcpPingLoop(ctx, mxmx, parsed.Host, out)
 | 
				
			||||||
 | 
						for len(tk.Pings) < int(m.config.repetitions()) {
 | 
				
			||||||
 | 
							meas := <-out
 | 
				
			||||||
 | 
							tk.Pings = append(tk.Pings, &SinglePing{
 | 
				
			||||||
 | 
								TCPConnect: measurex.NewArchivalTCPConnectList(meas.Connect),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil // return nil so we always submit the measurement
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tcpPingLoop sends all the ping requests and emits the results onto the out channel.
 | 
				
			||||||
 | 
					func (m *Measurer) tcpPingLoop(ctx context.Context, mxmx *measurex.Measurer,
 | 
				
			||||||
 | 
						address string, out chan<- *measurex.EndpointMeasurement) {
 | 
				
			||||||
 | 
						ticker := time.NewTicker(m.config.delay())
 | 
				
			||||||
 | 
						defer ticker.Stop()
 | 
				
			||||||
 | 
						for i := int64(0); i < m.config.repetitions(); i++ {
 | 
				
			||||||
 | 
							go m.tcpPingAsync(ctx, mxmx, address, out)
 | 
				
			||||||
 | 
							<-ticker.C
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tcpPingAsync performs a TCP ping and emits the result onto the out channel.
 | 
				
			||||||
 | 
					func (m *Measurer) tcpPingAsync(ctx context.Context, mxmx *measurex.Measurer,
 | 
				
			||||||
 | 
						address string, out chan<- *measurex.EndpointMeasurement) {
 | 
				
			||||||
 | 
						out <- m.tcpConnect(ctx, mxmx, address)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tcpConnect performs a TCP connect and returns the result to the caller.
 | 
				
			||||||
 | 
					func (m *Measurer) tcpConnect(ctx context.Context, mxmx *measurex.Measurer,
 | 
				
			||||||
 | 
						address string) *measurex.EndpointMeasurement {
 | 
				
			||||||
 | 
						ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
 | 
				
			||||||
 | 
						defer cancel()
 | 
				
			||||||
 | 
						return mxmx.TCPConnect(ctx, address)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewExperimentMeasurer creates a new ExperimentMeasurer.
 | 
				
			||||||
 | 
					func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			||||||
 | 
						return &Measurer{config: config}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
 | 
					type SummaryKeys struct {
 | 
				
			||||||
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetSummaryKeys implements model.ExperimentMeasurer.GetSummaryKeys.
 | 
				
			||||||
 | 
					func (m Measurer) GetSummaryKeys(measurement *model.Measurement) (interface{}, error) {
 | 
				
			||||||
 | 
						return SummaryKeys{IsAnomaly: false}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										111
									
								
								internal/engine/experiment/tcpping/tcpping_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								internal/engine/experiment/tcpping/tcpping_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					package tcpping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/mockable"
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConfig_repetitions(t *testing.T) {
 | 
				
			||||||
 | 
						c := Config{}
 | 
				
			||||||
 | 
						if c.repetitions() != 10 {
 | 
				
			||||||
 | 
							t.Fatal("invalid default number of repetitions")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConfig_delay(t *testing.T) {
 | 
				
			||||||
 | 
						c := Config{}
 | 
				
			||||||
 | 
						if c.delay() != time.Second {
 | 
				
			||||||
 | 
							t.Fatal("invalid default delay")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMeasurer_run(t *testing.T) {
 | 
				
			||||||
 | 
						// expectedPings is the expected number of pings
 | 
				
			||||||
 | 
						const expectedPings = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// runHelper is an helper function to run this set of tests.
 | 
				
			||||||
 | 
						runHelper := func(input string) (*model.Measurement, model.ExperimentMeasurer, error) {
 | 
				
			||||||
 | 
							m := NewExperimentMeasurer(Config{
 | 
				
			||||||
 | 
								Delay:       1, // millisecond
 | 
				
			||||||
 | 
								Repetitions: expectedPings,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if m.ExperimentName() != "tcpping" {
 | 
				
			||||||
 | 
								t.Fatal("invalid experiment name")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if m.ExperimentVersion() != "0.1.0" {
 | 
				
			||||||
 | 
								t.Fatal("invalid experiment version")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx := context.Background()
 | 
				
			||||||
 | 
							meas := &model.Measurement{
 | 
				
			||||||
 | 
								Input: model.MeasurementTarget(input),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sess := &mockable.Session{}
 | 
				
			||||||
 | 
							callbacks := model.NewPrinterCallbacks(model.DiscardLogger)
 | 
				
			||||||
 | 
							err := m.Run(ctx, sess, meas, callbacks)
 | 
				
			||||||
 | 
							return meas, m, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("with empty input", func(t *testing.T) {
 | 
				
			||||||
 | 
							_, _, err := runHelper("")
 | 
				
			||||||
 | 
							if !errors.Is(err, errNoInputProvided) {
 | 
				
			||||||
 | 
								t.Fatal("unexpected error", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("with invalid URL", func(t *testing.T) {
 | 
				
			||||||
 | 
							_, _, err := runHelper("\t")
 | 
				
			||||||
 | 
							if !errors.Is(err, errInputIsNotAnURL) {
 | 
				
			||||||
 | 
								t.Fatal("unexpected error", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("with invalid scheme", func(t *testing.T) {
 | 
				
			||||||
 | 
							_, _, err := runHelper("https://8.8.8.8:443/")
 | 
				
			||||||
 | 
							if !errors.Is(err, errInvalidScheme) {
 | 
				
			||||||
 | 
								t.Fatal("unexpected error", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("with missing port", func(t *testing.T) {
 | 
				
			||||||
 | 
							_, _, err := runHelper("tcpconnect://8.8.8.8")
 | 
				
			||||||
 | 
							if !errors.Is(err, errMissingPort) {
 | 
				
			||||||
 | 
								t.Fatal("unexpected error", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("with local listener", func(t *testing.T) {
 | 
				
			||||||
 | 
							srvr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
								w.WriteHeader(200)
 | 
				
			||||||
 | 
							}))
 | 
				
			||||||
 | 
							defer srvr.Close()
 | 
				
			||||||
 | 
							URL, err := url.Parse(srvr.URL)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							URL.Scheme = "tcpconnect"
 | 
				
			||||||
 | 
							meas, m, err := runHelper(URL.String())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tk := meas.TestKeys.(*TestKeys)
 | 
				
			||||||
 | 
							if len(tk.Pings) != expectedPings {
 | 
				
			||||||
 | 
								t.Fatal("unexpected number of pings")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ask, err := m.GetSummaryKeys(meas)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal("cannot obtain summary")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							summary := ask.(SummaryKeys)
 | 
				
			||||||
 | 
							if summary.IsAnomaly {
 | 
				
			||||||
 | 
								t.Fatal("expected no anomaly")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -151,7 +151,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	HTTPBlocking bool `json:"telegram_http_blocking"`
 | 
						HTTPBlocking bool `json:"telegram_http_blocking"`
 | 
				
			||||||
 | 
				
			|||||||
@ -165,7 +165,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -384,7 +384,7 @@ func failureString(failure *string) (s string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	DirPortTotal            int64 `json:"dir_port_total"`
 | 
						DirPortTotal            int64 `json:"dir_port_total"`
 | 
				
			||||||
 | 
				
			|||||||
@ -269,7 +269,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -121,7 +121,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	IsAnomaly bool `json:"-"`
 | 
						IsAnomaly bool `json:"-"`
 | 
				
			||||||
 | 
				
			|||||||
@ -257,7 +257,7 @@ func ComputeTCPBlocking(measurement []archival.TCPConnectEntry,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	Accessible bool   `json:"accessible"`
 | 
						Accessible bool   `json:"accessible"`
 | 
				
			||||||
 | 
				
			|||||||
@ -330,7 +330,7 @@ func (m *Measurer) measureEndpointH3(ctx context.Context, URL *url.URL, endpoint
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	Accessible bool   `json:"accessible"`
 | 
						Accessible bool   `json:"accessible"`
 | 
				
			||||||
 | 
				
			|||||||
@ -208,7 +208,7 @@ func (mx *Measurer) Run(ctx context.Context, sess model.ExperimentSession,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	Accessible bool   `json:"accessible"`
 | 
						Accessible bool   `json:"accessible"`
 | 
				
			||||||
 | 
				
			|||||||
@ -210,7 +210,7 @@ func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SummaryKeys contains summary keys for this experiment.
 | 
					// SummaryKeys contains summary keys for this experiment.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Note that this structure is part of the ABI contract with probe-cli
 | 
					// Note that this structure is part of the ABI contract with ooniprobe
 | 
				
			||||||
// therefore we should be careful when changing it.
 | 
					// therefore we should be careful when changing it.
 | 
				
			||||||
type SummaryKeys struct {
 | 
					type SummaryKeys struct {
 | 
				
			||||||
	RegistrationServerBlocking bool `json:"registration_server_blocking"`
 | 
						RegistrationServerBlocking bool `json:"registration_server_blocking"`
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user