bwlimit

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Feb 23, 2026 License: MIT Imports: 6 Imported by: 3

README

build coverage goreport Docs

bwlimit

Go net.Conn bandwidth limiter.

Only depends on the standard library.

Usage

go get github.com/linkdata/bwlimit

Ticker must be created with bwlimit.NewTicker(). The zero-value Ticker is not supported. Limits are enforced in 100ms slices with fractional carry-over between slices, so very low limits are accurate over time but can still be bursty at slice boundaries. After calling Limiter.Stop(), bandwidth metrics (Count and Rate) are no longer updated.

Example

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/linkdata/bwlimit"
)

func main() {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello world!"))
	}))
	defer srv.Close()

	// limit reads to 100 bytes/sec, unlimited writes
	lim := bwlimit.NewLimiter(100, 0)
	defer lim.Stop()

	// clone the default transport so this change is local to this client
	tp := http.DefaultTransport.(*http.Transport).Clone()
	tp.DialContext = lim.Wrap(nil).DialContext
	client := &http.Client{Transport: tp}

	// make a request and time it
	now := time.Now()
	resp, err := client.Get(srv.URL)
	elapsed := time.Since(now)

	if err == nil {
		defer resp.Body.Close()
		var body []byte
		if body, err = io.ReadAll(resp.Body); err == nil {
			fmt.Printf("%v %v %q\n", elapsed >= time.Second, lim.Reads.Count.Load() > 100, string(body))
		}
	}
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultNetDialer = &net.Dialer{}

Functions

This section is empty.

Types

type Conn

type Conn struct {
	net.Conn // underlying net.Conn
	*Limiter // Limiter to use
}

func (*Conn) Read

func (c *Conn) Read(b []byte) (n int, err error)

func (*Conn) Write

func (c *Conn) Write(b []byte) (n int, err error)

type ContextDialer added in v0.12.0

type ContextDialer interface {
	DialContext(ctx context.Context, network, address string) (conn net.Conn, err error)
}

type Dialer

type Dialer struct {
	ContextDialer // ContextDialer we wrap
	*Limiter      // Limiter to use
}

func (*Dialer) Dial

func (d *Dialer) Dial(network string, address string) (net.Conn, error)

func (*Dialer) DialContext

func (d *Dialer) DialContext(ctx context.Context, network, address string) (conn net.Conn, err error)

type Limiter

type Limiter struct {
	*Ticker
	Reads  *Operation
	Writes *Operation
}

func NewLimiter

func NewLimiter(limits ...int64) *Limiter

NewLimiter returns a new limiter from DefaultTicker. If DefaultTicker has been stopped, returns nil. If you provide limits, the first will set both read and write limits, the second will set the write limit. Limits are applied in 100ms slices with fractional carry-over between slices, so very low rates are accurate over time but can be bursty at slice boundaries.

To stop the Limiter and free it's resources, call Stop.

func (*Limiter) Stop added in v0.11.0

func (l *Limiter) Stop()

Stop stops the Limiter and frees any resources. Reads and writes on a stopped and rate-limited Limiter returns io.EOF. On an unlimited Limiter they function as normal.

Count and Rate metrics are not updated after Stop.

func (*Limiter) Wrap added in v0.7.0

func (l *Limiter) Wrap(cd ContextDialer) ContextDialer

Wrap returns a ContextDialer wrapping cd that is bandwidth limited by this Limiter.

If cd is nil we use DefaultNetDialer. If cd is already limited by this Limiter, cd is returned unchanged.

type Listener

type Listener struct {
	net.Listener // underlying net.Listener
	*Limiter     // Limiter to use
}

func (*Listener) Accept

func (l *Listener) Accept() (conn net.Conn, err error)

type Operation

type Operation struct {
	*Ticker              // Ticker we belong to
	Limit   atomic.Int64 // bandwith limit in bytes/sec
	Rate    atomic.Int64 // current rate in bytes/sec
	Count   atomic.Int64 // number of bytes seen
	// contains filtered or unexported fields
}

func NewOperation

func NewOperation(t *Ticker, limits []int64, idx int) (op *Operation)

func (*Operation) Stop added in v0.11.0

func (op *Operation) Stop()

type Ticker added in v0.10.0

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

A Ticker synchronizes rate calculation among multiple Limiters. Ticker values must be created with NewTicker; the zero value is not supported.

var DefaultTicker *Ticker = NewTicker()

func NewTicker added in v1.0.0

func NewTicker() (ot *Ticker)

NewTicker creates and starts a Ticker.

func (*Ticker) NewLimiter added in v0.11.0

func (ot *Ticker) NewLimiter(limits ...int64) (l *Limiter)

NewLimiter returns a new Limiter using this Ticker. If this Ticker is stopped, returns nil.

If you provide limits, the first will set both read and write limits, the second will set the write limit. Limits are applied in 100ms slices with fractional carry-over between slices, so very low rates are accurate over time but can be bursty at slice boundaries.

To stop the limiter and free it's resources, call Stop.

func (*Ticker) Stop added in v1.0.0

func (ot *Ticker) Stop()

Stop stops the Ticker and closes the current WaitCh channel.

func (*Ticker) WaitCh added in v0.11.0

func (ot *Ticker) WaitCh() (ch <-chan struct{})

WaitCh returns a channel that will close when the current rate limit time slice runs out.

Jump to

Keyboard shortcuts

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