summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile6
-rw-r--r--config/config.go81
-rw-r--r--constants/constants.go2
-rw-r--r--datalogger/logger.go4
-rw-r--r--extendedstats/darwin.go22
-rw-r--r--extendedstats/other.go2
-rw-r--r--go.mod8
-rw-r--r--go.sum18
-rw-r--r--lgc/lgc.go122
-rw-r--r--networkQuality.go106
-rw-r--r--rpm/rpm.go23
-rw-r--r--traceable/traceable_test.go5
-rw-r--r--utilities/transport.go46
-rw-r--r--utilities/utilities.go16
15 files changed, 290 insertions, 173 deletions
diff --git a/.gitignore b/.gitignore
index 1a990f8..9449fc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-networkQuality \ No newline at end of file
+/networkQuality
diff --git a/Makefile b/Makefile
index f2df1c5..4a3db5c 100644
--- a/Makefile
+++ b/Makefile
@@ -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")
}
diff --git a/go.mod b/go.mod
index 3956ad9..a448c75 100644
--- a/go.mod
+++ b/go.mod
@@ -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
)
diff --git a/go.sum b/go.sum
index c642a0b..3a4ddff 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/lgc/lgc.go b/lgc/lgc.go
index bbab5ad..0b3f075 100644
--- a/lgc/lgc.go
+++ b/lgc/lgc.go
@@ -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)
+ }
+ }
}
diff --git a/rpm/rpm.go b/rpm/rpm.go
index d5aad20..3774c8f 100644
--- a/rpm/rpm.go
+++ b/rpm/rpm.go
@@ -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)
+}