From 38dac9520db9deb93bd8023e569c2d62850dfe74 Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Sat, 11 Oct 2014 10:49:51 +0100
Subject: [PATCH] add a benchmark tool

---
 cmd/radiobench/radiobench.go | 146 +++++++++++++++++++++++++++++++++++
 1 file changed, 146 insertions(+)
 create mode 100644 cmd/radiobench/radiobench.go

diff --git a/cmd/radiobench/radiobench.go b/cmd/radiobench/radiobench.go
new file mode 100644
index 00000000..3ace6c6e
--- /dev/null
+++ b/cmd/radiobench/radiobench.go
@@ -0,0 +1,146 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	numConns int
+
+	stats     = &Stats{}
+	retryTime = 2 * time.Second
+)
+
+type Stats struct {
+	HttpStatus map[int]int
+	HttpErrors int
+	Errors     int
+	lock       sync.Mutex
+}
+
+func (s *Stats) HttpError(resp *http.Response) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	cur, ok := s.HttpStatus[resp.StatusCode]
+	if !ok {
+		cur = 0
+	}
+	s.HttpStatus[resp.StatusCode] = cur + 1
+	s.HttpErrors++
+}
+
+func (s *Stats) Error() {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	s.Errors++
+}
+
+func (s *Stats) Dump() {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	log.Printf("errs=%d http_errs=%d http_status=%v", s.Errors, s.HttpErrors, s.HttpStatus)
+}
+
+func init() {
+	flag.IntVar(&numConns, "n", 3, "number of parallel connections")
+	flag.IntVar(&numConns, "num-clients", 3, "number of parallel connections")
+}
+
+func readstream(id int, streamUrl string) error {
+	resp, err := http.Get(streamUrl)
+	if err != nil {
+		stats.Error()
+		return err
+	}
+	if resp.StatusCode != 200 {
+		stats.HttpError(resp)
+		resp.Body.Close()
+		return fmt.Errorf("http status %s", resp.Status)
+	}
+
+	if resp.Header.Get("Content-Type") == "audio/x-mpegurl" {
+		data, err := ioutil.ReadAll(resp.Body)
+		resp.Body.Close()
+		if err != nil {
+			stats.Error()
+			return err
+		}
+
+		streamUrl = strings.TrimSpace(string(data))
+		resp, err = http.Get(streamUrl)
+		if err != nil {
+			stats.Error()
+			return err
+		}
+		if resp.StatusCode != 200 {
+			stats.HttpError(resp)
+			resp.Body.Close()
+			return fmt.Errorf("http status %s", resp.Status)
+		}
+	}
+
+	log.Printf("worker(%d): connected to %s", id, streamUrl)
+
+	defer resp.Body.Close()
+
+	// Just read data and discard it.
+	buf := make([]byte, 16384)
+	for {
+		n, err := resp.Body.Read(buf)
+		if err != nil {
+			stats.Error()
+			break
+		}
+		if n == 0 {
+			break
+		}
+	}
+	return fmt.Errorf("connection lost")
+}
+
+func worker(id int, streamUrl string) {
+	for {
+		err := readstream(id, streamUrl)
+		log.Printf("worker(%d): %v", id, err)
+		time.Sleep(retryTime)
+	}
+}
+
+func dumpStats() {
+	t := time.NewTicker(10 * time.Second)
+	for {
+		<-t.C
+		stats.Dump()
+	}
+}
+
+func main() {
+	flag.Parse()
+	if flag.NArg() != 1 {
+		fmt.Printf("Usage: radiobench [<OPTIONS>] <STREAM_URL>\n")
+		os.Exit(1)
+	}
+
+	streamUrl := flag.Arg(0)
+
+	go dumpStats()
+
+	var wg sync.WaitGroup
+	for i := 0; i < numConns; i++ {
+		wg.Add(1)
+		go func(id int) {
+			worker(id, streamUrl)
+			wg.Done()
+		}(i)
+	}
+	wg.Wait()
+}
-- 
GitLab