slowjoe

package module
v0.0.0-...-58b7831 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

slowjoe

Slow Joe

Go Report Card Build Status Coverage Status GitHub release (latest by date) GitHub

Simple TCP proxy to test your services for poor network conditions.

Simplicity is the most important aspect. No one wants to spend hours looking for dependencies, then learning yet another DSL or the quirks of a config file, right? Downloading a single, static binary (see releases) and reading the description of a few flags is all you need to start. Being a Docker aficionado makes things even simpler: all you need is to run docker run, as the service image is available on Docker Hub.

WARNING: unstable product. API, configuration and behaviour may and will change without a warning.

Quick start

If docker is available it is by far the easiest way to download and run service:

docker run --rm -it adamwasila/slowjoe:latest

INFO[0000] Upstream set                                  address="127.0.0.1:8000"
INFO[0000] Listen on TCP socket                          bind="127.0.0.1:9998"

Otherwise, go to releases subpage and download latest version, unpack and put in your PATH.

Last option is to build on your own. See Install section below for details.

With the binary in your PATH, you may try the following examples. First, run a less trivial example where slowjoe works as a proxy to the httpbin service. Let's assume that 10% of requests should have throughput limited to 1kb/s.

slowjoe -a -u "httpbin.org:80" -t 0.1 -r 1024

Use curl to check proxy response:

curl http://localhost:9998/headers

{
  "headers": {
    "Accept": "*/*", 
    "Host": "localhost", 
    "User-Agent": "curl/7.64.0"
  }
}

Now hit the following link in your browser http://localhost:9998/image/jpeg and experience modem-like connection speed. Welcome back to the Internet of the 90s.

Next test closing immediately behaviour:

slowjoe -u "httpbin.org:80" -c 1.0

Now, all requests should be closed without sending any data.

Both behaviours may be mixed together. Here, half of the connections are closed immediately and half are throttled to 1000 bytes per second:

slowjoe -u "httpbin.org:80" -c 0.5 -t 0.5 -r 1000

Finally, point your browser to http://localhost:6000 to see current settings and a list of currently open connections:

webdashboard

Note: -a option must be set to enable web dashboard. Then -p may be used to set port other than default 6000.

Install

Go 1.26 should be installed on the system. While it should compile succesfully using older versions it is recommended to use version 1.26 or newer.

Clone project repository to your machine:

git clone https://github.com/adamwasila/slowjoe.git

To build, enter repository and run:

go build -o slowjoe ./cmd/slowjoe

Configuration

Help flag -h allows to see full configuration options:

slowjoe -h

Usage of slowjoe:
  -a, --admin                   Enable admin console service
  -p, --admin-port int          Port for admin console service (default 6000)
  -b, --bind string             Address to bind listening socket to (default "0.0.0.0:9998")
  -c, --close-chance float      Probability of closing socket abruptly
  -d, --delay duration          Initial delay when connection starts to deteriorate
  -r, --rate int                Maximum data rate of bytes per second if throttling applied (see --throttle-chance)
  -t, --throttle-chance float   Probability of throttling
  -u, --upstream string         <host>[:port] of upstream service (default "127.0.0.1:8000")
  -v, --verbose                 Enable verbose output (debug logs)
  -w, --very-verbose            Enable very verbose output (trace logs)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Config

func Config(cfg config.Config) proxyOption

func Execute

func Execute(jobs ...func()) func(*executor)

Execute wraps list of plain, argumentless funtions to be independent jobs to be executed concurrently

func ExecuteWithContext

func ExecuteWithContext(jobs ...func(context.Context)) func(*executor)

ExecuteWithContext wraps list of plain, argumentless funtions to be independent jobs to be executed concurrently; additional context argument allows job to monitor cancellation signals from outside.

func Instrument

func Instrument(inst ...Instrumentation) proxyOption

func SetSignalCallback

func SetSignalCallback(callOnSignal func()) (wait, cancel func())

SetSignalCallback registers callback function to be called when one of SIGTERM, SIGINT signals is received. Returns two functions: first one should be called from separate goroutine as it is returing only if signal is received; it calls callback before returning second one is cancelling waiting for signal; callback won't be called.

func Version

func Version(version string) proxyOption

func WhenAllFinished

func WhenAllFinished(closeHandler func()) func(*executor)

WhenAllFinished adds handler that is called when all jobs finishes. It will be called only once and only when all jobs quit no matter of the result or panic they raise.

func WhenPanic

func WhenPanic(panicHandler func(interface{})) func(*executor)

WhenPanic adds handler called for any job that panics. Note that handler must be reentrant as may be called multiple times by different goroutines

Types

type Instrumentation

