2021-02-02 12:05:47 +01:00
|
|
|
package hhfm_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2021-06-15 14:01:45 +02:00
|
|
|
"io"
|
2021-02-02 12:05:47 +01:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/apex/log"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/engine/experiment/hhfm"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/engine/experiment/urlgetter"
|
2021-06-22 00:12:03 +02:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/engine/mockable"
|
2022-01-03 13:53:23 +01:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
2021-09-28 12:42:01 +02:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
2022-06-02 00:50:55 +02:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/tracex"
|
2021-02-02 12:05:47 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestNewExperimentMeasurer(t *testing.T) {
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
if measurer.ExperimentName() != "http_header_field_manipulation" {
|
|
|
|
t.Fatal("unexpected name")
|
|
|
|
}
|
|
|
|
if measurer.ExperimentVersion() != "0.2.0" {
|
|
|
|
t.Fatal("unexpected version")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSuccess(t *testing.T) {
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
sess := &mockable.Session{
|
|
|
|
MockableLogger: log.Log,
|
2022-01-03 13:53:23 +01:00
|
|
|
MockableTestHelpers: map[string][]model.OOAPIService{
|
2021-02-02 12:05:47 +01:00
|
|
|
"http-return-json-headers": {{
|
|
|
|
Address: "http://37.218.241.94:80",
|
|
|
|
Type: "legacy",
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
callbacks := model.NewPrinterCallbacks(log.Log)
|
|
|
|
err := measurer.Run(ctx, sess, measurement, callbacks)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
tk := measurement.TestKeys.(*hhfm.TestKeys)
|
|
|
|
if tk.Agent != "agent" {
|
|
|
|
t.Fatal("invalid Agent")
|
|
|
|
}
|
|
|
|
if tk.Failure != nil {
|
2021-06-09 09:42:31 +02:00
|
|
|
t.Fatal("invalid Failure", *tk.Failure)
|
2021-02-02 12:05:47 +01:00
|
|
|
}
|
|
|
|
if len(tk.Requests) != 1 {
|
|
|
|
t.Fatal("invalid Requests")
|
|
|
|
}
|
|
|
|
request := tk.Requests[0]
|
|
|
|
if request.Failure != nil {
|
|
|
|
t.Fatal("invalid Requests[0].Failure")
|
|
|
|
}
|
|
|
|
if request.Request.Body.Value != "" {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Body.Value")
|
|
|
|
}
|
|
|
|
if request.Request.BodyIsTruncated != false {
|
|
|
|
t.Fatal("invalid Requests[0].Request.BodyIsTruncated")
|
|
|
|
}
|
|
|
|
if len(request.Request.HeadersList) != 6 {
|
|
|
|
t.Fatal("invalid Requests[0].Request.HeadersList length")
|
|
|
|
}
|
|
|
|
if len(request.Request.Headers) != 6 {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Headers length")
|
|
|
|
}
|
|
|
|
if strings.ToUpper(request.Request.Method) != "GET" {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Method")
|
|
|
|
}
|
|
|
|
if request.Request.Tor.ExitIP != nil {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Tor.ExitIP")
|
|
|
|
}
|
|
|
|
if request.Request.Tor.ExitName != nil {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Tor.ExitName")
|
|
|
|
}
|
|
|
|
if request.Request.Tor.IsTor != false {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Tor.IsTor")
|
|
|
|
}
|
|
|
|
ths, ok := sess.GetTestHelpersByName("http-return-json-headers")
|
|
|
|
if !ok || len(ths) < 1 || ths[0].Type != "legacy" {
|
|
|
|
t.Fatal("cannot get the test helper")
|
|
|
|
}
|
|
|
|
if request.Request.URL != ths[0].Address {
|
|
|
|
t.Fatal("invalid Requests[0].Request.URL")
|
|
|
|
}
|
|
|
|
if len(request.Response.Body.Value) < 1 {
|
|
|
|
t.Fatal("invalid Requests[0].Response.Body.Value length")
|
|
|
|
}
|
|
|
|
if request.Response.BodyIsTruncated != false {
|
|
|
|
t.Fatal("invalid Requests[0].Response.BodyIsTruncated")
|
|
|
|
}
|
|
|
|
if request.Response.Code != 200 {
|
|
|
|
t.Fatal("invalid Requests[0].Code")
|
|
|
|
}
|
|
|
|
if len(request.Response.HeadersList) != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].HeadersList length")
|
|
|
|
}
|
|
|
|
if len(request.Response.Headers) != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].Headers length")
|
|
|
|
}
|
|
|
|
if request.T != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].T")
|
|
|
|
}
|
|
|
|
if tk.SOCKSProxy != nil {
|
|
|
|
t.Fatal("invalid SOCKSProxy")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldName != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldName")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldNumber != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldNumber")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldValue != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldValue")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderNameCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameCapitalization")
|
|
|
|
}
|
|
|
|
if len(tk.Tampering.HeaderNameDiff) != 0 {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameDiff")
|
|
|
|
}
|
|
|
|
if tk.Tampering.RequestLineCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.RequestLineCapitalization")
|
|
|
|
}
|
|
|
|
if tk.Tampering.Total != false {
|
|
|
|
t.Fatal("invalid Tampering.Total")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCancelledContext(t *testing.T) {
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
cancel()
|
|
|
|
sess := &mockable.Session{
|
|
|
|
MockableLogger: log.Log,
|
2022-01-03 13:53:23 +01:00
|
|
|
MockableTestHelpers: map[string][]model.OOAPIService{
|
2021-02-02 12:05:47 +01:00
|
|
|
"http-return-json-headers": {{
|
|
|
|
Address: "http://37.218.241.94:80",
|
|
|
|
Type: "legacy",
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
callbacks := model.NewPrinterCallbacks(log.Log)
|
|
|
|
err := measurer.Run(ctx, sess, measurement, callbacks)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
tk := measurement.TestKeys.(*hhfm.TestKeys)
|
|
|
|
if tk.Agent != "agent" {
|
|
|
|
t.Fatal("invalid Agent")
|
|
|
|
}
|
2021-09-28 12:42:01 +02:00
|
|
|
if *tk.Failure != netxlite.FailureInterrupted {
|
2021-02-02 12:05:47 +01:00
|
|
|
t.Fatal("invalid Failure")
|
|
|
|
}
|
|
|
|
if len(tk.Requests) != 1 {
|
|
|
|
t.Fatal("invalid Requests")
|
|
|
|
}
|
|
|
|
request := tk.Requests[0]
|
2021-09-28 12:42:01 +02:00
|
|
|
if *request.Failure != netxlite.FailureInterrupted {
|
2021-02-02 12:05:47 +01:00
|
|
|
t.Fatal("invalid Requests[0].Failure")
|
|
|
|
}
|
|
|
|
if request.Request.Body.Value != "" {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Body.Value")
|
|
|
|
}
|
|
|
|
if request.Request.BodyIsTruncated != false {
|
|
|
|
t.Fatal("invalid Requests[0].Request.BodyIsTruncated")
|
|
|
|
}
|
|
|
|
if len(request.Request.HeadersList) != 6 {
|
|
|
|
t.Fatal("invalid Requests[0].Request.HeadersList length")
|
|
|
|
}
|
|
|
|
if len(request.Request.Headers) != 6 {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Headers length")
|
|
|
|
}
|
|
|
|
if strings.ToUpper(request.Request.Method) != "GET" {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Method")
|
|
|
|
}
|
|
|
|
if request.Request.Tor.ExitIP != nil {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Tor.ExitIP")
|
|
|
|
}
|
|
|
|
if request.Request.Tor.ExitName != nil {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Tor.ExitName")
|
|
|
|
}
|
|
|
|
if request.Request.Tor.IsTor != false {
|
|
|
|
t.Fatal("invalid Requests[0].Request.Tor.IsTor")
|
|
|
|
}
|
|
|
|
ths, ok := sess.GetTestHelpersByName("http-return-json-headers")
|
|
|
|
if !ok || len(ths) < 1 || ths[0].Type != "legacy" {
|
|
|
|
t.Fatal("cannot get the test helper")
|
|
|
|
}
|
|
|
|
if request.Request.URL != ths[0].Address {
|
|
|
|
t.Fatal("invalid Requests[0].Request.URL")
|
|
|
|
}
|
|
|
|
if len(request.Response.Body.Value) != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].Response.Body.Value length")
|
|
|
|
}
|
|
|
|
if request.Response.BodyIsTruncated != false {
|
|
|
|
t.Fatal("invalid Requests[0].Response.BodyIsTruncated")
|
|
|
|
}
|
|
|
|
if request.Response.Code != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].Code")
|
|
|
|
}
|
|
|
|
if len(request.Response.HeadersList) != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].HeadersList length")
|
|
|
|
}
|
|
|
|
if len(request.Response.Headers) != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].Headers length")
|
|
|
|
}
|
|
|
|
if request.T != 0 {
|
|
|
|
t.Fatal("invalid Requests[0].T")
|
|
|
|
}
|
|
|
|
if tk.SOCKSProxy != nil {
|
|
|
|
t.Fatal("invalid SOCKSProxy")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldName != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldName")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldNumber != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldNumber")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldValue != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldValue")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderNameCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameCapitalization")
|
|
|
|
}
|
|
|
|
if len(tk.Tampering.HeaderNameDiff) != 0 {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameDiff")
|
|
|
|
}
|
|
|
|
if tk.Tampering.RequestLineCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.RequestLineCapitalization")
|
|
|
|
}
|
|
|
|
if tk.Tampering.Total != true {
|
|
|
|
t.Fatal("invalid Tampering.Total")
|
|
|
|
}
|
|
|
|
sk, err := measurer.GetSummaryKeys(measurement)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, ok := sk.(hhfm.SummaryKeys); !ok {
|
|
|
|
t.Fatal("invalid type for summary keys")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoHelpers(t *testing.T) {
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
sess := &mockable.Session{}
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
callbacks := model.NewPrinterCallbacks(log.Log)
|
|
|
|
err := measurer.Run(ctx, sess, measurement, callbacks)
|
|
|
|
if !errors.Is(err, hhfm.ErrNoAvailableTestHelpers) {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
tk := measurement.TestKeys.(*hhfm.TestKeys)
|
|
|
|
if tk.Agent != "agent" {
|
|
|
|
t.Fatal("invalid Agent")
|
|
|
|
}
|
|
|
|
if tk.Failure != nil {
|
|
|
|
t.Fatal("invalid Failure")
|
|
|
|
}
|
|
|
|
if len(tk.Requests) != 0 {
|
|
|
|
t.Fatal("invalid Requests")
|
|
|
|
}
|
|
|
|
if tk.SOCKSProxy != nil {
|
|
|
|
t.Fatal("invalid SOCKSProxy")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldName != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldName")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldNumber != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldNumber")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldValue != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldValue")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderNameCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameCapitalization")
|
|
|
|
}
|
|
|
|
if len(tk.Tampering.HeaderNameDiff) != 0 {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameDiff")
|
|
|
|
}
|
|
|
|
if tk.Tampering.RequestLineCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.RequestLineCapitalization")
|
|
|
|
}
|
|
|
|
if tk.Tampering.Total != false {
|
|
|
|
t.Fatal("invalid Tampering.Total")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoActualHelpersInList(t *testing.T) {
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
sess := &mockable.Session{
|
2022-01-03 13:53:23 +01:00
|
|
|
MockableTestHelpers: map[string][]model.OOAPIService{
|
2021-02-02 12:05:47 +01:00
|
|
|
"http-return-json-headers": nil,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
callbacks := model.NewPrinterCallbacks(log.Log)
|
|
|
|
err := measurer.Run(ctx, sess, measurement, callbacks)
|
|
|
|
if !errors.Is(err, hhfm.ErrNoAvailableTestHelpers) {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
tk := measurement.TestKeys.(*hhfm.TestKeys)
|
|
|
|
if tk.Agent != "agent" {
|
|
|
|
t.Fatal("invalid Agent")
|
|
|
|
}
|
|
|
|
if tk.Failure != nil {
|
|
|
|
t.Fatal("invalid Failure")
|
|
|
|
}
|
|
|
|
if len(tk.Requests) != 0 {
|
|
|
|
t.Fatal("invalid Requests")
|
|
|
|
}
|
|
|
|
if tk.SOCKSProxy != nil {
|
|
|
|
t.Fatal("invalid SOCKSProxy")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldName != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldName")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldNumber != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldNumber")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldValue != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldValue")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderNameCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameCapitalization")
|
|
|
|
}
|
|
|
|
if len(tk.Tampering.HeaderNameDiff) != 0 {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameDiff")
|
|
|
|
}
|
|
|
|
if tk.Tampering.RequestLineCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.RequestLineCapitalization")
|
|
|
|
}
|
|
|
|
if tk.Tampering.Total != false {
|
|
|
|
t.Fatal("invalid Tampering.Total")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWrongTestHelperType(t *testing.T) {
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
sess := &mockable.Session{
|
2022-01-03 13:53:23 +01:00
|
|
|
MockableTestHelpers: map[string][]model.OOAPIService{
|
2021-02-02 12:05:47 +01:00
|
|
|
"http-return-json-headers": {{
|
|
|
|
Address: "http://127.0.0.1",
|
|
|
|
Type: "antani",
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
callbacks := model.NewPrinterCallbacks(log.Log)
|
|
|
|
err := measurer.Run(ctx, sess, measurement, callbacks)
|
|
|
|
if !errors.Is(err, hhfm.ErrInvalidHelperType) {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
tk := measurement.TestKeys.(*hhfm.TestKeys)
|
|
|
|
if tk.Agent != "agent" {
|
|
|
|
t.Fatal("invalid Agent")
|
|
|
|
}
|
|
|
|
if tk.Failure != nil {
|
|
|
|
t.Fatal("invalid Failure")
|
|
|
|
}
|
|
|
|
if len(tk.Requests) != 0 {
|
|
|
|
t.Fatal("invalid Requests")
|
|
|
|
}
|
|
|
|
if tk.SOCKSProxy != nil {
|
|
|
|
t.Fatal("invalid SOCKSProxy")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldName != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldName")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldNumber != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldNumber")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldValue != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldValue")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderNameCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameCapitalization")
|
|
|
|
}
|
|
|
|
if len(tk.Tampering.HeaderNameDiff) != 0 {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameDiff")
|
|
|
|
}
|
|
|
|
if tk.Tampering.RequestLineCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.RequestLineCapitalization")
|
|
|
|
}
|
|
|
|
if tk.Tampering.Total != false {
|
|
|
|
t.Fatal("invalid Tampering.Total")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewRequestFailure(t *testing.T) {
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
sess := &mockable.Session{
|
2022-01-03 13:53:23 +01:00
|
|
|
MockableTestHelpers: map[string][]model.OOAPIService{
|
2021-02-02 12:05:47 +01:00
|
|
|
"http-return-json-headers": {{
|
|
|
|
Address: "http://127.0.0.1\t\t\t", // invalid
|
|
|
|
Type: "legacy",
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
callbacks := model.NewPrinterCallbacks(log.Log)
|
|
|
|
err := measurer.Run(ctx, sess, measurement, callbacks)
|
|
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
tk := measurement.TestKeys.(*hhfm.TestKeys)
|
|
|
|
if tk.Agent != "agent" {
|
|
|
|
t.Fatal("invalid Agent")
|
|
|
|
}
|
|
|
|
if tk.Failure != nil {
|
|
|
|
t.Fatal("invalid Failure")
|
|
|
|
}
|
|
|
|
if len(tk.Requests) != 0 {
|
|
|
|
t.Fatal("invalid Requests")
|
|
|
|
}
|
|
|
|
if tk.SOCKSProxy != nil {
|
|
|
|
t.Fatal("invalid SOCKSProxy")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldName != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldName")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldNumber != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldNumber")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldValue != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldValue")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderNameCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameCapitalization")
|
|
|
|
}
|
|
|
|
if len(tk.Tampering.HeaderNameDiff) != 0 {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameDiff")
|
|
|
|
}
|
|
|
|
if tk.Tampering.RequestLineCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.RequestLineCapitalization")
|
|
|
|
}
|
|
|
|
if tk.Tampering.Total != false {
|
|
|
|
t.Fatal("invalid Tampering.Total")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInvalidJSONBody(t *testing.T) {
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
fmt.Fprintln(w, "Hello, client") // not valid JSON
|
|
|
|
}))
|
|
|
|
defer server.Close()
|
|
|
|
measurer := hhfm.NewExperimentMeasurer(hhfm.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
sess := &mockable.Session{
|
2022-01-03 13:53:23 +01:00
|
|
|
MockableTestHelpers: map[string][]model.OOAPIService{
|
2021-02-02 12:05:47 +01:00
|
|
|
"http-return-json-headers": {{
|
|
|
|
Address: server.URL,
|
|
|
|
Type: "legacy",
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
callbacks := model.NewPrinterCallbacks(log.Log)
|
|
|
|
err := measurer.Run(ctx, sess, measurement, callbacks)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
tk := measurement.TestKeys.(*hhfm.TestKeys)
|
|
|
|
if tk.Agent != "agent" {
|
|
|
|
t.Fatal("invalid Agent")
|
|
|
|
}
|
2021-09-28 12:42:01 +02:00
|
|
|
if *tk.Failure != netxlite.FailureJSONParseError {
|
2021-02-02 12:05:47 +01:00
|
|
|
t.Fatal("invalid Failure")
|
|
|
|
}
|
|
|
|
if len(tk.Requests) != 1 {
|
|
|
|
t.Fatal("invalid Requests")
|
|
|
|
}
|
|
|
|
// we already check the content of Requests in other tests
|
|
|
|
if tk.SOCKSProxy != nil {
|
|
|
|
t.Fatal("invalid SOCKSProxy")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldName != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldName")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldNumber != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldNumber")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderFieldValue != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderFieldValue")
|
|
|
|
}
|
|
|
|
if tk.Tampering.HeaderNameCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameCapitalization")
|
|
|
|
}
|
|
|
|
if len(tk.Tampering.HeaderNameDiff) != 0 {
|
|
|
|
t.Fatal("invalid Tampering.HeaderNameDiff")
|
|
|
|
}
|
|
|
|
if tk.Tampering.RequestLineCapitalization != false {
|
|
|
|
t.Fatal("invalid Tampering.RequestLineCapitalization")
|
|
|
|
}
|
|
|
|
if tk.Tampering.Total != true {
|
|
|
|
t.Fatal("invalid Tampering.Total")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTransactStatusCodeFailure(t *testing.T) {
|
|
|
|
txp := FakeTransport{Resp: &http.Response{
|
2021-06-15 14:01:45 +02:00
|
|
|
Body: io.NopCloser(strings.NewReader("")),
|
2021-02-02 12:05:47 +01:00
|
|
|
StatusCode: 500,
|
|
|
|
}}
|
|
|
|
resp, body, err := hhfm.Transact(txp, &http.Request{},
|
|
|
|
model.NewPrinterCallbacks(log.Log))
|
|
|
|
if !errors.Is(err, urlgetter.ErrHTTPRequestFailed) {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
if resp != nil {
|
|
|
|
t.Fatal("resp is not nil")
|
|
|
|
}
|
|
|
|
if body != nil {
|
|
|
|
t.Fatal("body is not nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTransactCannotReadBody(t *testing.T) {
|
|
|
|
expected := errors.New("mocked error")
|
|
|
|
txp := FakeTransport{Resp: &http.Response{
|
|
|
|
Body: &FakeBody{Err: expected},
|
|
|
|
StatusCode: 200,
|
|
|
|
}}
|
|
|
|
resp, body, err := hhfm.Transact(txp, &http.Request{},
|
|
|
|
model.NewPrinterCallbacks(log.Log))
|
|
|
|
if !errors.Is(err, expected) {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
if resp != nil {
|
|
|
|
t.Fatal("resp is not nil")
|
|
|
|
}
|
|
|
|
if body != nil {
|
|
|
|
t.Fatal("body is not nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTestKeys_FillTampering(t *testing.T) {
|
|
|
|
type fields struct {
|
|
|
|
Agent string
|
|
|
|
Failure *string
|
2022-05-31 21:53:01 +02:00
|
|
|
Requests []tracex.RequestEntry
|
2021-02-02 12:05:47 +01:00
|
|
|
SOCKSProxy *string
|
|
|
|
Tampering hhfm.Tampering
|
|
|
|
}
|
|
|
|
type args struct {
|
|
|
|
req *http.Request
|
|
|
|
jsonHeaders hhfm.JSONHeaders
|
|
|
|
headers map[string]string
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
fields fields
|
|
|
|
args args
|
|
|
|
}{{
|
|
|
|
name: "Request line capitalisation",
|
|
|
|
fields: fields{
|
|
|
|
Tampering: hhfm.Tampering{
|
|
|
|
RequestLineCapitalization: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
req: &http.Request{
|
|
|
|
Method: "GeT",
|
|
|
|
},
|
|
|
|
jsonHeaders: hhfm.JSONHeaders{
|
|
|
|
RequestLine: "GET / HTTP/1.1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
name: "Header field number",
|
|
|
|
fields: fields{
|
|
|
|
Tampering: hhfm.Tampering{
|
|
|
|
HeaderFieldNumber: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
req: &http.Request{
|
|
|
|
Method: "GeT",
|
|
|
|
},
|
|
|
|
jsonHeaders: hhfm.JSONHeaders{
|
|
|
|
RequestLine: "GeT / HTTP/1.1",
|
|
|
|
},
|
|
|
|
headers: map[string]string{
|
|
|
|
"UsEr-AgENt": "miniooni/0.1.0-dev",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
name: "Header name diff",
|
|
|
|
fields: fields{
|
|
|
|
Tampering: hhfm.Tampering{
|
|
|
|
HeaderNameCapitalization: true,
|
|
|
|
HeaderNameDiff: []string{"UsEr-AgENt", "User-Agent"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
req: &http.Request{
|
|
|
|
Method: "GeT",
|
|
|
|
},
|
|
|
|
jsonHeaders: hhfm.JSONHeaders{
|
|
|
|
RequestLine: "GeT / HTTP/1.1",
|
|
|
|
HeadersDict: map[string][]string{
|
|
|
|
"User-Agent": {"miniooni/0.1.0-dev"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
headers: map[string]string{
|
|
|
|
"UsEr-AgENt": "miniooni/0.1.0-dev",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
name: "Header value diff",
|
|
|
|
fields: fields{
|
|
|
|
Tampering: hhfm.Tampering{
|
|
|
|
HeaderFieldValue: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
req: &http.Request{
|
|
|
|
Method: "GeT",
|
|
|
|
},
|
|
|
|
jsonHeaders: hhfm.JSONHeaders{
|
|
|
|
RequestLine: "GeT / HTTP/1.1",
|
|
|
|
HeadersDict: map[string][]string{
|
|
|
|
"UsEr-AgENt": {"MINIOONI/0.1.0-dev"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
headers: map[string]string{
|
|
|
|
"UsEr-AgENt": "miniooni/0.1.0-dev",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
name: "Number of headers per key diffs",
|
|
|
|
fields: fields{
|
|
|
|
Tampering: hhfm.Tampering{
|
|
|
|
HeaderFieldValue: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
req: &http.Request{
|
|
|
|
Method: "GeT",
|
|
|
|
},
|
|
|
|
jsonHeaders: hhfm.JSONHeaders{
|
|
|
|
RequestLine: "GeT / HTTP/1.1",
|
|
|
|
HeadersDict: map[string][]string{
|
|
|
|
"UsEr-AgENt": {"miniooni/0.1.0-dev", "ooniprobe-engine/0.1.0-dev"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
headers: map[string]string{
|
|
|
|
"UsEr-AgENt": "miniooni/0.1.0-dev",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
tk := &hhfm.TestKeys{
|
|
|
|
Agent: tt.fields.Agent,
|
|
|
|
Failure: tt.fields.Failure,
|
|
|
|
Requests: tt.fields.Requests,
|
|
|
|
SOCKSProxy: tt.fields.SOCKSProxy,
|
|
|
|
}
|
|
|
|
tk.FillTampering(tt.args.req, tt.args.jsonHeaders, tt.args.headers)
|
|
|
|
if diff := cmp.Diff(tt.fields.Tampering, tk.Tampering); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewRequestEntryList(t *testing.T) {
|
|
|
|
type args struct {
|
|
|
|
req *http.Request
|
|
|
|
headers map[string]string
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
args args
|
2022-05-31 21:53:01 +02:00
|
|
|
wantOut []tracex.RequestEntry
|
2021-02-02 12:05:47 +01:00
|
|
|
}{{
|
|
|
|
name: "common case",
|
|
|
|
args: args{
|
|
|
|
req: &http.Request{
|
|
|
|
Method: "GeT",
|
|
|
|
URL: &url.URL{
|
|
|
|
Scheme: "http",
|
|
|
|
Host: "10.0.0.1",
|
|
|
|
Path: "/",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
headers: map[string]string{
|
|
|
|
"ContENt-tYPE": "text/plain",
|
|
|
|
"User-aGENT": "foo/1.0",
|
|
|
|
},
|
|
|
|
},
|
2022-05-31 21:53:01 +02:00
|
|
|
wantOut: []tracex.RequestEntry{{
|
|
|
|
Request: tracex.HTTPRequest{
|
|
|
|
HeadersList: []tracex.HTTPHeader{{
|
2021-02-02 12:05:47 +01:00
|
|
|
Key: "ContENt-tYPE",
|
2022-05-31 21:53:01 +02:00
|
|
|
Value: tracex.MaybeBinaryValue{Value: "text/plain"},
|
2021-02-02 12:05:47 +01:00
|
|
|
}, {
|
|
|
|
Key: "User-aGENT",
|
2022-05-31 21:53:01 +02:00
|
|
|
Value: tracex.MaybeBinaryValue{Value: "foo/1.0"},
|
2021-02-02 12:05:47 +01:00
|
|
|
}},
|
2022-05-31 21:53:01 +02:00
|
|
|
Headers: map[string]tracex.MaybeBinaryValue{
|
2021-02-02 12:05:47 +01:00
|
|
|
"ContENt-tYPE": {Value: "text/plain"},
|
|
|
|
"User-aGENT": {Value: "foo/1.0"},
|
|
|
|
},
|
|
|
|
Method: "GeT",
|
|
|
|
URL: "http://10.0.0.1/",
|
|
|
|
},
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
name: "without headers",
|
|
|
|
args: args{
|
|
|
|
req: &http.Request{
|
|
|
|
Method: "GeT",
|
|
|
|
URL: &url.URL{
|
|
|
|
Scheme: "http",
|
|
|
|
Host: "10.0.0.1",
|
|
|
|
Path: "/",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2022-05-31 21:53:01 +02:00
|
|
|
wantOut: []tracex.RequestEntry{{
|
|
|
|
Request: tracex.HTTPRequest{
|
2021-02-02 12:05:47 +01:00
|
|
|
Method: "GeT",
|
2022-05-31 21:53:01 +02:00
|
|
|
Headers: make(map[string]tracex.MaybeBinaryValue),
|
|
|
|
HeadersList: []tracex.HTTPHeader{},
|
2021-02-02 12:05:47 +01:00
|
|
|
URL: "http://10.0.0.1/",
|
|
|
|
},
|
|
|
|
}},
|
|
|
|
}}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
gotOut := hhfm.NewRequestEntryList(tt.args.req, tt.args.headers)
|
|
|
|
if diff := cmp.Diff(tt.wantOut, gotOut); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewHTTPResponse(t *testing.T) {
|
|
|
|
type args struct {
|
|
|
|
resp *http.Response
|
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
args args
|
2022-05-31 21:53:01 +02:00
|
|
|
wantOut tracex.HTTPResponse
|
2021-02-02 12:05:47 +01:00
|
|
|
}{{
|
|
|
|
name: "common case",
|
|
|
|
args: args{
|
|
|
|
resp: &http.Response{
|
|
|
|
StatusCode: 200,
|
|
|
|
Header: http.Header{
|
|
|
|
"Content-Type": []string{"text/plain"},
|
|
|
|
"User-Agent": []string{"foo/1.0"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
data: []byte("deadbeef"),
|
|
|
|
},
|
2022-05-31 21:53:01 +02:00
|
|
|
wantOut: tracex.HTTPResponse{
|
|
|
|
Body: tracex.MaybeBinaryValue{Value: "deadbeef"},
|
2021-02-02 12:05:47 +01:00
|
|
|
Code: 200,
|
2022-05-31 21:53:01 +02:00
|
|
|
HeadersList: []tracex.HTTPHeader{{
|
2021-02-02 12:05:47 +01:00
|
|
|
Key: "Content-Type",
|
2022-05-31 21:53:01 +02:00
|
|
|
Value: tracex.MaybeBinaryValue{Value: "text/plain"},
|
2021-02-02 12:05:47 +01:00
|
|
|
}, {
|
|
|
|
Key: "User-Agent",
|
2022-05-31 21:53:01 +02:00
|
|
|
Value: tracex.MaybeBinaryValue{Value: "foo/1.0"},
|
2021-02-02 12:05:47 +01:00
|
|
|
}},
|
2022-05-31 21:53:01 +02:00
|
|
|
Headers: map[string]tracex.MaybeBinaryValue{
|
2021-02-02 12:05:47 +01:00
|
|
|
"Content-Type": {Value: "text/plain"},
|
|
|
|
"User-Agent": {Value: "foo/1.0"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
name: "with no HTTP header and body",
|
|
|
|
args: args{
|
|
|
|
resp: &http.Response{StatusCode: 200},
|
|
|
|
},
|
2022-05-31 21:53:01 +02:00
|
|
|
wantOut: tracex.HTTPResponse{
|
|
|
|
Body: tracex.MaybeBinaryValue{Value: ""},
|
2021-02-02 12:05:47 +01:00
|
|
|
Code: 200,
|
2022-05-31 21:53:01 +02:00
|
|
|
HeadersList: []tracex.HTTPHeader{},
|
|
|
|
Headers: map[string]tracex.MaybeBinaryValue{},
|
2021-02-02 12:05:47 +01:00
|
|
|
},
|
|
|
|
}}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
gotOut := hhfm.NewHTTPResponse(tt.args.resp, tt.args.data)
|
|
|
|
if diff := cmp.Diff(tt.wantOut, gotOut); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDialerDialContext(t *testing.T) {
|
|
|
|
expected := errors.New("mocked error")
|
|
|
|
d := hhfm.Dialer{Dialer: FakeDialer{Err: expected}}
|
|
|
|
conn, err := d.DialContext(context.Background(), "tcp", "127.0.0.1:80")
|
|
|
|
if !errors.Is(err, expected) {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
if conn != nil {
|
|
|
|
t.Fatal("conn is not nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSummaryKeysInvalidType(t *testing.T) {
|
|
|
|
measurement := new(model.Measurement)
|
|
|
|
m := &hhfm.Measurer{}
|
|
|
|
_, err := m.GetSummaryKeys(measurement)
|
|
|
|
if err.Error() != "invalid test keys type" {
|
|
|
|
t.Fatal("not the error we expected")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSummaryKeysWorksAsIntended(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
tampering hhfm.Tampering
|
|
|
|
isAnomaly bool
|
|
|
|
}{{
|
|
|
|
tampering: hhfm.Tampering{},
|
|
|
|
isAnomaly: false,
|
|
|
|
}, {
|
|
|
|
tampering: hhfm.Tampering{HeaderFieldName: true},
|
|
|
|
isAnomaly: true,
|
|
|
|
}, {
|
|
|
|
tampering: hhfm.Tampering{HeaderFieldNumber: true},
|
|
|
|
isAnomaly: true,
|
|
|
|
}, {
|
|
|
|
tampering: hhfm.Tampering{HeaderFieldValue: true},
|
|
|
|
isAnomaly: true,
|
|
|
|
}, {
|
|
|
|
tampering: hhfm.Tampering{HeaderNameCapitalization: true},
|
|
|
|
isAnomaly: true,
|
|
|
|
}, {
|
|
|
|
tampering: hhfm.Tampering{RequestLineCapitalization: true},
|
|
|
|
isAnomaly: true,
|
|
|
|
}, {
|
|
|
|
tampering: hhfm.Tampering{Total: true},
|
|
|
|
isAnomaly: true,
|
|
|
|
}}
|
|
|
|
for idx, tt := range tests {
|
|
|
|
t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
|
|
|
|
m := &hhfm.Measurer{}
|
|
|
|
measurement := &model.Measurement{TestKeys: &hhfm.TestKeys{
|
|
|
|
Tampering: tt.tampering,
|
|
|
|
}}
|
|
|
|
got, err := m.GetSummaryKeys(measurement)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sk := got.(hhfm.SummaryKeys)
|
|
|
|
if sk.IsAnomaly != tt.isAnomaly {
|
|
|
|
t.Fatal("unexpected isAnomaly value")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|