github.com/network-quality/goresponsiveness@v0.0.0-20240129151524-343954285090/traceable/traceable_test.go (about)

     1  /*
     2   * This file is part of Go Responsiveness.
     3   *
     4   * Go Responsiveness is free software: you can redistribute it and/or modify it under
     5   * the terms of the GNU General Public License as published by the Free Software Foundation,
     6   * either version 2 of the License, or (at your option) any later version.
     7   * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY
     8   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
     9   * PARTICULAR PURPOSE. See the GNU General Public License for more details.
    10   *
    11   * You should have received a copy of the GNU General Public License along
    12   * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>.
    13   */
    14  
    15  package traceable
    16  
    17  import (
    18  	"context"
    19  	"crypto/tls"
    20  	"io"
    21  	"net/http"
    22  	"net/http/httptrace"
    23  	"sync"
    24  	"sync/atomic"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/network-quality/goresponsiveness/debug"
    29  )
    30  
    31  type CountingTraceable struct {
    32  	Counter *uint64
    33  }
    34  
    35  func (lgd *CountingTraceable) SetDnsStartTimeInfo(
    36  	now time.Time,
    37  	dnsStartInfo httptrace.DNSStartInfo,
    38  ) {
    39  
    40  }
    41  
    42  func (lgd *CountingTraceable) SetDnsDoneTimeInfo(
    43  	now time.Time,
    44  	dnsDoneInfo httptrace.DNSDoneInfo,
    45  ) {
    46  
    47  }
    48  
    49  func (lgd *CountingTraceable) SetConnectStartTime(
    50  	now time.Time,
    51  ) {
    52  
    53  }
    54  
    55  func (lgd *CountingTraceable) SetConnectDoneTimeError(
    56  	now time.Time,
    57  	err error,
    58  ) {
    59  
    60  }
    61  
    62  func (lgd *CountingTraceable) SetGetConnTime(now time.Time) {
    63  }
    64  
    65  func (lgd *CountingTraceable) SetGotConnTimeInfo(
    66  	now time.Time,
    67  	gotConnInfo httptrace.GotConnInfo,
    68  ) {
    69  	atomic.AddUint64(lgd.Counter, 1)
    70  }
    71  
    72  func (lgd *CountingTraceable) SetTLSHandshakeStartTime(
    73  	now time.Time,
    74  ) {
    75  }
    76  
    77  func (lgd *CountingTraceable) SetTLSHandshakeDoneTimeState(
    78  	now time.Time,
    79  	connectionState tls.ConnectionState,
    80  ) {
    81  }
    82  
    83  func (lgd *CountingTraceable) SetHttpWroteRequestTimeInfo(
    84  	now time.Time,
    85  	info httptrace.WroteRequestInfo,
    86  ) {
    87  }
    88  
    89  func (lgd *CountingTraceable) SetHttpResponseReadyTime(
    90  	now time.Time,
    91  ) {
    92  }
    93  
    94  // Ensure that two different http client request tracers started with the same context
    95  // do not compose and receive each other's callbacks.
    96  func TestDuplicativeTraceables(t *testing.T) {
    97  
    98  	singleCtx, singleCtxCancel_ := context.WithCancel(context.Background())
    99  	defer singleCtxCancel_()
   100  
   101  	client := http.Client{}
   102  
   103  	counterA := new(uint64)
   104  	countingTracerA := CountingTraceable{Counter: counterA}
   105  	counterB := new(uint64)
   106  	countingTracerB := CountingTraceable{Counter: counterB}
   107  
   108  	debugging := debug.NewDebugWithPrefix(debug.Debug, "TestDuplicativeTraceables")
   109  	request_a, err := http.NewRequestWithContext(
   110  		httptrace.WithClientTrace(
   111  			singleCtx,
   112  			GenerateHttpTimingTracer(&countingTracerA, debugging.Level),
   113  		),
   114  		"GET",
   115  		"https://www.google.com/",
   116  		nil,
   117  	)
   118  	if err != nil {
   119  		t.Fatalf("Failed to create request A to GET google.com")
   120  	}
   121  	request_b, err := http.NewRequestWithContext(
   122  		httptrace.WithClientTrace(
   123  			singleCtx,
   124  			GenerateHttpTimingTracer(&countingTracerB, debugging.Level),
   125  		),
   126  		"GET",
   127  		"https://www.google.com/",
   128  		nil,
   129  	)
   130  	if err != nil {
   131  		t.Fatalf("Failed to create request B to GET google.com")
   132  	}
   133  
   134  	requestWg := new(sync.WaitGroup)
   135  
   136  	requestWg.Add(2)
   137  	go func() {
   138  		defer requestWg.Done()
   139  		get, err := client.Do(request_a)
   140  		if err != nil {
   141  			return
   142  		}
   143  		_, _ = io.Copy(io.Discard, get.Body)
   144  		get.Body.Close()
   145  	}()
   146  	go func() {
   147  		defer requestWg.Done()
   148  		get, err := client.Do(request_b)
   149  		if err != nil {
   150  			return
   151  		}
   152  		_, _ = io.Copy(io.Discard, get.Body)
   153  		get.Body.Close()
   154  	}()
   155  
   156  	requestWg.Wait()
   157  
   158  	if !(*counterA == 1 && *counterB == 1) {
   159  		t.Fatalf(
   160  			"Two separate tracers received overlapping callbacks on each other's http requests: (counterA: %d, counterB: %d)",
   161  			*counterA,
   162  			*counterB,
   163  		)
   164  	}
   165  
   166  }