type Instrumentation interface {
	ConnectionOpened(id, alias, typ string)
	ConnectionProgressed(id, alias, direction string, transferredBytes int)
	ConnectionDelayed(id, alias, direction string, delay time.Duration)
	ConnectionCompleted(id, alias, direction string, transferredBytes int, duration time.Duration)
	ConnectionScheduledClose(id, alias string, delay time.Duration)
	ConnectionClosedUpstream(id, alias string)
	ConnectionClosedDownstream(id, alias string)
	ConnectionClosed(id, alias string, duration time.Duration)
}

Instrumentation is interface of hooks called by main application logic to support decoupled and pluggable logging, metrics, tracing etc.

type Instrumentations

type Instrumentations []Instrumentation

func (Instrumentations) ConnectionClosed

func (ci Instrumentations) ConnectionClosed(id, alias string, d time.Duration)

func (Instrumentations) ConnectionClosedDownstream

func (ci Instrumentations) ConnectionClosedDownstream(id, alias string)

func (Instrumentations) ConnectionClosedUpstream

func (ci Instrumentations) ConnectionClosedUpstream(id, alias string)

func (Instrumentations) ConnectionCompleted

func (ci Instrumentations) ConnectionCompleted(id, alias, direction string, transferredBytes int, duration time.Duration)

func (Instrumentations) ConnectionDelayed

func (ci Instrumentations) ConnectionDelayed(id, alias, direction string, delay time.Duration)

func (Instrumentations) ConnectionOpened

func (ci Instrumentations) ConnectionOpened(id, alias, typ string)

func (Instrumentations) ConnectionProgressed

func (ci Instrumentations) ConnectionProgressed(id, alias, direction string, transferredBytes int)

func (Instrumentations) ConnectionScheduledClose

func (ci Instrumentations) ConnectionScheduledClose(id, alias string, delay time.Duration)

type Logs

type Logs struct {
	Log *logrus.Logger
}

func DefaultLogs

func DefaultLogs() *Logs

func (*Logs) ConnectionClosed

func (l *Logs) ConnectionClosed(id, alias string, d time.Duration)

func (*Logs) ConnectionClosedDownstream

func (l *Logs) ConnectionClosedDownstream(id, alias string)

func (*Logs) ConnectionClosedUpstream

func (l *Logs) ConnectionClosedUpstream(id, alias string)

func (*Logs) ConnectionCompleted

func (l *Logs) ConnectionCompleted(id, alias, direction string, transferredBytes int, duration time.Duration)

func (*Logs) ConnectionDelayed

func (l *Logs) ConnectionDelayed(id, alias, direction string, delay time.Duration)

func (*Logs) ConnectionOpened

func (l *Logs) ConnectionOpened(id, alias, typ string)

func (*Logs) ConnectionProgressed

func (l *Logs) ConnectionProgressed(id, alias, direction string, transferredBytes int)

func (*Logs) ConnectionScheduledClose

func (l *Logs) ConnectionScheduledClose(id, alias string, delay time.Duration)

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

func (*Metrics) ConnectionClosed

func (m *Metrics) ConnectionClosed(id, alias string, d time.Duration)

func (*Metrics) ConnectionClosedDownstream

func (m *Metrics) ConnectionClosedDownstream(id, alias string)

func (*Metrics) ConnectionClosedUpstream

func (m *Metrics) ConnectionClosedUpstream(id, alias string)

func (*Metrics) ConnectionCompleted

func (m *Metrics) ConnectionCompleted(id, alias, direction string, transferredBytes int, duration time.Duration)

func (*Metrics) ConnectionDelayed

func (m *Metrics) ConnectionDelayed(id, alias, direction string, delay time.Duration)

func (*Metrics) ConnectionOpened

func (m *Metrics) ConnectionOpened(id, alias, typ string)

func (*Metrics) ConnectionProgressed

func (m *Metrics) ConnectionProgressed(id, alias, direction string, transferredBytes int)

func (*Metrics) ConnectionScheduledClose

func (m *Metrics) ConnectionScheduledClose(id, alias string, delay time.Duration)

func (*Metrics) Init

func (m *Metrics) Init(ctx context.Context, g *run.Group, adminPort int, data *admin.AdminData)

type Proxy

type Proxy struct {
	// contains filtered or unexported fields
}

func New

func New(options ...proxyOption) (*Proxy, error)

func (*Proxy) Listen

func (p *Proxy) Listen(ctx context.Context, g *run.Group) error

type Runner

type Runner interface {
	Run(ctx context.Context)
}

Runner is the interface that wraps basic, argumentless Run method

func Executor

func Executor(ops ...func(*executor)) Runner

Executor returns new instance of concurrent jobs executor

Directories

Path Synopsis
cmd
slowjoe command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL