feat(oohelperd): log messages at info level (#896)
We're using a request-specific logger where we also print the ID of the request. This design helps to observe logs produced by concurrent requests. Part of https://github.com/ooni/probe/issues/2183 While there, fix https://github.com/ooni/probe/issues/2241
This commit is contained in:
		
							parent
							
								
									8ca7645026
								
							
						
					
					
						commit
						4241ee4bc1
					
				@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/measurexlite"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model"
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
						"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/tracex"
 | 
						"github.com/ooni/probe-cli/v3/internal/tracex"
 | 
				
			||||||
@ -26,8 +27,11 @@ type dnsConfig struct {
 | 
				
			|||||||
	// Domain is the MANDATORY domain to resolve.
 | 
						// Domain is the MANDATORY domain to resolve.
 | 
				
			||||||
	Domain string
 | 
						Domain string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Logger is the MANDATORY logger to use.
 | 
				
			||||||
 | 
						Logger model.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewResolver is the MANDATORY factory to create a new resolver.
 | 
						// NewResolver is the MANDATORY factory to create a new resolver.
 | 
				
			||||||
	NewResolver func() model.Resolver
 | 
						NewResolver func(model.Logger) model.Resolver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Out is the channel where we publish the results.
 | 
						// Out is the channel where we publish the results.
 | 
				
			||||||
	Out chan ctrlDNSResult
 | 
						Out chan ctrlDNSResult
 | 
				
			||||||
@ -42,9 +46,11 @@ func dnsDo(ctx context.Context, config *dnsConfig) {
 | 
				
			|||||||
	ctx, cancel := context.WithTimeout(ctx, timeout)
 | 
						ctx, cancel := context.WithTimeout(ctx, timeout)
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
	defer config.Wg.Done()
 | 
						defer config.Wg.Done()
 | 
				
			||||||
	reso := config.NewResolver()
 | 
						reso := config.NewResolver(config.Logger)
 | 
				
			||||||
	defer reso.CloseIdleConnections()
 | 
						defer reso.CloseIdleConnections()
 | 
				
			||||||
 | 
						ol := measurexlite.NewOperationLogger(config.Logger, "DNSLookup %s", config.Domain)
 | 
				
			||||||
	addrs, err := reso.LookupHost(ctx, config.Domain)
 | 
						addrs, err := reso.LookupHost(ctx, config.Domain)
 | 
				
			||||||
 | 
						ol.Stop(err)
 | 
				
			||||||
	if addrs == nil {
 | 
						if addrs == nil {
 | 
				
			||||||
		addrs = []string{} // fix: the old test helper did that
 | 
							addrs = []string{} // fix: the old test helper did that
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -68,7 +68,8 @@ func TestDNSDo(t *testing.T) {
 | 
				
			|||||||
		ctx := context.Background()
 | 
							ctx := context.Background()
 | 
				
			||||||
		config := &dnsConfig{
 | 
							config := &dnsConfig{
 | 
				
			||||||
			Domain: "antani.ooni.org",
 | 
								Domain: "antani.ooni.org",
 | 
				
			||||||
			NewResolver: func() model.Resolver {
 | 
								Logger: model.DiscardLogger,
 | 
				
			||||||
 | 
								NewResolver: func(model.Logger) model.Resolver {
 | 
				
			||||||
				return &mocks.Resolver{
 | 
									return &mocks.Resolver{
 | 
				
			||||||
					MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
 | 
										MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
 | 
				
			||||||
						return nil, netxlite.ErrOODNSNoSuchHost
 | 
											return nil, netxlite.ErrOODNSNoSuchHost
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model"
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
						"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/runtimex"
 | 
						"github.com/ooni/probe-cli/v3/internal/runtimex"
 | 
				
			||||||
@ -18,20 +19,26 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// handler implements the Web Connectivity test helper HTTP API.
 | 
					// handler implements the Web Connectivity test helper HTTP API.
 | 
				
			||||||
type handler struct {
 | 
					type handler struct {
 | 
				
			||||||
 | 
						// BaseLogger is the MANDATORY logger to use.
 | 
				
			||||||
 | 
						BaseLogger model.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Indexer is the MANDATORY atomic integer used to assign an index to requests.
 | 
				
			||||||
 | 
						Indexer *atomicx.Int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// MaxAcceptableBody is the MANDATORY maximum acceptable response body.
 | 
						// MaxAcceptableBody is the MANDATORY maximum acceptable response body.
 | 
				
			||||||
	MaxAcceptableBody int64
 | 
						MaxAcceptableBody int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewClient is the MANDATORY factory to create a new HTTPClient.
 | 
						// NewClient is the MANDATORY factory to create a new HTTPClient.
 | 
				
			||||||
	NewClient func() model.HTTPClient
 | 
						NewClient func(model.Logger) model.HTTPClient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewDialer is the MANDATORY factory to create a new Dialer.
 | 
						// NewDialer is the MANDATORY factory to create a new Dialer.
 | 
				
			||||||
	NewDialer func() model.Dialer
 | 
						NewDialer func(model.Logger) model.Dialer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewResolver is the MANDATORY factory for creating a new resolver.
 | 
						// NewResolver is the MANDATORY factory for creating a new resolver.
 | 
				
			||||||
	NewResolver func() model.Resolver
 | 
						NewResolver func(model.Logger) model.Resolver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewTLSHandshaker is the MANDATORY factory for creating a new TLS handshaker.
 | 
						// NewTLSHandshaker is the MANDATORY factory for creating a new TLS handshaker.
 | 
				
			||||||
	NewTLSHandshaker func() model.TLSHandshaker
 | 
						NewTLSHandshaker func(model.Logger) model.TLSHandshaker
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ http.Handler = &handler{}
 | 
					var _ http.Handler = &handler{}
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model"
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model/mocks"
 | 
						"github.com/ooni/probe-cli/v3/internal/model/mocks"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
						"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
				
			||||||
@ -53,17 +54,19 @@ const requestWithoutDomainName = `{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestHandlerWorkingAsIntended(t *testing.T) {
 | 
					func TestHandlerWorkingAsIntended(t *testing.T) {
 | 
				
			||||||
	handler := &handler{
 | 
						handler := &handler{
 | 
				
			||||||
 | 
							BaseLogger:        model.DiscardLogger,
 | 
				
			||||||
 | 
							Indexer:           &atomicx.Int64{},
 | 
				
			||||||
		MaxAcceptableBody: 1 << 24,
 | 
							MaxAcceptableBody: 1 << 24,
 | 
				
			||||||
		NewClient: func() model.HTTPClient {
 | 
							NewClient: func(model.Logger) model.HTTPClient {
 | 
				
			||||||
			return http.DefaultClient
 | 
								return http.DefaultClient
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		NewDialer: func() model.Dialer {
 | 
							NewDialer: func(model.Logger) model.Dialer {
 | 
				
			||||||
			return netxlite.NewDialerWithStdlibResolver(model.DiscardLogger)
 | 
								return netxlite.NewDialerWithStdlibResolver(model.DiscardLogger)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		NewResolver: func() model.Resolver {
 | 
							NewResolver: func(model.Logger) model.Resolver {
 | 
				
			||||||
			return netxlite.NewUnwrappedStdlibResolver()
 | 
								return netxlite.NewUnwrappedStdlibResolver()
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		NewTLSHandshaker: func() model.TLSHandshaker {
 | 
							NewTLSHandshaker: func(model.Logger) model.TLSHandshaker {
 | 
				
			||||||
			return netxlite.NewTLSHandshakerStdlib(model.DiscardLogger)
 | 
								return netxlite.NewTLSHandshakerStdlib(model.DiscardLogger)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -30,11 +30,14 @@ type httpConfig struct {
 | 
				
			|||||||
	// Headers is OPTIONAL and contains the request headers we should set.
 | 
						// Headers is OPTIONAL and contains the request headers we should set.
 | 
				
			||||||
	Headers map[string][]string
 | 
						Headers map[string][]string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Logger is the MANDATORY logger to use.
 | 
				
			||||||
 | 
						Logger model.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// MaxAcceptableBody is MANDATORY and specifies the maximum acceptable body size.
 | 
						// MaxAcceptableBody is MANDATORY and specifies the maximum acceptable body size.
 | 
				
			||||||
	MaxAcceptableBody int64
 | 
						MaxAcceptableBody int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewClient is the MANDATORY factory to create a new client.
 | 
						// NewClient is the MANDATORY factory to create a new client.
 | 
				
			||||||
	NewClient func() model.HTTPClient
 | 
						NewClient func(model.Logger) model.HTTPClient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Out is the MANDATORY channel where we'll post results.
 | 
						// Out is the MANDATORY channel where we'll post results.
 | 
				
			||||||
	Out chan ctrlHTTPResponse
 | 
						Out chan ctrlHTTPResponse
 | 
				
			||||||
@ -48,6 +51,7 @@ type httpConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// httpDo performs the HTTP check.
 | 
					// httpDo performs the HTTP check.
 | 
				
			||||||
func httpDo(ctx context.Context, config *httpConfig) {
 | 
					func httpDo(ctx context.Context, config *httpConfig) {
 | 
				
			||||||
 | 
						ol := measurexlite.NewOperationLogger(config.Logger, "GET %s", config.URL)
 | 
				
			||||||
	const timeout = 15 * time.Second
 | 
						const timeout = 15 * time.Second
 | 
				
			||||||
	ctx, cancel := context.WithTimeout(ctx, timeout)
 | 
						ctx, cancel := context.WithTimeout(ctx, timeout)
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
@ -62,6 +66,7 @@ func httpDo(ctx context.Context, config *httpConfig) {
 | 
				
			|||||||
			Headers:    map[string]string{},
 | 
								Headers:    map[string]string{},
 | 
				
			||||||
			StatusCode: -1,
 | 
								StatusCode: -1,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							ol.Stop(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// The original test helper failed with extra headers while here
 | 
						// The original test helper failed with extra headers while here
 | 
				
			||||||
@ -74,7 +79,7 @@ func httpDo(ctx context.Context, config *httpConfig) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	clnt := config.NewClient()
 | 
						clnt := config.NewClient(config.Logger)
 | 
				
			||||||
	defer clnt.CloseIdleConnections()
 | 
						defer clnt.CloseIdleConnections()
 | 
				
			||||||
	resp, err := clnt.Do(req)
 | 
						resp, err := clnt.Do(req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -86,6 +91,7 @@ func httpDo(ctx context.Context, config *httpConfig) {
 | 
				
			|||||||
			Headers:    map[string]string{},
 | 
								Headers:    map[string]string{},
 | 
				
			||||||
			StatusCode: -1,
 | 
								StatusCode: -1,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							ol.Stop(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer resp.Body.Close()
 | 
						defer resp.Body.Close()
 | 
				
			||||||
@ -95,6 +101,7 @@ func httpDo(ctx context.Context, config *httpConfig) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	reader := &io.LimitedReader{R: resp.Body, N: config.MaxAcceptableBody}
 | 
						reader := &io.LimitedReader{R: resp.Body, N: config.MaxAcceptableBody}
 | 
				
			||||||
	data, err := netxlite.ReadAllContext(ctx, reader)
 | 
						data, err := netxlite.ReadAllContext(ctx, reader)
 | 
				
			||||||
 | 
						ol.Stop(err)
 | 
				
			||||||
	config.Out <- ctrlHTTPResponse{
 | 
						config.Out <- ctrlHTTPResponse{
 | 
				
			||||||
		BodyLength: int64(len(data)),
 | 
							BodyLength: int64(len(data)),
 | 
				
			||||||
		Failure:    httpMapFailure(err),
 | 
							Failure:    httpMapFailure(err),
 | 
				
			||||||
 | 
				
			|||||||
@ -20,8 +20,9 @@ func TestHTTPDoWithInvalidURL(t *testing.T) {
 | 
				
			|||||||
	wg.Add(1)
 | 
						wg.Add(1)
 | 
				
			||||||
	go httpDo(ctx, &httpConfig{
 | 
						go httpDo(ctx, &httpConfig{
 | 
				
			||||||
		Headers:           nil,
 | 
							Headers:           nil,
 | 
				
			||||||
 | 
							Logger:            model.DiscardLogger,
 | 
				
			||||||
		MaxAcceptableBody: 1 << 24,
 | 
							MaxAcceptableBody: 1 << 24,
 | 
				
			||||||
		NewClient: func() model.HTTPClient {
 | 
							NewClient: func(model.Logger) model.HTTPClient {
 | 
				
			||||||
			return http.DefaultClient
 | 
								return http.DefaultClient
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Out: httpch,
 | 
							Out: httpch,
 | 
				
			||||||
@ -44,8 +45,9 @@ func TestHTTPDoWithHTTPTransportFailure(t *testing.T) {
 | 
				
			|||||||
	wg.Add(1)
 | 
						wg.Add(1)
 | 
				
			||||||
	go httpDo(ctx, &httpConfig{
 | 
						go httpDo(ctx, &httpConfig{
 | 
				
			||||||
		Headers:           nil,
 | 
							Headers:           nil,
 | 
				
			||||||
 | 
							Logger:            model.DiscardLogger,
 | 
				
			||||||
		MaxAcceptableBody: 1 << 24,
 | 
							MaxAcceptableBody: 1 << 24,
 | 
				
			||||||
		NewClient: func() model.HTTPClient {
 | 
							NewClient: func(model.Logger) model.HTTPClient {
 | 
				
			||||||
			return &http.Client{
 | 
								return &http.Client{
 | 
				
			||||||
				Transport: &mocks.HTTPTransport{
 | 
									Transport: &mocks.HTTPTransport{
 | 
				
			||||||
					MockRoundTrip: func(req *http.Request) (*http.Response, error) {
 | 
										MockRoundTrip: func(req *http.Request) (*http.Response, error) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										41
									
								
								internal/cmd/oohelperd/logging.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								internal/cmd/oohelperd/logging.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// indexLogger is a logger with an index.
 | 
				
			||||||
 | 
					type indexLogger struct {
 | 
				
			||||||
 | 
						indexstr string
 | 
				
			||||||
 | 
						logger   model.Logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ model.Logger = &indexLogger{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Debug implements DebugLogger.Debug
 | 
				
			||||||
 | 
					func (p *indexLogger) Debug(msg string) {
 | 
				
			||||||
 | 
						p.logger.Debug(p.indexstr + msg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Debugf implements DebugLogger.Debugf
 | 
				
			||||||
 | 
					func (p *indexLogger) Debugf(format string, v ...interface{}) {
 | 
				
			||||||
 | 
						p.logger.Debugf(p.indexstr+format, v...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Info implements InfoLogger.Info
 | 
				
			||||||
 | 
					func (p *indexLogger) Info(msg string) {
 | 
				
			||||||
 | 
						p.logger.Info(p.indexstr + msg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Infov implements InfoLogger.Infov
 | 
				
			||||||
 | 
					func (p *indexLogger) Infof(format string, v ...interface{}) {
 | 
				
			||||||
 | 
						p.logger.Infof(p.indexstr+format, v...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warn implements Logger.Warn
 | 
				
			||||||
 | 
					func (p *indexLogger) Warn(msg string) {
 | 
				
			||||||
 | 
						p.logger.Warn(p.indexstr + msg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warnf implements Logger.Warnf
 | 
				
			||||||
 | 
					func (p *indexLogger) Warnf(format string, v ...interface{}) {
 | 
				
			||||||
 | 
						p.logger.Warnf(p.indexstr+format, v...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										105
									
								
								internal/cmd/oohelperd/logging_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								internal/cmd/oohelperd/logging_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/model/mocks"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIndexLogger(t *testing.T) {
 | 
				
			||||||
 | 
						t.Run("Debug", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := "<0>antani"
 | 
				
			||||||
 | 
							base := &mocks.Logger{
 | 
				
			||||||
 | 
								MockDebug: func(message string) {
 | 
				
			||||||
 | 
									if message != expected {
 | 
				
			||||||
 | 
										t.Fatal("unexpected message")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger := &indexLogger{
 | 
				
			||||||
 | 
								indexstr: "<0>",
 | 
				
			||||||
 | 
								logger:   base,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger.Debug("antani")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Info", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := "<0>antani"
 | 
				
			||||||
 | 
							base := &mocks.Logger{
 | 
				
			||||||
 | 
								MockInfo: func(message string) {
 | 
				
			||||||
 | 
									if message != expected {
 | 
				
			||||||
 | 
										t.Fatal("unexpected message")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger := &indexLogger{
 | 
				
			||||||
 | 
								indexstr: "<0>",
 | 
				
			||||||
 | 
								logger:   base,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger.Info("antani")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Warn", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := "<0>antani"
 | 
				
			||||||
 | 
							base := &mocks.Logger{
 | 
				
			||||||
 | 
								MockWarn: func(message string) {
 | 
				
			||||||
 | 
									if message != expected {
 | 
				
			||||||
 | 
										t.Fatal("unexpected message")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger := &indexLogger{
 | 
				
			||||||
 | 
								indexstr: "<0>",
 | 
				
			||||||
 | 
								logger:   base,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger.Warn("antani")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Debugf", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := "<0>antani%d"
 | 
				
			||||||
 | 
							base := &mocks.Logger{
 | 
				
			||||||
 | 
								MockDebugf: func(format string, v ...any) {
 | 
				
			||||||
 | 
									if format != expected {
 | 
				
			||||||
 | 
										t.Fatal("unexpected message")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger := &indexLogger{
 | 
				
			||||||
 | 
								indexstr: "<0>",
 | 
				
			||||||
 | 
								logger:   base,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger.Debugf("antani%d", 11)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Infof", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := "<0>antani%d"
 | 
				
			||||||
 | 
							base := &mocks.Logger{
 | 
				
			||||||
 | 
								MockInfof: func(format string, v ...any) {
 | 
				
			||||||
 | 
									if format != expected {
 | 
				
			||||||
 | 
										t.Fatal("unexpected message")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger := &indexLogger{
 | 
				
			||||||
 | 
								indexstr: "<0>",
 | 
				
			||||||
 | 
								logger:   base,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger.Infof("antani%d", 11)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Warnf", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := "<0>antani%d"
 | 
				
			||||||
 | 
							base := &mocks.Logger{
 | 
				
			||||||
 | 
								MockWarnf: func(format string, v ...any) {
 | 
				
			||||||
 | 
									if format != expected {
 | 
				
			||||||
 | 
										t.Fatal("unexpected message")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger := &indexLogger{
 | 
				
			||||||
 | 
								indexstr: "<0>",
 | 
				
			||||||
 | 
								logger:   base,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger.Warnf("antani%d", 11)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/apex/log"
 | 
						"github.com/apex/log"
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model"
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
						"github.com/ooni/probe-cli/v3/internal/netxlite"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/runtimex"
 | 
						"github.com/ooni/probe-cli/v3/internal/runtimex"
 | 
				
			||||||
@ -29,11 +30,11 @@ func init() {
 | 
				
			|||||||
	srvCtx, srvCancel = context.WithCancel(context.Background())
 | 
						srvCtx, srvCancel = context.WithCancel(context.Background())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newResolver() model.Resolver {
 | 
					func newResolver(logger model.Logger) model.Resolver {
 | 
				
			||||||
	// Implementation note: pin to a specific resolver so we don't depend upon the
 | 
						// Implementation note: pin to a specific resolver so we don't depend upon the
 | 
				
			||||||
	// default resolver configured by the box. Also, use an encrypted transport thus
 | 
						// default resolver configured by the box. Also, use an encrypted transport thus
 | 
				
			||||||
	// we're less vulnerable to any policy implemented by the box's provider.
 | 
						// we're less vulnerable to any policy implemented by the box's provider.
 | 
				
			||||||
	resolver := netxlite.NewParallelDNSOverHTTPSResolver(log.Log, "https://8.8.8.8/dns-query")
 | 
						resolver := netxlite.NewParallelDNSOverHTTPSResolver(logger, "https://dns.google/dns-query")
 | 
				
			||||||
	return resolver
 | 
						return resolver
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,16 +55,18 @@ func main() {
 | 
				
			|||||||
	defer srvCancel()
 | 
						defer srvCancel()
 | 
				
			||||||
	mux := http.NewServeMux()
 | 
						mux := http.NewServeMux()
 | 
				
			||||||
	mux.Handle("/", &handler{
 | 
						mux.Handle("/", &handler{
 | 
				
			||||||
 | 
							BaseLogger:        log.Log,
 | 
				
			||||||
 | 
							Indexer:           &atomicx.Int64{},
 | 
				
			||||||
		MaxAcceptableBody: maxAcceptableBody,
 | 
							MaxAcceptableBody: maxAcceptableBody,
 | 
				
			||||||
		NewClient: func() model.HTTPClient {
 | 
							NewClient: func(logger model.Logger) model.HTTPClient {
 | 
				
			||||||
			return netxlite.NewHTTPClientWithResolver(log.Log, newResolver())
 | 
								return netxlite.NewHTTPClientWithResolver(logger, newResolver(logger))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		NewDialer: func() model.Dialer {
 | 
							NewDialer: func(logger model.Logger) model.Dialer {
 | 
				
			||||||
			return netxlite.NewDialerWithResolver(log.Log, newResolver())
 | 
								return netxlite.NewDialerWithResolver(logger, newResolver(logger))
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		NewResolver: newResolver,
 | 
							NewResolver: newResolver,
 | 
				
			||||||
		NewTLSHandshaker: func() model.TLSHandshaker {
 | 
							NewTLSHandshaker: func(logger model.Logger) model.TLSHandshaker {
 | 
				
			||||||
			return netxlite.NewTLSHandshakerStdlib(log.Log)
 | 
								return netxlite.NewTLSHandshakerStdlib(logger)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	srv := &http.Server{Addr: *endpoint, Handler: mux}
 | 
						srv := &http.Server{Addr: *endpoint, Handler: mux}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
@ -24,9 +25,16 @@ type (
 | 
				
			|||||||
// measure performs the measurement described by the request and
 | 
					// measure performs the measurement described by the request and
 | 
				
			||||||
// returns the corresponding response or an error.
 | 
					// returns the corresponding response or an error.
 | 
				
			||||||
func measure(ctx context.Context, config *handler, creq *ctrlRequest) (*ctrlResponse, error) {
 | 
					func measure(ctx context.Context, config *handler, creq *ctrlRequest) (*ctrlResponse, error) {
 | 
				
			||||||
 | 
						// create indexed logger
 | 
				
			||||||
 | 
						logger := &indexLogger{
 | 
				
			||||||
 | 
							indexstr: fmt.Sprintf("<#%d> ", config.Indexer.Add(1)),
 | 
				
			||||||
 | 
							logger:   config.BaseLogger,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// parse input for correctness
 | 
						// parse input for correctness
 | 
				
			||||||
	URL, err := url.Parse(creq.HTTPRequest)
 | 
						URL, err := url.Parse(creq.HTTPRequest)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Warnf("cannot parse URL: %s", err.Error())
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wg := &sync.WaitGroup{}
 | 
						wg := &sync.WaitGroup{}
 | 
				
			||||||
@ -37,6 +45,7 @@ func measure(ctx context.Context, config *handler, creq *ctrlRequest) (*ctrlResp
 | 
				
			|||||||
		wg.Add(1)
 | 
							wg.Add(1)
 | 
				
			||||||
		go dnsDo(ctx, &dnsConfig{
 | 
							go dnsDo(ctx, &dnsConfig{
 | 
				
			||||||
			Domain:      URL.Hostname(),
 | 
								Domain:      URL.Hostname(),
 | 
				
			||||||
 | 
								Logger:      logger,
 | 
				
			||||||
			NewResolver: config.NewResolver,
 | 
								NewResolver: config.NewResolver,
 | 
				
			||||||
			Out:         dnsch,
 | 
								Out:         dnsch,
 | 
				
			||||||
			Wg:          wg,
 | 
								Wg:          wg,
 | 
				
			||||||
@ -78,6 +87,7 @@ func measure(ctx context.Context, config *handler, creq *ctrlRequest) (*ctrlResp
 | 
				
			|||||||
			Address:          endpoint.Addr,
 | 
								Address:          endpoint.Addr,
 | 
				
			||||||
			EnableTLS:        endpoint.TLS,
 | 
								EnableTLS:        endpoint.TLS,
 | 
				
			||||||
			Endpoint:         endpoint.Epnt,
 | 
								Endpoint:         endpoint.Epnt,
 | 
				
			||||||
 | 
								Logger:           logger,
 | 
				
			||||||
			NewDialer:        config.NewDialer,
 | 
								NewDialer:        config.NewDialer,
 | 
				
			||||||
			NewTSLHandshaker: config.NewTLSHandshaker,
 | 
								NewTSLHandshaker: config.NewTLSHandshaker,
 | 
				
			||||||
			URLHostname:      URL.Hostname(),
 | 
								URLHostname:      URL.Hostname(),
 | 
				
			||||||
@ -91,6 +101,7 @@ func measure(ctx context.Context, config *handler, creq *ctrlRequest) (*ctrlResp
 | 
				
			|||||||
	wg.Add(1)
 | 
						wg.Add(1)
 | 
				
			||||||
	go httpDo(ctx, &httpConfig{
 | 
						go httpDo(ctx, &httpConfig{
 | 
				
			||||||
		Headers:           creq.HTTPRequestHeaders,
 | 
							Headers:           creq.HTTPRequestHeaders,
 | 
				
			||||||
 | 
							Logger:            logger,
 | 
				
			||||||
		MaxAcceptableBody: config.MaxAcceptableBody,
 | 
							MaxAcceptableBody: config.MaxAcceptableBody,
 | 
				
			||||||
		NewClient:         config.NewClient,
 | 
							NewClient:         config.NewClient,
 | 
				
			||||||
		Out:               httpch,
 | 
							Out:               httpch,
 | 
				
			||||||
 | 
				
			|||||||
@ -47,11 +47,14 @@ type tcpConfig struct {
 | 
				
			|||||||
	// Endpoint is the MANDATORY endpoint to connect to.
 | 
						// Endpoint is the MANDATORY endpoint to connect to.
 | 
				
			||||||
	Endpoint string
 | 
						Endpoint string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Logger is the MANDATORY logger to use.
 | 
				
			||||||
 | 
						Logger model.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewDialer is the MANDATORY factory for creating a new dialer.
 | 
						// NewDialer is the MANDATORY factory for creating a new dialer.
 | 
				
			||||||
	NewDialer func() model.Dialer
 | 
						NewDialer func(model.Logger) model.Dialer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// NewTSLHandshaker is the MANDATORY factory for creating a new handshaker.
 | 
						// NewTSLHandshaker is the MANDATORY factory for creating a new handshaker.
 | 
				
			||||||
	NewTSLHandshaker func() model.TLSHandshaker
 | 
						NewTSLHandshaker func(model.Logger) model.TLSHandshaker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Out is the MANDATORY where we'll post the TCP measurement results.
 | 
						// Out is the MANDATORY where we'll post the TCP measurement results.
 | 
				
			||||||
	Out chan *tcpResultPair
 | 
						Out chan *tcpResultPair
 | 
				
			||||||
@ -78,13 +81,21 @@ func tcpDo(ctx context.Context, config *tcpConfig) {
 | 
				
			|||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		config.Out <- out
 | 
							config.Out <- out
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	dialer := config.NewDialer()
 | 
						ol := measurexlite.NewOperationLogger(
 | 
				
			||||||
 | 
							config.Logger,
 | 
				
			||||||
 | 
							"TCPConnect %s EnableTLS=%v SNI=%s",
 | 
				
			||||||
 | 
							config.Endpoint,
 | 
				
			||||||
 | 
							config.EnableTLS,
 | 
				
			||||||
 | 
							config.URLHostname,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						dialer := config.NewDialer(config.Logger)
 | 
				
			||||||
	defer dialer.CloseIdleConnections()
 | 
						defer dialer.CloseIdleConnections()
 | 
				
			||||||
	conn, err := dialer.DialContext(ctx, "tcp", config.Endpoint)
 | 
						conn, err := dialer.DialContext(ctx, "tcp", config.Endpoint)
 | 
				
			||||||
	out.TCP.Failure = tcpMapFailure(newfailure(err))
 | 
						out.TCP.Failure = tcpMapFailure(newfailure(err))
 | 
				
			||||||
	out.TCP.Status = err == nil
 | 
						out.TCP.Status = err == nil
 | 
				
			||||||
	defer measurexlite.MaybeClose(conn)
 | 
						defer measurexlite.MaybeClose(conn)
 | 
				
			||||||
	if err != nil || !config.EnableTLS {
 | 
						if err != nil || !config.EnableTLS {
 | 
				
			||||||
 | 
							ol.Stop(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tlsConfig := &tls.Config{
 | 
						tlsConfig := &tls.Config{
 | 
				
			||||||
@ -92,8 +103,9 @@ func tcpDo(ctx context.Context, config *tcpConfig) {
 | 
				
			|||||||
		RootCAs:    netxlite.NewDefaultCertPool(),
 | 
							RootCAs:    netxlite.NewDefaultCertPool(),
 | 
				
			||||||
		ServerName: config.URLHostname,
 | 
							ServerName: config.URLHostname,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	thx := config.NewTSLHandshaker()
 | 
						thx := config.NewTSLHandshaker(config.Logger)
 | 
				
			||||||
	tlsConn, _, err := thx.Handshake(ctx, conn, tlsConfig)
 | 
						tlsConn, _, err := thx.Handshake(ctx, conn, tlsConfig)
 | 
				
			||||||
 | 
						ol.Stop(err)
 | 
				
			||||||
	out.TLS = &ctrlTLSResult{
 | 
						out.TLS = &ctrlTLSResult{
 | 
				
			||||||
		ServerName: config.URLHostname,
 | 
							ServerName: config.URLHostname,
 | 
				
			||||||
		Status:     err == nil,
 | 
							Status:     err == nil,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user