Repository: fortio/fortio (go)

Issue:
bad url in the UI gives unhelpful error message
`Aborting because of unable to create client 0 for http://does.not.exist.fortio.org/`
with the dns etc error in the logs but not on the page



Pull request:
propagate error back to client
fixes #411

Patch:
diff --git a/bincommon/commonflags.go b/bincommon/commonflags.go
index fbec7a6..4ab58e3 100644
--- a/bincommon/commonflags.go
+++ b/bincommon/commonflags.go
@@ -120,7 +120,7 @@ func SharedMain(usage func(io.Writer, ...interface{})) {
 func FetchURL(o *fhttp.HTTPOptions) {
 	// keepAlive could be just false when making 1 fetch but it helps debugging
 	// the http client when making a single request if using the flags
-	client := fhttp.NewClient(o)
+	client, _ := fhttp.NewClient(o)
 	if client == nil {
 		return // error logged already
 	}
diff --git a/fhttp/http_client.go b/fhttp/http_client.go
index bac55b5..f732e4a 100644
--- a/fhttp/http_client.go
+++ b/fhttp/http_client.go
@@ -260,7 +260,7 @@ func (h *HTTPOptions) AddAndValidateExtraHeader(hdr string) error {
 }
 
 // newHttpRequest makes a new http GET request for url with User-Agent.
-func newHTTPRequest(o *HTTPOptions) *http.Request {
+func newHTTPRequest(o *HTTPOptions) (*http.Request, error) {
 	method := o.Method()
 	var body io.Reader
 	if method == fnet.POST {
@@ -270,14 +270,14 @@ func newHTTPRequest(o *HTTPOptions) *http.Request {
 	req, err := http.NewRequest(method, o.URL, body)
 	if err != nil {
 		log.Errf("Unable to make %s request for %s : %v", method, o.URL, err)
-		return nil
+		return nil, err
 	}
 	req.Header = o.GenerateHeaders()
 	if o.hostOverride != "" {
 		req.Host = o.hostOverride
 	}
 	if !log.LogDebug() {
-		return req
+		return req, nil
 	}
 	bytes, err := httputil.DumpRequestOut(req, false)
 	if err != nil {
@@ -285,7 +285,7 @@ func newHTTPRequest(o *HTTPOptions) *http.Request {
 	} else {
 		log.Debugf("For URL %s, sending:\n%s", o.URL, bytes)
 	}
-	return req
+	return req, nil
 }
 
 // Client object for making repeated requests of the same URL using the same
@@ -355,7 +355,7 @@ func (c *Client) Fetch() (int, []byte, int) {
 
 // NewClient creates either a standard or fast client (depending on
 // the DisableFastClient flag).
-func NewClient(o *HTTPOptions) Fetcher {
+func NewClient(o *HTTPOptions) (Fetcher, error) {
 	o.Init(o.URL) // For completely new options
 	// For changes to options after init
 	o.URLSchemeCheck()
@@ -366,11 +366,11 @@ func NewClient(o *HTTPOptions) Fetcher {
 }
 
 // NewStdClient creates a client object that wraps the net/http standard client.
-func NewStdClient(o *HTTPOptions) *Client {
+func NewStdClient(o *HTTPOptions) (*Client, error) {
 	o.Init(o.URL) // also normalizes NumConnections etc to be valid.
-	req := newHTTPRequest(o)
+	req, err := newHTTPRequest(o)
 	if req == nil {
-		return nil
+		return nil, err
 	}
 	tr := http.Transport{
 		MaxIdleConns:        o.NumConnections,
@@ -408,7 +408,7 @@ func NewStdClient(o *HTTPOptions) *Client {
 			return http.ErrUseLastResponse
 		}
 	}
-	return &client
+	return &client, nil
 }
 
 // FetchURL fetches the data at the given url using the standard client and default options.
@@ -425,7 +425,7 @@ func FetchURL(url string) (int, []byte) {
 // Fetch creates a client an performs a fetch according to the http options passed in.
 // To be used only for single fetches or when performance doesn't matter as the client is closed at the end.
 func Fetch(httpOptions *HTTPOptions) (int, []byte) {
-	cli := NewClient(httpOptions)
+	cli, _ := NewClient(httpOptions)
 	code, data, _ := cli.Fetch()
 	cli.Close()
 	return code, data
@@ -468,7 +468,7 @@ func (c *FastClient) Close() int {
 // NewFastClient makes a basic, efficient http 1.0/1.1 client.
 // This function itself doesn't need to be super efficient as it is created at
 // the beginning and then reused many times.
-func NewFastClient(o *HTTPOptions) Fetcher {
+func NewFastClient(o *HTTPOptions) (Fetcher, error) {
 	method := o.Method()
 	payloadLen := len(o.Payload)
 	o.Init(o.URL)
@@ -480,11 +480,11 @@ func NewFastClient(o *HTTPOptions) Fetcher {
 	url, err := url.Parse(o.URL)
 	if err != nil {
 		log.Errf("Bad url '%s' : %v", o.URL, err)
-		return nil
+		return nil, err
 	}
 	if url.Scheme != "http" {
 		log.Errf("Only http is supported with the optimized client, use -stdclient for url %s", o.URL)
-		return nil
+		return nil, fmt.Errorf("only http for fast client")
 	}
 	// note: Host includes the port
 	bc := FastClient{
@@ -503,14 +503,15 @@ func NewFastClient(o *HTTPOptions) Fetcher {
 		addr = uds
 	} else {
 		var tAddr *net.TCPAddr // strangely we get a non nil wrap of nil if assigning to addr directly
+		var err error
 		if o.Resolve != "" {
-			tAddr = fnet.Resolve(o.Resolve, bc.port)
+			tAddr, err = fnet.Resolve(o.Resolve, bc.port)
 		} else {
-			tAddr = fnet.Resolve(bc.hostname, bc.port)
+			tAddr, err = fnet.Resolve(bc.hostname, bc.port)
 		}
 		if tAddr == nil {
 			// Error already logged
-			return nil
+			return nil, err
 		}
 		addr = tAddr
 	}
@@ -547,7 +548,7 @@ func NewFastClient(o *HTTPOptions) Fetcher {
 	}
 	bc.req = buf.Bytes()
 	log.Debugf("Created client:\n%+v\n%s", bc.dest, bc.req)
-	return &bc
+	return &bc, nil
 }
 
 // return the result from the state.
diff --git a/fhttp/http_server.go b/fhttp/http_server.go
index 501c89c..8c024ac 100644
--- a/fhttp/http_server.go
+++ b/fhttp/http_server.go
@@ -376,7 +376,7 @@ func FetcherHandler(w http.ResponseWriter, r *http.Request) {
 	opts := NewHTTPOptions("http://" + url)
 	opts.HTTPReqTimeOut = 5 * time.Minute
 	OnBehalfOf(opts, r)
-	client := NewClient(opts)
+	client, _ := NewClient(opts)
 	if client == nil {
 		return // error logged already
 	}
diff --git a/fhttp/httprunner.go b/fhttp/httprunner.go
index c481463..84d005d 100644
--- a/fhttp/httprunner.go
+++ b/fhttp/httprunner.go
@@ -97,9 +97,10 @@ func RunHTTPTest(o *HTTPRunnerOptions) (*HTTPRunnerResults, error) {
 	for i := 0; i < numThreads; i++ {
 		r.Options().Runners[i] = &httpstate[i]
 		// Create a client (and transport) and connect once for each 'thread'
-		httpstate[i].client = NewClient(&o.HTTPOptions)
+		var err error
+		httpstate[i].client, err = NewClient(&o.HTTPOptions)
 		if httpstate[i].client == nil {
-			return nil, fmt.Errorf("unable to create client %d for %s", i, o.URL)
+			return nil, err
 		}
 		if o.Exactly <= 0 {
 			code, data, headerSize := httpstate[i].client.Fetch()
diff --git a/fnet/network.go b/fnet/network.go
index 3a1ed45..0635490 100644
--- a/fnet/network.go
+++ b/fnet/network.go
@@ -159,11 +159,11 @@ func GetPort(lAddr net.Addr) string {
 
 // ResolveDestination returns the TCP address of the "host:port" suitable for net.Dial.
 // nil in case of errors.
-func ResolveDestination(dest string) *net.TCPAddr {
+func ResolveDestination(dest string) (*net.TCPAddr, error) {
 	i := strings.LastIndex(dest, ":") // important so [::1]:port works
 	if i < 0 {
 		log.Errf("Destination '%s' is not host:port format", dest)
-		return nil
+		return nil, fmt.Errorf("destination '%s' is not host:port format", dest)
 	}
 	host := dest[0:i]
 	port := dest[i+1:]
@@ -172,7 +172,7 @@ func ResolveDestination(dest string) *net.TCPAddr {
 
 // Resolve returns the TCP address of the host,port suitable for net.Dial.
 // nil in case of errors.
-func Resolve(host string, port string) *net.TCPAddr {
+func Resolve(host string, port string) (*net.TCPAddr, error) {
 	log.Debugf("Resolve() called with host=%s port=%s", host, port)
 	dest := &net.TCPAddr{}
 	if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
@@ -189,7 +189,7 @@ func Resolve(host string, port string) *net.TCPAddr {
 		addrs, err = net.LookupIP(host)
 		if err != nil {
 			log.Errf("Unable to lookup '%s' : %v", host, err)
-			return nil
+			return nil, err
 		}
 		if len(addrs) > 1 && log.LogDebug() {
 			log.Debugf("Using only the first of the addresses for %s : %v", host, addrs)
@@ -200,9 +200,9 @@ func Resolve(host string, port string) *net.TCPAddr {
 	dest.Port, err = net.LookupPort("tcp", port)
 	if err != nil {
 		log.Errf("Unable to resolve port '%s' : %v", port, err)
-		return nil
+		return nil, err
 	}
-	return dest
+	return dest, nil
 }
 
 // Copy is a debug version of io.Copy without the zero Copy optimizations.
@@ -327,7 +327,8 @@ func Proxy(port string, dest net.Addr) net.Addr {
 // ProxyToDestination opens a proxy from the listenPort (or addr:port or unix domain socket path) and forwards
 // all traffic to destination (host:port).
 func ProxyToDestination(listenPort string, destination string) net.Addr {
-	return Proxy(listenPort, ResolveDestination(destination))
+	addr, _ := ResolveDestination(destination)
+	return Proxy(listenPort, addr)
 }
 
 // NormalizeHostPort generates host:port string for the address or uses localhost instead of [::]
@@ -431,7 +432,10 @@ func SmallReadUntil(r io.Reader, stopByte byte, max int) ([]byte, bool, error) {
 // NetCat connects to the destination and reads from in, sends to the socket, and write what it reads from the socket to out.
 func NetCat(dest string, in io.Reader, out io.Writer, stopOnEOF bool) error {
 	log.Infof("NetCat to %s, stop on eof %v", dest, stopOnEOF)
-	a := ResolveDestination(dest)
+	a, err := ResolveDestination(dest)
+	if a == nil {
+		return err // already logged
+	}
 	d, err := net.DialTCP("tcp", nil, a)
 	if err != nil {
 		log.Errf("Connection error to %q: %v", dest, err)
diff --git a/tcprunner/tcprunner.go b/tcprunner/tcprunner.go
index c4a8aad..e289605 100644
--- a/tcprunner/tcprunner.go
+++ b/tcprunner/tcprunner.go
@@ -105,15 +105,15 @@ func GeneratePayload(t int, i int64) []byte {
 }
 
 // NewTCPClient creates and initialize and returns a client based on the TCPOptions.
-func NewTCPClient(o *TCPOptions) *TCPClient {
+func NewTCPClient(o *TCPOptions) (*TCPClient, error) {
 	c := TCPClient{}
 	d := o.Destination
 	d = strings.TrimPrefix(d, TCPURLPrefix)
 	d = strings.TrimSuffix(d, "/")
 	c.destination = d
-	tAddr := fnet.ResolveDestination(d)
+	tAddr, err := fnet.ResolveDestination(d)
 	if tAddr == nil {
-		return nil
+		return nil, err
 	}
 	c.dest = tAddr
 	c.req = o.Payload
@@ -131,7 +131,7 @@ func NewTCPClient(o *TCPOptions) *TCPClient {
 		log.Warnf("Invalid timeout %v, setting to %v", c.reqTimeout, fhttp.HTTPReqTimeOutDefaultValue)
 		c.reqTimeout = fhttp.HTTPReqTimeOutDefaultValue
 	}
-	return &c
+	return &c, nil
 }
 
 func (c *TCPClient) connect() (net.Conn, error) {
@@ -233,12 +233,13 @@ func RunTCPTest(o *RunnerOptions) (*RunnerResults, error) {
 	}
 	total.Destination = o.Destination
 	tcpstate := make([]RunnerResults, numThreads)
+	var err error
 	for i := 0; i < numThreads; i++ {
 		r.Options().Runners[i] = &tcpstate[i]
 		// Create a client (and transport) and connect once for each 'thread'
-		tcpstate[i].client = NewTCPClient(&o.TCPOptions)
+		tcpstate[i].client, err = NewTCPClient(&o.TCPOptions)
 		if tcpstate[i].client == nil {
-			return nil, fmt.Errorf("unable to create client %d for %s", i, o.Destination)
+			return nil, fmt.Errorf("unable to create client %d for %s: %v", i, o.Destination, err)
 		}
 		tcpstate[i].client.connID = i
 		if o.Exactly <= 0 {
diff --git a/ui/uihandler.go b/ui/uihandler.go
index 830cfc1..2842215 100644
--- a/ui/uihandler.go
+++ b/ui/uihandler.go
@@ -702,7 +702,7 @@ func SyncHandler(w http.ResponseWriter, r *http.Request) {
 	// If we had hundreds of thousands of entry we should stream, parallelize (connection pool)
 	// and not do multiple passes over the same data, but for small tsv this is fine.
 	// use std client to change the url and handle https:
-	client := fhttp.NewStdClient(o)
+	client, _ := fhttp.NewStdClient(o)
 	if client == nil {
 		_, _ = w.Write([]byte("invalid url!<script>setPB(1,1)</script></body></html>\n"))
 		w.WriteHeader(422 /*Unprocessable Entity*/)
diff --git a/version/version.go b/version/version.go
index da0000b..a041c6b 100644
--- a/version/version.go
+++ b/version/version.go
@@ -23,8 +23,8 @@ import (
 
 const (
 	major = 1
-	minor = 9
-	patch = 1
+	minor = 10
+	patch = 0
 
 	debug = false // turn on to debug init()
 )


Test patch:
diff --git a/fhttp/http_test.go b/fhttp/http_test.go
index bd75109..5c51097 100644
--- a/fhttp/http_test.go
+++ b/fhttp/http_test.go
@@ -82,7 +82,7 @@ func TestNewHTTPRequest(t *testing.T) {
 	for _, tst := range tests {
 		o := NewHTTPOptions(tst.url)
 		o.AddAndValidateExtraHeader("Host: www.google.com")
-		r := newHTTPRequest(o)
+		r, _ := newHTTPRequest(o)
 		if tst.ok != (r != nil) {
 			t.Errorf("Got %v, expecting ok %v for url '%s'", r, tst.ok, tst.url)
 		}
@@ -509,7 +509,7 @@ func TestPayloadWithEchoBack(t *testing.T) {
 		opts := NewHTTPOptions(url)
 		opts.DisableFastClient = test.disableFastClient
 		opts.Payload = test.payload
-		cli := NewClient(opts)
+		cli, _ := NewClient(opts)
 		code, body, header := cli.Fetch()
 		if code != 200 {
 			t.Errorf("Unexpected error %d", code)
@@ -532,7 +532,7 @@ func TestUnixDomainHttp(t *testing.T) {
 		t.Fatalf("Error for Serve for %s", uds)
 	}
 	o := HTTPOptions{UnixDomainSocket: uds, URL: "http://foo.bar:123/debug1"}
-	client := NewClient(&o)
+	client, _ := NewClient(&o)
 	code, data, _ := client.Fetch()
 	if code != http.StatusOK {
 		t.Errorf("Got error %d fetching uds %s", code, uds)
@@ -570,7 +570,7 @@ func TestH10Cli(t *testing.T) {
 	opts := NewHTTPOptions(url)
 	opts.HTTP10 = true
 	opts.AddAndValidateExtraHeader("Host: mhostname")
-	cli := NewFastClient(opts)
+	cli, _ := NewFastClient(opts)
 	code, _, _ := cli.Fetch()
 	if code != 200 {
 		t.Errorf("http 1.0 unexpected error %d", code)
@@ -589,7 +589,7 @@ func TestSmallBufferAndNoKeepAlive(t *testing.T) {
 	sz := BufferSizeKb * 1024
 	url := fmt.Sprintf("http://localhost:%d/?size=%d", a.Port, sz+1) // trigger buffer problem
 	opts := NewHTTPOptions(url)
-	cli := NewFastClient(opts)
+	cli, _ := NewFastClient(opts)
 	_, data, _ := cli.Fetch()
 	recSz := len(data)
 	if recSz > sz {
@@ -598,7 +598,7 @@ func TestSmallBufferAndNoKeepAlive(t *testing.T) {
 	cli.Close()
 	// Same test without keepalive (exercises a different path)
 	opts.DisableKeepAlive = true
-	cli = NewFastClient(opts)
+	cli, _ = NewFastClient(opts)
 	_, data, _ = cli.Fetch()
 	recSz = len(data)
 	if recSz > sz {
@@ -609,14 +609,14 @@ func TestSmallBufferAndNoKeepAlive(t *testing.T) {
 
 func TestBadUrl(t *testing.T) {
 	opts := NewHTTPOptions("not a valid url")
-	cli := NewFastClient(opts)
-	if cli != nil {
+	cli, err := NewFastClient(opts)
+	if cli != nil || err == nil {
 		t.Errorf("config1: got a client %v despite bogus url %s", cli, opts.URL)
 		cli.Close()
 	}
 	opts.URL = "http://doesnotexist.fortio.org"
-	cli = NewFastClient(opts)
-	if cli != nil {
+	cli, err = NewFastClient(opts)
+	if cli != nil || err == nil {
 		t.Errorf("config2: got a client %v despite bogus url %s", cli, opts.URL)
 		cli.Close()
 	}
@@ -626,7 +626,7 @@ func TestDefaultPort(t *testing.T) {
 	// TODO: change back to fortio demo server once setup
 	url := "http://istio.io/" // shall imply port 80
 	opts := NewHTTPOptions(url)
-	cli := NewFastClient(opts)
+	cli, _ := NewFastClient(opts)
 	code, _, _ := cli.Fetch()
 	if code != 301 {
 		t.Errorf("unexpected code for %s: %d (expecting 301 redirect to https)", url, code)
@@ -644,14 +644,14 @@ func TestDefaultPort(t *testing.T) {
 	cli.Close()
 	opts.URL = "https://fortio.org" // will be https port 443
 	opts.Insecure = true            // not needed as we have valid certs but to exercise that code
-	cli = NewFastClient(opts)
-	if cli != nil {
+	cli, err := NewFastClient(opts)
+	if cli != nil || err == nil {
 		// If https support was added, remove this whitebox/for coverage purpose assertion
 		t.Errorf("fast client isn't supposed to support https (yet), got %v", cli)
 	}
-	cli = NewClient(opts)
+	cli, err = NewClient(opts)
 	if cli == nil {
-		t.Fatalf("Couldn't get a client using NewClient on modified opts.")
+		t.Fatalf("Couldn't get a client using NewClient on modified opts: %v", err)
 	}
 	// currently fast client fails with https:
 	code, _, _ = cli.Fetch()
@@ -679,7 +679,7 @@ func TestNoFirstChunkSizeInitially(t *testing.T) {
 	m.HandleFunc("/", delayedChunkedSize)
 	url := fmt.Sprintf("http://localhost:%d/delayedChunkedSize", a.Port)
 	o := HTTPOptions{URL: url}
-	client := NewClient(&o)
+	client, _ := NewClient(&o)
 	code, data, header := client.Fetch() // used to panic/bug #127
 	t.Logf("delayedChunkedSize result code %d, data len %d, headerlen %d", code, len(data), header)
 	if code != 200 {
@@ -697,7 +697,7 @@ func TestInvalidRequest(t *testing.T) {
 		NumConnections: -3,                       // bogus NumConnections will get fixed
 		HTTPReqTimeOut: -1,
 	}
-	client := NewStdClient(&o)
+	client, _ := NewStdClient(&o)
 	if o.NumConnections <= 0 {
 		t.Errorf("Got %d NumConnections, was expecting normalization to 1", o.NumConnections)
 	}
@@ -708,8 +708,8 @@ func TestInvalidRequest(t *testing.T) {
 		t.Errorf("Got %d code while expecting bad request (%d)", code, http.StatusBadRequest)
 	}
 	o.URL = client.url
-	c2 := NewStdClient(&o)
-	if c2 != nil {
+	c2, err := NewStdClient(&o)
+	if c2 != nil || err == nil {
 		t.Errorf("Got non nil client %+v code while expecting nil for bad request", c2)
 	}
 }
@@ -720,7 +720,7 @@ func TestPayloadSizeSmall(t *testing.T) {
 	for _, size := range []int{768, 0, 1} {
 		url := fmt.Sprintf("http://localhost:%d/with-size?size=%d", a.Port, size)
 		o := HTTPOptions{URL: url}
-		client := NewClient(&o)
+		client, _ := NewClient(&o)
 		code, data, header := client.Fetch() // used to panic/bug #127
 		t.Logf("TestPayloadSize result code %d, data len %d, headerlen %d", code, len(data), header)
 		if code != http.StatusOK {
@@ -761,7 +761,7 @@ func TestPayloadForClient(t *testing.T) {
 		hOptions.URL = "www.google.com"
 		hOptions.ContentType = test.contentType
 		hOptions.Payload = test.payload
-		client := NewStdClient(&hOptions)
+		client, _ := NewStdClient(&hOptions)
 		contentType := client.req.Header.Get("Content-Type")
 		if contentType != test.contentType {
 			t.Errorf("Got %s, expected %s as a content type", contentType, test.contentType)
@@ -815,7 +815,7 @@ func TestPayloadForFastClient(t *testing.T) {
 		hOptions.URL = "www.google.com"
 		hOptions.ContentType = test.contentType
 		hOptions.Payload = test.payload
-		client := NewFastClient(&hOptions)
+		client, _ := NewFastClient(&hOptions)
 		body := string(client.(*FastClient).req)
 		if body != test.expectedReqBody {
 			t.Errorf("Got\n%s\nexpecting\n%s", body, test.expectedReqBody)
@@ -830,7 +830,7 @@ func TestPayloadSizeLarge(t *testing.T) {
 	size := 200000
 	url := fmt.Sprintf("http://localhost:%d/with-size?size=%d&status=888", a.Port, size)
 	o := HTTPOptions{URL: url, DisableFastClient: true}
-	client := NewClient(&o)
+	client, _ := NewClient(&o)
 	code, data, header := client.Fetch() // used to panic/bug #127
 	t.Logf("TestPayloadSize result code %d, data len %d, headerlen %d", code, len(data), header)
 	if code != 888 {
@@ -850,7 +850,7 @@ func TestDebugHandlerSortedHeaders(t *testing.T) {
 	o.AddAndValidateExtraHeader("CCC: ccc")
 	o.AddAndValidateExtraHeader("ZZZ: zzz")
 	o.AddAndValidateExtraHeader("AAA: aaa")
-	client := NewClient(&o)
+	client, _ := NewClient(&o)
 	code, data, header := client.Fetch() // used to panic/bug #127
 	t.Logf("TestDebugHandlerSortedHeaders result code %d, data len %d, headerlen %d", code, len(data), header)
 	if code != http.StatusOK {
@@ -1063,7 +1063,7 @@ func TestDefaultHeadersAndOptionsInit(t *testing.T) {
 	// Un initialized http options:
 	o := HTTPOptions{URL: fmt.Sprintf("http://localhost:%d/debug", addr.Port)}
 	o1 := o
-	cli1 := NewStdClient(&o1)
+	cli1, _ := NewStdClient(&o1)
 	code, data, _ := cli1.Fetch()
 	if code != 200 {
 		t.Errorf("Non ok code %d for debug default fetch1", code)
@@ -1073,7 +1073,7 @@ func TestDefaultHeadersAndOptionsInit(t *testing.T) {
 		t.Errorf("Didn't find default header echoed back in std client1 %s (expecting %s)", DebugSummary(data, 512), expected)
 	}
 	o2 := o
-	cli2 := NewFastClient(&o2)
+	cli2, _ := NewFastClient(&o2)
 	code, data, _ = cli2.Fetch()
 	if code != 200 {
 		t.Errorf("Non ok code %d for debug default fetch2", code)
diff --git a/fhttp/httprunner_test.go b/fhttp/httprunner_test.go
index 4f0ca71..a87a7c5 100644
--- a/fhttp/httprunner_test.go
+++ b/fhttp/httprunner_test.go
@@ -60,12 +60,13 @@ func TestHTTPRunner(t *testing.T) {
 		URL: opts.URL,
 	}
 	o1 := rawOpts
-	if r, _, _ := NewFastClient(&o1).Fetch(); r != http.StatusOK {
+	fc, _ := NewFastClient(&o1)
+	if r, _, _ := fc.Fetch(); r != http.StatusOK {
 		t.Errorf("Fast Client with raw option should still work with warning in logs")
 	}
 	o1 = rawOpts
 	o1.URL = "http://www.doesnotexist.badtld/"
-	c := NewStdClient(&o1)
+	c, _ := NewStdClient(&o1)
 	c.ChangeURL(rawOpts.URL)
 	if r, _, _ := c.Fetch(); r != http.StatusOK {
 		t.Errorf("Std Client with raw option should still work with warning in logs")
@@ -210,7 +211,8 @@ func TestServe(t *testing.T) {
 	o := NewHTTPOptions(url)
 	o.AddAndValidateExtraHeader("X-Header: value1")
 	o.AddAndValidateExtraHeader("X-Header: value2")
-	code, data, _ := NewClient(o).Fetch()
+	c, _ := NewClient(o)
+	code, data, _ := c.Fetch()
 	if code != http.StatusOK {
 		t.Errorf("Unexpected non 200 ret code for debug url %s : %d", url, code)
 	}
diff --git a/fnet/network_test.go b/fnet/network_test.go
index 3ddc828..fa137b5 100644
--- a/fnet/network_test.go
+++ b/fnet/network_test.go
@@ -105,7 +105,7 @@ func TestResolveDestination(t *testing.T) {
 	for _, tt := range tests {
 		tt := tt // pin
 		t.Run(tt.name, func(t *testing.T) {
-			got := fnet.ResolveDestination(tt.destination)
+			got, _ := fnet.ResolveDestination(tt.destination)
 			gotStr := ""
 			if got != nil {
 				gotStr = got.String()
@@ -118,10 +118,10 @@ func TestResolveDestination(t *testing.T) {
 }
 
 func TestResolveDestinationMultipleIps(t *testing.T) {
-	addr := fnet.ResolveDestination("www.google.com:443")
-	t.Logf("Found google addr %+v", addr)
-	if addr == nil {
-		t.Error("got nil address for google")
+	addr, err := fnet.ResolveDestination("www.google.com:443")
+	t.Logf("Found google addr %+v err=%v", addr, err)
+	if addr == nil || err != nil {
+		t.Errorf("got nil address for google: %v", err)
 	}
 }
 
@@ -347,11 +347,11 @@ func TestProxyErrors(t *testing.T) {
 }
 
 func TestResolveIpV6(t *testing.T) {
-	addr := fnet.Resolve("[::1]", "http")
+	addr, err := fnet.Resolve("[::1]", "http")
 	addrStr := addr.String()
 	expected := "[::1]:80"
 	if addrStr != expected {
-		t.Errorf("Got '%s' instead of '%s'", addrStr, expected)
+		t.Errorf("Got '%s' instead of '%s': %v", addrStr, expected, err)
 	}
 }