summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Meyer <[email protected]>2023-02-15 14:17:00 -0800
committerRandall Meyer <[email protected]>2023-02-22 09:18:25 -0800
commitbfa2e2b0fa93b6059fba0581b52d6d60a53b5a4a (patch)
treeff8a9707caf844be368106ee8000ce7c6e0b57db
parentaba993ed378297f48ff6be18b17c6a963d3fd190 (diff)
new flag: --connect-to
Allows user to override DNS for the initial config request. This is accomplished using a custom DialContext overring the hostname used to Dial to. This allows for TLS certificate validation to still happen(optionally) while connecting to TLS secured resources. Also, - allows for optional enforcement of certificate verification - stamp built git version into binary and adds a --version option - adds a user-agent to all outgoing request - exit(1) on failures for easier shell error detection
-rw-r--r--Makefile6
-rw-r--r--config/config.go62
-rw-r--r--constants/constants.go2
-rw-r--r--lgc/lgc.go109
-rw-r--r--networkQuality.go55
-rw-r--r--rpm/rpm.go22
-rw-r--r--utilities/transport.go46
-rw-r--r--utilities/utilities.go12
8 files changed, 188 insertions, 126 deletions
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 f223ec5..5c4fb8d 100644
--- a/config/config.go
+++ b/config/config.go
@@ -24,53 +24,59 @@ import (
"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)
req, err := http.NewRequest("GET", c.Source, nil)
if err != nil {
return fmt.Errorf(
- "Error: Could not create request for configuration host %s: %v\n",
+ "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,
)
@@ -79,7 +85,7 @@ func (c *Config) Get(configHost string, configPath string, keyLogger io.Writer)
if resp.StatusCode != 200 {
return fmt.Errorf(
- "Error: Configuration host %s returned %d for config request\n",
+ "Error: Configuration host %s returned %d for config request",
configHost,
resp.StatusCode,
)
@@ -88,7 +94,7 @@ func (c *Config) Get(configHost string, configPath string, keyLogger io.Writer)
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,
)
@@ -103,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
}
@@ -133,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/lgc/lgc.go b/lgc/lgc.go
index 1597060..0b3f075 100644
--- a/lgc/lgc.go
+++ b/lgc/lgc.go
@@ -29,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 {
@@ -53,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(
@@ -257,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) {
@@ -276,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)
@@ -309,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
@@ -355,17 +352,18 @@ 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 {
@@ -416,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
@@ -460,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) {
@@ -473,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 5d9cea4..ef7543d 100644
--- a/networkQuality.go
+++ b/networkQuality.go
@@ -95,16 +95,36 @@ 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)
@@ -143,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 {
@@ -176,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(
@@ -187,7 +209,7 @@ func main() {
config.Source,
err,
)
- return
+ os.Exit(1)
}
if debug.IsDebug(debugLevel) {
fmt.Printf("Configuration: %s\n", config)
@@ -219,7 +241,7 @@ func main() {
*profile,
err,
)
- return
+ os.Exit(1)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
@@ -332,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,
}
}
diff --git a/rpm/rpm.go b/rpm/rpm.go
index ece0082..3774c8f 100644
--- a/rpm/rpm.go
+++ b/rpm/rpm.go
@@ -32,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(
@@ -61,8 +60,10 @@ func addFlows(
}
type ProbeConfiguration struct {
- URL string
- Host string
+ ConnectToAddr string
+ URL string
+ Host string
+ InsecureSkipVerify bool
}
type ProbeDataPoint struct {
@@ -157,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 {
@@ -292,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) {
@@ -311,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/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 57b4a90..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
@@ -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)
+}