From db6bfb3677c38fbfad80e60ab79afa435ceae970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuffel?= Date: Tue, 19 Jul 2022 20:22:20 +0200 Subject: [PATCH 1/5] feat: enable speedbump library usage --- args.go | 17 ++++++------ args_test.go | 14 +++++----- base.go => lib/base.go | 2 +- connection.go => lib/connection.go | 2 +- connection_test.go => lib/connection_test.go | 2 +- .../latency_generator.go | 26 +++++++++---------- .../latency_generator_test.go | 14 +++++----- sawtooth.go => lib/sawtooth.go | 2 +- sawtooth_test.go => lib/sawtooth_test.go | 2 +- sine.go => lib/sine.go | 2 +- speedbump.go => lib/speedbump.go | 25 ++++++++++++++---- speedbump_test.go => lib/speedbump_test.go | 14 +++++----- main.go | 4 ++- 13 files changed, 72 insertions(+), 54 deletions(-) rename base.go => lib/base.go (93%) rename connection.go => lib/connection.go (99%) rename connection_test.go => lib/connection_test.go (99%) rename latency_generator.go => lib/latency_generator.go (67%) rename latency_generator_test.go => lib/latency_generator_test.go (84%) rename sawtooth.go => lib/sawtooth.go (96%) rename sawtooth_test.go => lib/sawtooth_test.go (97%) rename sine.go => lib/sine.go (96%) rename speedbump.go => lib/speedbump.go (72%) rename speedbump_test.go => lib/speedbump_test.go (92%) diff --git a/args.go b/args.go index d5e3d09..88869cc 100644 --- a/args.go +++ b/args.go @@ -1,10 +1,11 @@ package main import ( + "github.com/kffl/speedbump/lib" "gopkg.in/alecthomas/kingpin.v2" ) -func parseArgs(args []string) (*SpeedbumpCfg, error) { +func parseArgs(args []string) (*lib.SpeedbumpCfg, error) { var app = kingpin.New("speedbump", "TCP proxy for simulating variable network latency.") var ( @@ -42,16 +43,16 @@ func parseArgs(args []string) (*SpeedbumpCfg, error) { return nil, err } - var cfg = SpeedbumpCfg{ + var cfg = lib.SpeedbumpCfg{ Port: *port, DestAddr: *destAddr, BufferSize: int(*bufferSize), - Latency: &LatencyCfg{ - base: *latency, - sineAmplitude: *sineAmplitude, - sinePeriod: *sinePeriod, - sawAmplitute: *sawAmplitute, - sawPeriod: *sawPeriod, + Latency: &lib.LatencyCfg{ + Base: *latency, + SineAmplitude: *sineAmplitude, + SinePeriod: *sinePeriod, + SawAmplitute: *sawAmplitute, + SawPeriod: *sawPeriod, }, LogLevel: *logLevel, } diff --git a/args_test.go b/args_test.go index 36dde25..146931c 100644 --- a/args_test.go +++ b/args_test.go @@ -13,8 +13,8 @@ func TestParseArgsDefault(t *testing.T) { assert.Equal(t, cfg.DestAddr, "localhost:80") assert.Equal(t, cfg.Port, 8000) assert.Equal(t, 0xffff+1, cfg.BufferSize) - assert.Equal(t, time.Millisecond*5, cfg.Latency.base) - assert.Equal(t, time.Duration(0), cfg.Latency.sineAmplitude) + assert.Equal(t, time.Millisecond*5, cfg.Latency.Base) + assert.Equal(t, time.Duration(0), cfg.Latency.SineAmplitude) } func TestParseArgsError(t *testing.T) { @@ -37,9 +37,9 @@ func TestParseArgsAll(t *testing.T) { assert.Equal(t, cfg.DestAddr, "host:777") assert.Equal(t, cfg.Port, 1234) assert.Equal(t, 200, cfg.BufferSize) - assert.Equal(t, time.Millisecond*100, cfg.Latency.base) - assert.Equal(t, time.Millisecond*50, cfg.Latency.sineAmplitude) - assert.Equal(t, time.Minute, cfg.Latency.sinePeriod) - assert.Equal(t, time.Duration(0), cfg.Latency.sawAmplitute) - assert.Equal(t, time.Duration(0), cfg.Latency.sawPeriod) + assert.Equal(t, time.Millisecond*100, cfg.Latency.Base) + assert.Equal(t, time.Millisecond*50, cfg.Latency.SineAmplitude) + assert.Equal(t, time.Minute, cfg.Latency.SinePeriod) + assert.Equal(t, time.Duration(0), cfg.Latency.SawAmplitute) + assert.Equal(t, time.Duration(0), cfg.Latency.SawPeriod) } diff --git a/base.go b/lib/base.go similarity index 93% rename from base.go rename to lib/base.go index f69d6d3..06df55f 100644 --- a/base.go +++ b/lib/base.go @@ -1,4 +1,4 @@ -package main +package lib import "time" diff --git a/connection.go b/lib/connection.go similarity index 99% rename from connection.go rename to lib/connection.go index a416994..a4500a7 100644 --- a/connection.go +++ b/lib/connection.go @@ -1,4 +1,4 @@ -package main +package lib import ( "context" diff --git a/connection_test.go b/lib/connection_test.go similarity index 99% rename from connection_test.go rename to lib/connection_test.go index 862bf21..2bd1a81 100644 --- a/connection_test.go +++ b/lib/connection_test.go @@ -1,4 +1,4 @@ -package main +package lib import ( "context" diff --git a/latency_generator.go b/lib/latency_generator.go similarity index 67% rename from latency_generator.go rename to lib/latency_generator.go index 7cef75c..3d19daa 100644 --- a/latency_generator.go +++ b/lib/latency_generator.go @@ -1,4 +1,4 @@ -package main +package lib import ( "time" @@ -9,11 +9,11 @@ type LatencyGenerator interface { } type LatencyCfg struct { - base time.Duration - sineAmplitude time.Duration - sinePeriod time.Duration - sawAmplitute time.Duration - sawPeriod time.Duration + Base time.Duration + SineAmplitude time.Duration + SinePeriod time.Duration + SawAmplitute time.Duration + SawPeriod time.Duration } type latencySummand interface { @@ -26,17 +26,17 @@ type simpleLatencyGenerator struct { } func newSimpleLatencyGenerator(start time.Time, cfg *LatencyCfg) simpleLatencyGenerator { - summands := []latencySummand{baseLatencySummand{cfg.base}} - if cfg.sineAmplitude > 0 && cfg.sinePeriod > 0 { + summands := []latencySummand{baseLatencySummand{cfg.Base}} + if cfg.SineAmplitude > 0 && cfg.SinePeriod > 0 { summands = append(summands, sineLatencySummand{ - cfg.sineAmplitude, - cfg.sinePeriod, + cfg.SineAmplitude, + cfg.SinePeriod, }) } - if cfg.sawAmplitute > 0 && cfg.sawPeriod > 0 { + if cfg.SawAmplitute > 0 && cfg.SawPeriod > 0 { summands = append(summands, sawtoothLatencySummand{ - cfg.sawAmplitute, - cfg.sawPeriod, + cfg.SawAmplitute, + cfg.SawPeriod, }) } return simpleLatencyGenerator{ diff --git a/latency_generator_test.go b/lib/latency_generator_test.go similarity index 84% rename from latency_generator_test.go rename to lib/latency_generator_test.go index 4870e41..e3c4cf4 100644 --- a/latency_generator_test.go +++ b/lib/latency_generator_test.go @@ -1,4 +1,4 @@ -package main +package lib import ( "testing" @@ -10,9 +10,9 @@ import ( func TestSimpleLatencyGeneratorWithSine(t *testing.T) { start := time.Now() g := newSimpleLatencyGenerator(start, &LatencyCfg{ - base: time.Second * 3, - sineAmplitude: time.Second * 2, - sinePeriod: time.Second * 8, + Base: time.Second * 3, + SineAmplitude: time.Second * 2, + SinePeriod: time.Second * 8, }) startingVal := g.generateLatency(start) @@ -28,9 +28,9 @@ func TestSimpleLatencyGeneratorWithSine(t *testing.T) { func TestSimpleLatencyGeneratorWithSawtooth(t *testing.T) { start := time.Now() g := newSimpleLatencyGenerator(start, &LatencyCfg{ - base: time.Second * 3, - sawAmplitute: time.Second * 2, - sawPeriod: time.Second * 8, + Base: time.Second * 3, + SawAmplitute: time.Second * 2, + SawPeriod: time.Second * 8, }) startingVal := g.generateLatency(start) diff --git a/sawtooth.go b/lib/sawtooth.go similarity index 96% rename from sawtooth.go rename to lib/sawtooth.go index 679b5c9..316fad5 100644 --- a/sawtooth.go +++ b/lib/sawtooth.go @@ -1,4 +1,4 @@ -package main +package lib import "time" diff --git a/sawtooth_test.go b/lib/sawtooth_test.go similarity index 97% rename from sawtooth_test.go rename to lib/sawtooth_test.go index eb0742c..7f1b716 100644 --- a/sawtooth_test.go +++ b/lib/sawtooth_test.go @@ -1,4 +1,4 @@ -package main +package lib import ( "testing" diff --git a/sine.go b/lib/sine.go similarity index 96% rename from sine.go rename to lib/sine.go index 3feb883..fd72082 100644 --- a/sine.go +++ b/lib/sine.go @@ -1,4 +1,4 @@ -package main +package lib import ( "math" diff --git a/speedbump.go b/lib/speedbump.go similarity index 72% rename from speedbump.go rename to lib/speedbump.go index 1d3d1c9..deb937b 100644 --- a/speedbump.go +++ b/lib/speedbump.go @@ -1,4 +1,5 @@ -package main +// Package lib allows for using speedbump as a library. +package lib import ( "context" @@ -11,6 +12,7 @@ import ( "github.com/hashicorp/go-hclog" ) +// Speedbump is a proxy instance returned by NewSpeedbump type Speedbump struct { bufferSize int srcAddr, destAddr net.TCPAddr @@ -25,14 +27,21 @@ type Speedbump struct { log hclog.Logger } +// SpeedbumpCfg contains Spedbump instance configuration type SpeedbumpCfg struct { - Port int - DestAddr string + // Port specifies the local port number to listen on + Port int + // DestAddr specifies the proxy desination address in host:port format + DestAddr string + // BufferSize specifies the size of a buffer used for TCP reads BufferSize int - Latency *LatencyCfg - LogLevel string + // LatencyCfg specifies parameters of the desired latency summands + Latency *LatencyCfg + // LogLevel can be one of: DEBUG, TRACE, INFO, WARN, ERROR + LogLevel string } +// NewSpeedbump creates a Speedbump instance based on a provided config func NewSpeedbump(cfg *SpeedbumpCfg) (*Speedbump, error) { localTCPAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", cfg.Port)) if err != nil { @@ -94,6 +103,9 @@ func (s *Speedbump) startProxyConnection(p *connection) { p.start() } +// Start launches a Speedbump instance. This operation will either block +// until all proxy connections are closed following a Stop() call or +// return immedietely if a ListenTCP error occurrs at startup. func (s *Speedbump) Start() error { listener, err := net.ListenTCP("tcp", &s.srcAddr) if err != nil { @@ -115,6 +127,9 @@ func (s *Speedbump) Start() error { return nil } +// Stop closes the Speedbump instance's TCP listener and notifies all existing +// proxy connections that Speedbump is shutting down. It doesn't wait for +// the individual proxy connections to close prior to returning. func (s *Speedbump) Stop() { s.log.Info("Stopping speedbump") // close TCP listener so that startAcceptLoop returns diff --git a/speedbump_test.go b/lib/speedbump_test.go similarity index 92% rename from speedbump_test.go rename to lib/speedbump_test.go index 85247b3..d9b33fa 100644 --- a/speedbump_test.go +++ b/lib/speedbump_test.go @@ -1,4 +1,4 @@ -package main +package lib import ( "fmt" @@ -12,9 +12,9 @@ import ( ) var defaultLatencyCfg = &LatencyCfg{ - base: time.Millisecond * 5, - sineAmplitude: time.Duration(0), - sinePeriod: time.Minute, + Base: time.Millisecond * 5, + SineAmplitude: time.Duration(0), + SinePeriod: time.Minute, } func startEchoSrv(port int) error { @@ -109,9 +109,9 @@ func TestSpeedbumpWithEchoServer(t *testing.T) { testSrvAddr, 0xffff, &LatencyCfg{ - base: time.Millisecond * 100, - sineAmplitude: time.Millisecond * 100, - sinePeriod: time.Millisecond * 400, + Base: time.Millisecond * 100, + SineAmplitude: time.Millisecond * 100, + SinePeriod: time.Millisecond * 400, }, "WARN", } diff --git a/main.go b/main.go index 87a5fcb..ab3ad99 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,8 @@ import ( "os" "os/signal" "syscall" + + "github.com/kffl/speedbump/lib" ) func exitWithError(err error) { @@ -19,7 +21,7 @@ func main() { exitWithError(err) } - s, err := NewSpeedbump(cfg) + s, err := lib.NewSpeedbump(cfg) if err != nil { exitWithError(err) From 2640b87fa38c4ba4c26e1a159135758c14ea2f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuffel?= Date: Tue, 19 Jul 2022 20:22:41 +0200 Subject: [PATCH 2/5] docs: add speedbump lib documentation --- lib/README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 lib/README.md diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000..aa132d6 --- /dev/null +++ b/lib/README.md @@ -0,0 +1,64 @@ +# speedbump `lib` package + +This package allows for using speedbump as external library in Go code. It can be useful for adding programmatic delay to TCP connections while running load tests (i.e. between the SUT and a database). + +Consult GoDoc for API reference: + +[![GoDoc](https://godoc.org/github.com/kffl/speedbump/lib?status.svg)](https://godoc.org/github.com/kffl/speedbump/lib) + +## Installation + +``` +go get github.com/kffl/speedbump/lib +``` + +## Example usage + +```go +package main + +import ( + "time" + + speedbump "github.com/kffl/speedbump/lib" +) + +func main() { + cfg := speedbump.SpeedbumpCfg{ + Port: 8000, + DestAddr: "localhost:80", + BufferSize: 16384, + Latency: &speedbump.LatencyCfg{ + Base: time.Millisecond * 100, + SineAmplitude: time.Millisecond * 50, + SinePeriod: time.Minute, + }, + LogLevel: "INFO", + } + + s, err := speedbump.NewSpeedbump(&cfg) + + if err != nil { + // handle creation error + return + } + + go func() { + // stop the proxy after 5 minutes + time.Sleep(time.Second * 5) + s.Stop() + }() + + // Start() will either block until .Stop() is called + // or return immedietely if there is a startup error + err = s.Start() + + if err != nil { + // handle startup error + return + } + + // DONE +} +``` + From 8d0190bfd4574dd8c33012c3126435d5612b9d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuffel?= Date: Tue, 19 Jul 2022 20:24:32 +0200 Subject: [PATCH 3/5] docs: add graph, godoc and example usage to README --- README.md | 26 +- assets/sawtooth.svg | 1325 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1344 insertions(+), 7 deletions(-) create mode 100644 assets/sawtooth.svg diff --git a/README.md b/README.md index c765f9e..73987d5 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,28 @@ -# speedbump - TCP proxy for simulating variable network latency +# speedbump - TCP proxy with variable latency
- Speedbump Logo + speedbump logo
+Speedbump is a TCP proxy written in Go which allows for simulating variable network latency. -[![CI Workflow](https://github.com/kffl/speedbump/workflows/CI/badge.svg)](https://github.com/kffl/speedbump/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/kffl/speedbump)](https://goreportcard.com/report/github.com/kffl/speedbump) [![Docker Image Version](https://img.shields.io/docker/v/kffl/speedbump)](https://hub.docker.com/r/kffl/speedbump) +[![CI Workflow](https://github.com/kffl/speedbump/workflows/CI/badge.svg)](https://github.com/kffl/speedbump/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/kffl/speedbump)](https://goreportcard.com/report/github.com/kffl/speedbump) [![Docker Image Version](https://img.shields.io/docker/v/kffl/speedbump)](https://hub.docker.com/r/kffl/speedbump) [![GoDoc](https://godoc.org/github.com/kffl/speedbump/lib?status.svg)](https://godoc.org/github.com/kffl/speedbump/lib) -Speedbump is a TCP proxy which allows for simulating variable network latency. +## Example usage -## Usage - -Spawn a new instance listening on port 2000 that proxies TCP traffic to localhost:80 with a base latency of 100ms and sine wave amplitude of 100ms (resulting in maximum added latency being 200ms and minimum being 0), period of which is 1m: +Spawn a new instance listening on port 2000 that proxies TCP traffic to localhost:80 with a base latency of 100ms and sine wave amplitude of 100ms (resulting in maximum added latency being 200ms and minimum being 0), period of which is 1 minute: ``` speedbump --latency=100ms --sine-amplitude=100ms --sine-period=1m --port=2000 localhost:80 ``` +Spawn a new instance with a base latency of 300ms and a sawtooth wave latency summand with amplitude of 200ms and period of 2 minutes (visualized by the graph below): + +``` +speedbump --latency=200ms --saw-amplitude=200ms --saw-period=2m --port=2000 localhost:80 +``` +
+ speedbump sawtooth wave graph +
+ ## CLI Arguments Reference: Output of `speedbump --help`: @@ -40,6 +48,10 @@ Args: TCP proxy destination in host:post format. ``` +## Using speedbump as a library + +Speedbump can be used as a Go library via its `lib` package. Check `lib` [README](lib/README.md) for additional information. + ## License Copyright Paweł Kuffel 2022, licensed under Apache 2.0 License. diff --git a/assets/sawtooth.svg b/assets/sawtooth.svg new file mode 100644 index 0000000..dc27bea --- /dev/null +++ b/assets/sawtooth.svg @@ -0,0 +1,1325 @@ + + + + + + + + 2022-07-19T20:17:48.714337 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 51d5c983564af4baf52015027cabddafd6c31c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuffel?= Date: Tue, 19 Jul 2022 20:28:30 +0200 Subject: [PATCH 4/5] docs: fix asset links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 73987d5..6226347 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # speedbump - TCP proxy with variable latency
- speedbump logo + speedbump logo
Speedbump is a TCP proxy written in Go which allows for simulating variable network latency. @@ -20,7 +20,7 @@ Spawn a new instance with a base latency of 300ms and a sawtooth wave latency su speedbump --latency=200ms --saw-amplitude=200ms --saw-period=2m --port=2000 localhost:80 ```
- speedbump sawtooth wave graph + speedbump sawtooth wave graph
## CLI Arguments Reference: From 28559ec3246d15b05bb504bcd03d790631e676fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kuffel?= Date: Tue, 19 Jul 2022 20:29:55 +0200 Subject: [PATCH 5/5] chore: set version to 0.1.0-rc4 --- args.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/args.go b/args.go index 88869cc..f8ea9a3 100644 --- a/args.go +++ b/args.go @@ -36,7 +36,7 @@ func parseArgs(args []string) (*lib.SpeedbumpCfg, error) { String() ) - app.Version("0.1.0-rc2") + app.Version("0.1.0-rc4") _, err := app.Parse(args) if err != nil {