diff options
| author | Will Hawkins <[email protected]> | 2023-02-22 19:17:09 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-02-22 19:17:09 -0800 |
| commit | e59ddfd3672750351f80fb35c804827b2f726642 (patch) | |
| tree | ff8a9707caf844be368106ee8000ce7c6e0b57db | |
| parent | ad8b4dac1ad3500904747a090de2d99f2f774156 (diff) | |
| parent | bfa2e2b0fa93b6059fba0581b52d6d60a53b5a4a (diff) | |
Merge pull request #44 from network-quality/connect_to_secure_merged
A grab bag of changes
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | config/config.go | 81 | ||||
| -rw-r--r-- | constants/constants.go | 2 | ||||
| -rw-r--r-- | datalogger/logger.go | 4 | ||||
| -rw-r--r-- | extendedstats/darwin.go | 22 | ||||
| -rw-r--r-- | extendedstats/other.go | 2 | ||||
| -rw-r--r-- | go.mod | 8 | ||||
| -rw-r--r-- | go.sum | 18 | ||||
| -rw-r--r-- | lgc/lgc.go | 122 | ||||
| -rw-r--r-- | networkQuality.go | 106 | ||||
| -rw-r--r-- | rpm/rpm.go | 23 | ||||
| -rw-r--r-- | traceable/traceable_test.go | 5 | ||||
| -rw-r--r-- | utilities/transport.go | 46 | ||||
| -rw-r--r-- | utilities/utilities.go | 16 |
15 files changed, 290 insertions, 173 deletions
@@ -1 +1 @@ -networkQuality
\ No newline at end of file +/networkQuality @@ -1,6 +1,10 @@ +PKG := github.com/network-quality/goresponsiveness +GIT_VERSION := $(shell git describe --always --long) +LDFLAGS := -ldflags "-X $(PKG)/utilities.GitVersion=$(GIT_VERSION)" + all: build test build: - go build networkQuality.go + go build $(LDFLAGS) networkQuality.go test: go test ./timeoutat/ ./traceable/ ./ms/ ./utilities/ golines: diff --git a/config/config.go b/config/config.go index 8d9eaa5..5c4fb8d 100644 --- a/config/config.go +++ b/config/config.go @@ -19,59 +19,82 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "strings" "github.com/network-quality/goresponsiveness/utilities" - "golang.org/x/net/http2" ) type ConfigUrls struct { - SmallUrl string `json:"small_https_download_url"` - SmallUrlHost string - LargeUrl string `json:"large_https_download_url"` - LargeUrlHost string - UploadUrl string `json:"https_upload_url"` - UploadUrlHost string + SmallUrl string `json:"small_https_download_url"` + LargeUrl string `json:"large_https_download_url"` + UploadUrl string `json:"https_upload_url"` } type Config struct { Version int Urls ConfigUrls `json:"urls"` Source string - Test_Endpoint string + ConnectToAddr string `json:"test_endpoint"` } -func (c *Config) Get(configHost string, configPath string, keyLogger io.Writer) error { - configTransport := http2.Transport{} - configTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - +func (c *Config) Get(configHost string, configPath string, insecureSkipVerify bool, keyLogger io.Writer) error { + configTransport := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: insecureSkipVerify, + }, + Proxy: http.ProxyFromEnvironment, + } if !utilities.IsInterfaceNil(keyLogger) { configTransport.TLSClientConfig.KeyLogWriter = keyLogger } - configClient := &http.Client{Transport: &configTransport} + + utilities.OverrideHostTransport(configTransport, c.ConnectToAddr) + + configClient := &http.Client{Transport: configTransport} + // Extraneous /s in URLs is normally okay, but the Apple CDN does not // like them. Make sure that we put exactly one (1) / between the host // and the path. if !strings.HasPrefix(configPath, "/") { configPath = "/" + configPath } + c.Source = fmt.Sprintf("https://%s%s", configHost, configPath) - resp, err := configClient.Get(c.Source) + req, err := http.NewRequest("GET", c.Source, nil) + if err != nil { + return fmt.Errorf( + "Error: Could not create request for configuration host %s: %v", + configHost, + err, + ) + } + + req.Header.Set("User-Agent", utilities.UserAgent()) + + resp, err := configClient.Do(req) if err != nil { return fmt.Errorf( - "could not connect to configuration host %s: %v", + "Error: could not connect to configuration host %s: %v", configHost, err, ) } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf( + "Error: Configuration host %s returned %d for config request", + configHost, + resp.StatusCode, + ) + } - jsonConfig, err := ioutil.ReadAll(resp.Body) + jsonConfig, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf( - "could not read configuration content downloaded from %s: %v", + "Error: Could not read configuration content downloaded from %s: %v", c.Source, err, ) @@ -86,26 +109,6 @@ func (c *Config) Get(configHost string, configPath string, keyLogger io.Writer) ) } - if len(c.Test_Endpoint) != 0 { - tempUrl, err := url.Parse(c.Urls.LargeUrl) - if err != nil { - return fmt.Errorf("error parsing large_https_download_url: %v", err) - } - c.Urls.LargeUrl = tempUrl.Scheme + "://" + c.Test_Endpoint + "" + tempUrl.Path - c.Urls.LargeUrlHost = tempUrl.Host - tempUrl, err = url.Parse(c.Urls.SmallUrl) - if err != nil { - return fmt.Errorf("error parsing small_https_download_url: %v", err) - } - c.Urls.SmallUrl = tempUrl.Scheme + "://" + c.Test_Endpoint + "" + tempUrl.Path - c.Urls.SmallUrlHost = tempUrl.Host - tempUrl, err = url.Parse(c.Urls.UploadUrl) - if err != nil { - return fmt.Errorf("error parsing https_upload_url: %v", err) - } - c.Urls.UploadUrl = tempUrl.Scheme + "://" + c.Test_Endpoint + "" + tempUrl.Path - c.Urls.UploadUrlHost = tempUrl.Host - } return nil } @@ -116,7 +119,7 @@ func (c *Config) String() string { c.Urls.SmallUrl, c.Urls.LargeUrl, c.Urls.UploadUrl, - c.Test_Endpoint, + c.ConnectToAddr, ) } diff --git a/constants/constants.go b/constants/constants.go index 8320295..66f7110 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -46,4 +46,6 @@ var ( DefaultDebug bool = false // The default URL for the config host. DefaultConfigHost string = "networkquality.example.com" + // The default determination of whether to verify server certificates + DefaultInsecureSkipVerify bool = true ) diff --git a/datalogger/logger.go b/datalogger/logger.go index 249a059..1f9c2d6 100644 --- a/datalogger/logger.go +++ b/datalogger/logger.go @@ -112,13 +112,13 @@ func (logger *CSVDataLogger[T]) Export() bool { visibleFields := reflect.VisibleFields(reflect.TypeOf((*T)(nil)).Elem()) for i, v := range visibleFields { description, success := v.Tag.Lookup("Description") - columnName := fmt.Sprintf("%s", v.Name) + columnName := v.Name if success { if description == "[OMIT]" { toOmit = append(toOmit, i) continue } - columnName = fmt.Sprintf("%s", description) + columnName = description } logger.destination.Write([]byte(fmt.Sprintf("%s, ", columnName))) } diff --git a/extendedstats/darwin.go b/extendedstats/darwin.go index 59d6e38..19c5bc0 100644 --- a/extendedstats/darwin.go +++ b/extendedstats/darwin.go @@ -28,8 +28,6 @@ import ( type AggregateExtendedStats struct { Maxseg uint64 - MaxSendMss uint64 - MaxRecvMss uint64 TotalRetransmissions uint64 totalSent uint64 TotalReorderings uint64 @@ -44,12 +42,8 @@ func ExtendedStatsAvailable() bool { } type TCPInfo struct { - Rxoutoforderbytes uint64 - Txretransmitbytes uint64 - Txbytes uint64 - Rtt uint32 - Maxseg uint32 - Snd_cwnd uint32 + unix.TCPConnectionInfo + Rtt uint32 // Srtt under Darwin } func (es *AggregateExtendedStats) IncorporateConnectionStats(basicConn net.Conn) error { @@ -98,20 +92,18 @@ func GetTCPInfo(basicConn net.Conn) (*TCPInfo, error) { var rawInfo *unix.TCPConnectionInfo = nil var tcpInfo *TCPInfo = nil - rawConn.Control(func(fd uintptr) { + rerr := rawConn.Control(func(fd uintptr) { rawInfo, err = unix.GetsockoptTCPConnectionInfo( int(fd), unix.IPPROTO_TCP, unix.TCP_CONNECTION_INFO, ) }) + if rerr != nil { + return nil, rerr + } if rawInfo != nil && err == nil { - tcpInfo = &TCPInfo{} - tcpInfo.Rxoutoforderbytes = rawInfo.Rxoutoforderbytes - tcpInfo.Txretransmitbytes = rawInfo.Txretransmitbytes - tcpInfo.Rtt = rawInfo.Srtt - tcpInfo.Snd_cwnd = rawInfo.Snd_cwnd - tcpInfo.Maxseg = rawInfo.Maxseg + tcpInfo = &TCPInfo{TCPConnectionInfo: *rawInfo, Rtt: rawInfo.Srtt} } return tcpInfo, err } diff --git a/extendedstats/other.go b/extendedstats/other.go index 2d76eaf..ed6d925 100644 --- a/extendedstats/other.go +++ b/extendedstats/other.go @@ -36,6 +36,6 @@ func ExtendedStatsAvailable() bool { return false } -func GetTCPInfo(basicConn net.Conn) (interface, error) { +func GetTCPInfo(basicConn net.Conn) (interface{}, error) { return nil, fmt.Errorf("GetTCPInfo is not supported on this platform") } @@ -3,11 +3,11 @@ module github.com/network-quality/goresponsiveness go 1.18 require ( - golang.org/x/net v0.0.0-20220225172249-27dd8689420f - golang.org/x/sys v0.1.0 + golang.org/x/net v0.7.0 + golang.org/x/sys v0.5.0 ) require ( - golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 - golang.org/x/text v0.3.7 // indirect + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb + golang.org/x/text v0.7.0 // indirect ) @@ -1,10 +1,8 @@ -golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE= -golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -19,7 +19,6 @@ import ( "crypto/tls" "fmt" "io" - "io/ioutil" "net/http" "net/http/httptrace" "sync" @@ -30,7 +29,6 @@ import ( "github.com/network-quality/goresponsiveness/stats" "github.com/network-quality/goresponsiveness/traceable" "github.com/network-quality/goresponsiveness/utilities" - "golang.org/x/net/http2" ) type LoadGeneratingConnection interface { @@ -54,19 +52,20 @@ func NewLoadGeneratingConnectionCollection() LoadGeneratingConnectionCollection // TODO: All 64-bit fields that are accessed atomically must // appear at the top of this struct. type LoadGeneratingConnectionDownload struct { - downloaded uint64 - lastIntervalEnd int64 - Path string - Host string - downloadStartTime time.Time - lastDownloaded uint64 - client *http.Client - debug debug.DebugLevel - valid bool - KeyLogger io.Writer - clientId uint64 - tracer *httptrace.ClientTrace - stats stats.TraceStats + downloaded uint64 + lastIntervalEnd int64 + ConnectToAddr string + URL string + downloadStartTime time.Time + lastDownloaded uint64 + client *http.Client + debug debug.DebugLevel + valid bool + InsecureSkipVerify bool + KeyLogger io.Writer + clientId uint64 + tracer *httptrace.ClientTrace + stats stats.TraceStats } func (lgd *LoadGeneratingConnectionDownload) SetDnsStartTimeInfo( @@ -258,8 +257,13 @@ func (lgd *LoadGeneratingConnectionDownload) Start( lgd.downloaded = 0 lgd.debug = debugLevel lgd.clientId = utilities.GenerateUniqueId() - transport := http2.Transport{} - transport.TLSClientConfig = &tls.Config{} + + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: lgd.InsecureSkipVerify, + }, + } if !utilities.IsInterfaceNil(lgd.KeyLogger) { if debug.IsDebug(lgd.debug) { @@ -277,9 +281,11 @@ func (lgd *LoadGeneratingConnectionDownload) Start( // https://github.com/golang/go/blob/7ca6902c171b336d98adbb103d701a013229c806/src/net/http/transport.go#L74 transport.TLSClientConfig.KeyLogWriter = lgd.KeyLogger } - transport.TLSClientConfig.InsecureSkipVerify = true + transport.TLSClientConfig.InsecureSkipVerify = lgd.InsecureSkipVerify + + utilities.OverrideHostTransport(transport, lgd.ConnectToAddr) - lgd.client = &http.Client{Transport: &transport} + lgd.client = &http.Client{Transport: transport} lgd.valid = true lgd.tracer = traceable.GenerateHttpTimingTracer(lgd, lgd.debug) @@ -310,26 +316,16 @@ func (lgd *LoadGeneratingConnectionDownload) doDownload(ctx context.Context) { if request, err = http.NewRequestWithContext( httptrace.WithClientTrace(ctx, lgd.tracer), "GET", - lgd.Path, + lgd.URL, nil, ); err != nil { lgd.valid = false return } - // To support test_endpoint - if len(lgd.Host) != 0 { - if debug.IsDebug(lgd.debug) { - fmt.Printf( - "Because of a test_endpoint in the config, there is a special Host set for this connection: %s\n", - lgd.Host, - ) - } - request.Host = lgd.Host - } - // Used to disable compression request.Header.Set("Accept-Encoding", "identity") + request.Header.Set("User-Agent", utilities.UserAgent()) lgd.downloadStartTime = time.Now() lgd.lastIntervalEnd = 0 @@ -346,7 +342,7 @@ func (lgd *LoadGeneratingConnectionDownload) doDownload(ctx context.Context) { return } cr := &countingReader{n: &lgd.downloaded, ctx: ctx, readable: get.Body} - _, _ = io.Copy(ioutil.Discard, cr) + _, _ = io.Copy(io.Discard, cr) get.Body.Close() if debug.IsDebug(lgd.debug) { fmt.Printf("Ending a load-generating download.\n") @@ -356,29 +352,30 @@ func (lgd *LoadGeneratingConnectionDownload) doDownload(ctx context.Context) { // TODO: All 64-bit fields that are accessed atomically must // appear at the top of this struct. type LoadGeneratingConnectionUpload struct { - uploaded uint64 - lastIntervalEnd int64 - Path string - Host string - uploadStartTime time.Time - lastUploaded uint64 - client *http.Client - debug debug.DebugLevel - valid bool - KeyLogger io.Writer - clientId uint64 + uploaded uint64 + lastIntervalEnd int64 + URL string + ConnectToAddr string + uploadStartTime time.Time + lastUploaded uint64 + client *http.Client + debug debug.DebugLevel + valid bool + InsecureSkipVerify bool + KeyLogger io.Writer + clientId uint64 } func (lgu *LoadGeneratingConnectionUpload) ClientId() uint64 { return lgu.clientId } -func (lgd *LoadGeneratingConnectionUpload) TransferredInInterval() (uint64, time.Duration) { - transferred := atomic.SwapUint64(&lgd.uploaded, 0) - newIntervalEnd := (time.Now().Sub(lgd.uploadStartTime)).Nanoseconds() - previousIntervalEnd := atomic.SwapInt64(&lgd.lastIntervalEnd, newIntervalEnd) +func (lgu *LoadGeneratingConnectionUpload) TransferredInInterval() (uint64, time.Duration) { + transferred := atomic.SwapUint64(&lgu.uploaded, 0) + newIntervalEnd := (time.Now().Sub(lgu.uploadStartTime)).Nanoseconds() + previousIntervalEnd := atomic.SwapInt64(&lgu.lastIntervalEnd, newIntervalEnd) intervalLength := time.Duration(newIntervalEnd - previousIntervalEnd) - if debug.IsDebug(lgd.debug) { + if debug.IsDebug(lgu.debug) { fmt.Printf("upload: Transferred: %v bytes in %v.\n", transferred, intervalLength) } return transferred, intervalLength @@ -417,26 +414,16 @@ func (lgu *LoadGeneratingConnectionUpload) doUpload(ctx context.Context) bool { if request, err = http.NewRequest( "POST", - lgu.Path, + lgu.URL, s, ); err != nil { lgu.valid = false return false } - // To support test_endpoint - if len(lgu.Host) != 0 { - if debug.IsDebug(lgu.debug) { - fmt.Printf( - "Because of a test_endpoint in the config, there is a special Host set for this connection: %s\n", - lgu.Host, - ) - } - request.Host = lgu.Host - } - // Used to disable compression request.Header.Set("Accept-Encoding", "identity") + request.Header.Set("User-Agent", utilities.UserAgent()) lgu.uploadStartTime = time.Now() lgu.lastIntervalEnd = 0 @@ -461,10 +448,12 @@ func (lgu *LoadGeneratingConnectionUpload) Start( lgu.clientId = utilities.GenerateUniqueId() lgu.debug = debugLevel - // See above for the rationale of doing http2.Transport{} here - // to ensure that we are using h2. - transport := http2.Transport{} - transport.TLSClientConfig = &tls.Config{} + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: lgu.InsecureSkipVerify, + }, + } if !utilities.IsInterfaceNil(lgu.KeyLogger) { if debug.IsDebug(lgu.debug) { @@ -474,9 +463,10 @@ func (lgu *LoadGeneratingConnectionUpload) Start( } transport.TLSClientConfig.KeyLogWriter = lgu.KeyLogger } - transport.TLSClientConfig.InsecureSkipVerify = true - lgu.client = &http.Client{Transport: &transport} + utilities.OverrideHostTransport(transport, lgu.ConnectToAddr) + + lgu.client = &http.Client{Transport: transport} lgu.valid = true if debug.IsDebug(lgu.debug) { diff --git a/networkQuality.go b/networkQuality.go index 1af90bb..ef7543d 100644 --- a/networkQuality.go +++ b/networkQuality.go @@ -15,9 +15,11 @@ package main import ( + "bytes" "context" "flag" "fmt" + "net/url" "os" "runtime/pprof" "time" @@ -53,6 +55,11 @@ var ( "config", "path on the server to the configuration endpoint.", ) + configURL = flag.String( + "url", + "", + "configuration URL (takes precedence over other configuration parts)", + ) debugCliFlag = flag.Bool( "debug", constants.DefaultDebug, @@ -88,14 +95,56 @@ var ( 100, "Time (in ms) between probes (foreign and self).", ) + connectToAddr = flag.String( + "connect-to", + "", + "address (hostname or IP) to connect to (overriding DNS). Disabled by default.", + ) + insecureSkipVerify = flag.Bool( + "insecure-skip-verify", + constants.DefaultInsecureSkipVerify, + "Enable server certificate validation.", + ) + prometheusStatsFilename = flag.String( + "prometheus-stats-filename", + "", + "If filename specified, prometheus stats will be written. If specified file exists, it will be overwritten.", + ) + showVersion = flag.Bool( + "version", + false, + "Show version.", + ) ) func main() { flag.Parse() + if *showVersion { + fmt.Fprintf(os.Stdout, "goresponsiveness %s\n", utilities.GitVersion) + os.Exit(0) + } + timeoutDuration := time.Second * time.Duration(*rpmtimeout) timeoutAbsoluteTime := time.Now().Add(timeoutDuration) - configHostPort := fmt.Sprintf("%s:%d", *configHost, *configPort) + + var configHostPort string + + // if user specified a full URL, use that and set the various parts we need out of it + if len(*configURL) > 0 { + parsedURL, err := url.ParseRequestURI(*configURL) + if err != nil { + fmt.Printf("Error: Could not parse %q: %s", *configURL, err) + os.Exit(1) + } + + *configHost = parsedURL.Hostname() + *configPath = parsedURL.Path + // We don't explicitly care about configuring the *configPort. + configHostPort = parsedURL.Host // host or host:port + } else { + configHostPort = fmt.Sprintf("%s:%d", *configHost, *configPort) + } // This is the overall operating context of the program. All other // contexts descend from this one. Canceling this one cancels all @@ -114,7 +163,9 @@ func main() { // all the network connections that are responsible for generating the load. networkActivityCtx, networkActivityCtxCancel := context.WithCancel(operatingCtx) - config := &config.Config{} + config := &config.Config{ + ConnectToAddr: *connectToAddr, + } var debugLevel debug.DebugLevel = debug.Error if *debugCliFlag { @@ -147,9 +198,9 @@ func main() { } } - if err := config.Get(configHostPort, *configPath, sslKeyFileConcurrentWriter); err != nil { + if err := config.Get(configHostPort, *configPath, *insecureSkipVerify, sslKeyFileConcurrentWriter); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) - return + os.Exit(1) } if err := config.IsValid(); err != nil { fmt.Fprintf( @@ -158,7 +209,7 @@ func main() { config.Source, err, ) - return + os.Exit(1) } if debug.IsDebug(debugLevel) { fmt.Printf("Configuration: %s\n", config) @@ -190,7 +241,7 @@ func main() { *profile, err, ) - return + os.Exit(1) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() @@ -303,31 +354,34 @@ func main() { */ generate_lgd := func() lgc.LoadGeneratingConnection { return &lgc.LoadGeneratingConnectionDownload{ - Path: config.Urls.LargeUrl, - Host: config.Urls.LargeUrlHost, - KeyLogger: sslKeyFileConcurrentWriter, + URL: config.Urls.LargeUrl, + KeyLogger: sslKeyFileConcurrentWriter, + ConnectToAddr: config.ConnectToAddr, + InsecureSkipVerify: *insecureSkipVerify, } } generate_lgu := func() lgc.LoadGeneratingConnection { return &lgc.LoadGeneratingConnectionUpload{ - Path: config.Urls.UploadUrl, - Host: config.Urls.UploadUrlHost, - KeyLogger: sslKeyFileConcurrentWriter, + URL: config.Urls.UploadUrl, + KeyLogger: sslKeyFileConcurrentWriter, + ConnectToAddr: config.ConnectToAddr, } } generateSelfProbeConfiguration := func() rpm.ProbeConfiguration { return rpm.ProbeConfiguration{ - URL: config.Urls.SmallUrl, - Host: config.Urls.SmallUrlHost, + URL: config.Urls.SmallUrl, + ConnectToAddr: config.ConnectToAddr, + InsecureSkipVerify: *insecureSkipVerify, } } generateForeignProbeConfiguration := func() rpm.ProbeConfiguration { return rpm.ProbeConfiguration{ - URL: config.Urls.SmallUrl, - Host: config.Urls.SmallUrlHost, + URL: config.Urls.SmallUrl, + ConnectToAddr: config.ConnectToAddr, + InsecureSkipVerify: *insecureSkipVerify, } } @@ -681,4 +735,24 @@ Trimmed Mean Foreign RTT: %f fmt.Printf("Done cooling down.\n") } + if len(*prometheusStatsFilename) > 0 { + var testStable int + if testRanToStability { + testStable = 1 + } + var buffer bytes.Buffer + buffer.WriteString(fmt.Sprintf("networkquality_test_stable %d\n", testStable)) + buffer.WriteString(fmt.Sprintf("networkquality_rpm_value %d\n", int64(p90Rpm))) + buffer.WriteString(fmt.Sprintf("networkquality_trimmed_rpm_value %d\n", int64(meanRpm))) //utilities.ToMbps(lastDownloadThroughputRate), + + buffer.WriteString(fmt.Sprintf("networkquality_download_bits_per_second %d\n", int64(lastDownloadThroughputRate))) + buffer.WriteString(fmt.Sprintf("networkquality_download_connections %d\n", int64(lastDownloadThroughputOpenConnectionCount))) + buffer.WriteString(fmt.Sprintf("networkquality_upload_bits_per_second %d\n", int64(lastUploadThroughputRate))) + buffer.WriteString(fmt.Sprintf("networkquality_upload_connections %d\n", lastUploadThroughputOpenConnectionCount)) + + if err := os.WriteFile(*prometheusStatsFilename, buffer.Bytes(), 0644); err != nil { + fmt.Printf("could not write %s: %s", *prometheusStatsFilename, err) + os.Exit(1) + } + } } @@ -11,6 +11,7 @@ * You should have received a copy of the GNU General Public License along * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>. */ + package rpm import ( @@ -31,7 +32,6 @@ import ( "github.com/network-quality/goresponsiveness/stats" "github.com/network-quality/goresponsiveness/traceable" "github.com/network-quality/goresponsiveness/utilities" - "golang.org/x/net/http2" ) func addFlows( @@ -60,8 +60,10 @@ func addFlows( } type ProbeConfiguration struct { - URL string - Host string + ConnectToAddr string + URL string + Host string + InsecureSkipVerify bool } type ProbeDataPoint struct { @@ -156,12 +158,9 @@ func Probe( return err } - // To support test_endpoint - if len(probeHost) != 0 { - probe_req.Host = probeHost - } // Used to disable compression probe_req.Header.Set("Accept-Encoding", "identity") + probe_req.Header.Set("User-Agent", utilities.UserAgent()) probe_resp, err := client.Do(probe_req) if err != nil { @@ -291,8 +290,9 @@ func CombinedProber( probeCount+1, ) } - transport := http2.Transport{} + transport := &http.Transport{} transport.TLSClientConfig = &tls.Config{} + transport.Proxy = http.ProxyFromEnvironment if !utilities.IsInterfaceNil(keyLogger) { if debug.IsDebug(debugging.Level) { @@ -310,9 +310,12 @@ func CombinedProber( // https://github.com/golang/go/blob/7ca6902c171b336d98adbb103d701a013229c806/src/net/http/transport.go#L74 transport.TLSClientConfig.KeyLogWriter = keyLogger } - transport.TLSClientConfig.InsecureSkipVerify = true - foreignProbeClient := &http.Client{Transport: &transport} + transport.TLSClientConfig.InsecureSkipVerify = foreignProbeConfiguration.InsecureSkipVerify + + utilities.OverrideHostTransport(transport, foreignProbeConfiguration.ConnectToAddr) + + foreignProbeClient := &http.Client{Transport: transport} // Start Foreign Connection Prober probeCount++ diff --git a/traceable/traceable_test.go b/traceable/traceable_test.go index 1ba6f51..e9d5a74 100644 --- a/traceable/traceable_test.go +++ b/traceable/traceable_test.go @@ -18,7 +18,6 @@ import ( "context" "crypto/tls" "io" - "io/ioutil" "net/http" "net/http/httptrace" "sync" @@ -141,7 +140,7 @@ func TestDuplicativeTraceables(t *testing.T) { if err != nil { return } - _, _ = io.Copy(ioutil.Discard, get.Body) + _, _ = io.Copy(io.Discard, get.Body) get.Body.Close() }() go func() { @@ -150,7 +149,7 @@ func TestDuplicativeTraceables(t *testing.T) { if err != nil { return } - _, _ = io.Copy(ioutil.Discard, get.Body) + _, _ = io.Copy(io.Discard, get.Body) get.Body.Close() }() diff --git a/utilities/transport.go b/utilities/transport.go new file mode 100644 index 0000000..2d70989 --- /dev/null +++ b/utilities/transport.go @@ -0,0 +1,46 @@ +/* + * This file is part of Go Responsiveness. + * + * Go Responsiveness is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software Foundation, + * either version 2 of the License, or (at your option) any later version. + * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>. + */ + +package utilities + +import ( + "context" + "net" + "net/http" + "time" + + "golang.org/x/net/http2" +) + +func OverrideHostTransport(transport *http.Transport, connectToAddr string) { + dialer := &net.Dialer{ + Timeout: 10 * time.Second, + } + + transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + _, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + if len(connectToAddr) > 0 { + addr = net.JoinHostPort(connectToAddr, port) + } + + return dialer.DialContext(ctx, network, addr) + } + + http2.ConfigureTransport(transport) + +} diff --git a/utilities/utilities.go b/utilities/utilities.go index 538889c..377be56 100644 --- a/utilities/utilities.go +++ b/utilities/utilities.go @@ -28,6 +28,11 @@ import ( "golang.org/x/exp/constraints" ) +var ( + // GitVersion is the Git revision hash + GitVersion = "dev" +) + func Iota(low int, high int) (made []int) { made = make([]int, high-low) @@ -46,9 +51,6 @@ func SignedPercentDifference[T constraints.Float | constraints.Integer]( current T, previous T, ) (difference float64) { - //return ((current - previous) / (float64(current+previous) / 2.0)) * float64( - //100, - // ) fCurrent := float64(current) fPrevious := float64(previous) return ((fCurrent - fPrevious) / fPrevious) * 100.0 @@ -180,9 +182,9 @@ func OrTimeout(f func(), timeout time.Duration) { return completed }() select { - case _ = <-completeChannel: + case <-completeChannel: break - case _ = <-time.After(timeout): + case <-time.After(timeout): break } } @@ -203,3 +205,7 @@ func ApproximatelyEqual[T float32 | float64](truth T, maybe T, fudge T) bool { diff := math.Abs((bTruth - bMaybe)) return diff < bFudge } + +func UserAgent() string { + return fmt.Sprintf("goresponsiveness/%s", GitVersion) +} |
