about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2020-04-12 18:28:11 +0200
committerLeah Neukirchen <leah@vuxu.org>2020-04-12 18:28:11 +0200
commitdb20f44b0d100ad042c2f86b8baf394665d8ec63 (patch)
treec96ff2ca586395ae869342f1d92c467319532ee1
parent3c83670c5f5a2395c7e08dfb0e45a001944ce406 (diff)
downloadhtping-db20f44b0d100ad042c2f86b8baf394665d8ec63.tar.gz
htping-db20f44b0d100ad042c2f86b8baf394665d8ec63.tar.xz
htping-db20f44b0d100ad042c2f86b8baf394665d8ec63.zip
allow parallel pinging of multiple urls
-rw-r--r--README4
-rw-r--r--htping.14
-rw-r--r--htping.go120
3 files changed, 71 insertions, 57 deletions
diff --git a/README b/README
index 1b710c9..c56f7fe 100644
--- a/README
+++ b/README
@@ -5,10 +5,10 @@ NAME
 
 SYNOPSIS
      htping [-4] [-6] [-H field:value] [-X method] [-c count] [-i interval]
-            [-f] [-k] [--http1.1] [--keepalive] [-l addr] [-q] host
+            [-f] [-k] [--http1.1] [--keepalive] [-l addr] [-q] urls ...
 
 DESCRIPTION
-     The htping utility periodically sends HTTP requests to host, prints the
+     The htping utility periodically sends HTTP requests to urls, prints the
      results and computes some statistics at exit.  Use Ctrl-C to quit htping.
 
      The options are as follows:
diff --git a/htping.1 b/htping.1
index 5ca4582..2fec57e 100644
--- a/htping.1
+++ b/htping.1
@@ -18,12 +18,12 @@
 .Op Fl -keepalive
 .Op Fl l Ar addr
 .Op Fl q
-.Ar host
+.Ar urls ...
 .Sh DESCRIPTION
 The
 .Nm
 utility periodically sends HTTP requests to
-.Ar host ,
+.Ar urls ,
 prints the results and computes some statistics at exit.
 Use Ctrl-C to quit
 .Nm .
diff --git a/htping.go b/htping.go
index 600ad50..5010c39 100644
--- a/htping.go
+++ b/htping.go
@@ -25,6 +25,7 @@ import (
 	"os/signal"
 	"strconv"
 	"strings"
+	"sync"
 	"sync/atomic"
 	"time"
 
@@ -101,8 +102,6 @@ type transport struct {
 	addr string
 }
 
-var myTransport *transport
-
 func newTransport() *transport {
 	tr := &transport{}
 
@@ -174,7 +173,7 @@ type result struct {
 	code int
 }
 
-func ping(url string, seq int, results chan result) {
+func ping(url string, seq int, myTransport *transport, results chan result) {
 	start := time.Now()
 
 	requestCounter.WithLabelValues(url).Inc()
@@ -260,13 +259,15 @@ func stats(results chan result, done chan bool) {
 
 		case <-done:
 			stop := time.Now()
-			fmt.Printf("\n%d requests sent, %d (%d%%) responses, %d (%d%%) successful, time %dms\n",
-				ntotal,
-				nrecv,
-				(100*nrecv)/int(ntotal),
-				nsucc,
-				(100*nsucc)/int(ntotal),
-				int64(stop.Sub(start)/time.Millisecond))
+			if ntotal > 0 {
+				fmt.Printf("\n%d requests sent, %d (%d%%) responses, %d (%d%%) successful, time %dms\n",
+					ntotal,
+					nrecv,
+					(100*nrecv)/int(ntotal),
+					nsucc,
+					(100*nsucc)/int(ntotal),
+					int64(stop.Sub(start)/time.Millisecond))
+			}
 			if nrecv > 0 {
 				mdev := math.Sqrt(sum2/float64(nrecv) -
 					sum/float64(nrecv)*sum/float64(nrecv))
@@ -323,24 +324,10 @@ func main() {
 	flag.Parse()
 
 	args := flag.Args()
-	if len(args) != 1 {
+	if len(args) < 1 {
 		flag.Usage()
 		os.Exit(2)
 	}
-	u := args[0]
-
-	u2, err := url.ParseRequestURI(u)
-	if (err != nil && strings.HasSuffix(err.Error(),
-		"invalid URI for request")) ||
-		(u2.Scheme != "http" && u2.Scheme != "https") {
-		u = "http://" + u
-	}
-
-	_, err = url.ParseRequestURI(u)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
-		os.Exit(1)
-	}
 
 	if *listenAddr != "" {
 		prometheus.MustRegister(requestCounter)
@@ -369,8 +356,6 @@ func main() {
 		}()
 	}
 
-	fmt.Printf("%s %s\n", method, u)
-
 	interrupt := make(chan os.Signal, 1)
 	signal.Notify(interrupt, os.Interrupt)
 
@@ -380,36 +365,65 @@ func main() {
 
 	count := 0
 
-	myTransport = newTransport()
+	var wg sync.WaitGroup
+	wg.Add(len(args))
 
-	if *flood {
-	flood_loop:
-		for {
-			select {
-			default:
-				ping(u, count, results)
-				count++
-			case <-interrupt:
-				break flood_loop
-			}
+	for _, mu := range args {
+		u := mu
+
+		u2, err := url.ParseRequestURI(u)
+		if (err != nil && strings.HasSuffix(err.Error(),
+			"invalid URI for request")) ||
+			(u2.Scheme != "http" && u2.Scheme != "https") {
+			u = "http://" + u
 		}
-	} else {
-		pingTicker := time.NewTicker(*sleep)
-		go ping(u, count, results)
-		count++
-	ping_loop:
-		for {
-			if *maxCount > 0 && count > *maxCount {
-				break
-			}
-			select {
-			case <-pingTicker.C:
-				go ping(u, count, results)
+
+		_, err = url.ParseRequestURI(u)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+			os.Exit(1)
+		}
+
+		fmt.Printf("%s %s\n", method, u)
+
+		go func() {
+			myTransport := newTransport()
+			defer wg.Done()
+
+			if *flood {
+				for {
+					select {
+					default:
+						ping(u, count, myTransport, results)
+						count++
+					}
+				}
+			} else {
+				pingTicker := time.NewTicker(*sleep)
+				go ping(u, count, myTransport, results)
 				count++
-			case <-interrupt:
-				break ping_loop
+				for {
+					if *maxCount > 0 && count > *maxCount {
+						break
+					}
+					select {
+					case <-pingTicker.C:
+						go ping(u, count, myTransport, results)
+						count++
+					}
+				}
 			}
-		}
+		}()
+	}
+
+	waitCh := make(chan struct{})
+	go func() {
+		wg.Wait()
+	}()
+
+	select {
+	case <-waitCh:
+	case <-interrupt:
 	}
 
 	done <- true