Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev->main (WIP) #114

Closed
wants to merge 55 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
01e4853
Tidy up: stop using globals
yunginnanet May 11, 2022
dca759a
Aesthetic: adjust banner version handling
yunginnanet May 11, 2022
64b0373
Minor adjustments
yunginnanet Jul 26, 2022
84b44f5
Adjust robots.txt generation
yunginnanet Aug 25, 2022
9094317
Lint: dead code removal
yunginnanet Aug 25, 2022
b2db55a
Update development versioning mechanism
yunginnanet Sep 11, 2022
c86b090
Selective merge from development branch
yunginnanet Sep 11, 2022
192e18c
Fix: Dev versioning
yunginnanet Sep 11, 2022
20e54b7
Merge branch 'master' into development
yunginnanet Sep 11, 2022
0461e01
Fix: log directory
yunginnanet Sep 11, 2022
7c0edb9
CI: Update
yunginnanet Sep 11, 2022
2210243
Merge branch 'master' into development
yunginnanet Dec 18, 2022
ecfcc06
Lint/refactor
yunginnanet Jan 1, 2023
6d00df0
Merge branch 'master' into development
yunginnanet Jan 3, 2023
141669f
Merge branch 'master' into development
yunginnanet Feb 3, 2023
098e21e
Merge branch 'master' into development
yunginnanet Feb 3, 2023
85897a2
CD: Enable automatic builds and releases
yunginnanet Feb 3, 2023
bcd514c
CD: Fix last commit
yunginnanet Feb 3, 2023
e99b81b
Merge branch 'master' into development
yunginnanet Mar 5, 2023
b8b4b56
Fix: Re-add speedometer for bandwidth metering feature
yunginnanet Mar 5, 2023
c61f6b4
Feat: Fix and finish bandwidth limiting io.Writer
yunginnanet Mar 5, 2023
6d43e2e
CI: Add race detector
yunginnanet Mar 5, 2023
34323c6
Fix: HTTP server failing to listen should be fatal
yunginnanet Mar 23, 2023
4126e2c
Merge branch 'master' into development
yunginnanet Mar 23, 2023
03113b4
Bump git.tcp.direct/kayos/common from 0.8.1 to 0.8.2 (#77)
dependabot[bot] Mar 24, 2023
2bfe90c
Update dependabot.yml
yunginnanet Apr 1, 2023
ba8ee74
Bump actions/setup-go from 2 to 4 (#78)
dependabot[bot] Apr 1, 2023
bca0f44
Bump wangyoucao577/go-release-action from 1.35 to 1.37 (#80)
dependabot[bot] Apr 1, 2023
a635c6d
Bump actions/checkout from 2 to 3 (#79)
dependabot[bot] Apr 1, 2023
9ca252d
Bump peter-evans/create-or-update-comment from 2 to 3 (#82)
dependabot[bot] Apr 6, 2023
3776301
Bump golang.org/x/term from 0.6.0 to 0.7.0 (#81)
dependabot[bot] Apr 6, 2023
2333643
Bump github.com/rs/zerolog from 1.29.0 to 1.29.1 (#83)
dependabot[bot] Apr 14, 2023
bab07d7
Bump github.com/valyala/fasthttp from 1.45.0 to 1.46.0 (#84)
dependabot[bot] Apr 24, 2023
57526ab
Bump wangyoucao577/go-release-action from 1.37 to 1.38 (#86)
dependabot[bot] May 2, 2023
698b4bd
Bump github.com/valyala/fasthttp from 1.46.0 to 1.47.0 (#85)
dependabot[bot] May 2, 2023
3cd2b7a
Bump git.tcp.direct/kayos/common from 0.8.2 to 0.8.3 (#87)
dependabot[bot] May 3, 2023
0005356
Bump golang.org/x/term from 0.7.0 to 0.8.0
dependabot[bot] May 4, 2023
42eea5f
Bump git.tcp.direct/kayos/common from 0.8.3 to 0.8.4
dependabot[bot] May 5, 2023
da957a4
Bump github.com/fasthttp/router from 1.4.18 to 1.4.19
dependabot[bot] May 8, 2023
85ca012
Bump git.tcp.direct/kayos/common from 0.8.4 to 0.8.5
dependabot[bot] May 22, 2023
37ce853
Bump git.tcp.direct/kayos/common from 0.8.5 to 0.8.6
dependabot[bot] May 25, 2023
ec44773
Chore: deps
yunginnanet Aug 12, 2023
d88ed90
Feat[speedo]: flesh out speedo and add TCP transfer test
yunginnanet Oct 19, 2023
e3e0525
Fix[CI](SAST): gosec nosec
yunginnanet Oct 19, 2023
7c5cc69
Chore: tidy up
yunginnanet Oct 19, 2023
7633aa3
Fix: remediate unit test race condition
yunginnanet Oct 19, 2023
3f79f26
Merge branch 'main' into development
yunginnanet Oct 20, 2023
3c39720
Chore: tidy up
yunginnanet Oct 19, 2023
add6bd5
Fix[CI]: gosec nosec (again)
yunginnanet Oct 20, 2023
c58a346
Fix[CI]: fix workflow branch name
yunginnanet Nov 16, 2023
f80435d
Merge branch 'main' into development
yunginnanet Nov 16, 2023
c717e6e
Merge branch 'main' into development
yunginnanet Nov 16, 2023
7e10031
Chore[CD]: Deprecate unused workflow
yunginnanet Nov 16, 2023
7a88e63
Merge branch 'main' into development
yunginnanet Jan 16, 2024
619af98
Merge branch 'main' into development
yunginnanet Jan 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
export PATH=$PATH:$(go env GOPATH)/bin
go install github.com/securego/gosec/v2/cmd/gosec@latest
gosec ./...
- name: go test -v ./...
run: go test -v ./...
- name: go test -race -v ./...
run: go test -race -v ./...
- name: go build -v ./...
run: go build -v ./...
2 changes: 1 addition & 1 deletion cmd/HellPot/HellPot.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func main() {
signal.Notify(stopChan, syscall.SIGINT, syscall.SIGTERM)

go func() {
log.Error().Err(http.Serve()).Msg("HTTP error")
log.Fatal().Err(http.Serve()).Msg("HTTP error")
}()

<-stopChan // wait for SIGINT
Expand Down
16 changes: 10 additions & 6 deletions internal/config/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ var (
logger zerolog.Logger
)

// StartLogger instantiates an instance of our zerolog loggger so we can hook it in our main package.
// While this does return a logger, it should not be used for additional retrievals of the logger. Use GetLogger()
func StartLogger(pretty bool, targets ...io.Writer) zerolog.Logger {
func prepLogDir() {
logDir = snek.GetString("logger.directory")
if !strings.HasSuffix(logDir, "/") {
logDir += "/"
Expand All @@ -29,7 +27,11 @@ func StartLogger(pretty bool, targets ...io.Writer) zerolog.Logger {
println("cannot create log directory: " + logDir + "(" + err.Error() + ")")
os.Exit(1)
}
}

// StartLogger instantiates an instance of our zerolog loggger so we can hook it in our main package.
// While this does return a logger, it should not be used for additional retrievals of the logger. Use GetLogger().
func StartLogger(pretty bool, targets ...io.Writer) zerolog.Logger {
logFileName := "HellPot"

if snek.GetBool("logger.use_date_filename") {
Expand All @@ -44,9 +46,11 @@ func StartLogger(pretty bool, targets ...io.Writer) zerolog.Logger {
case len(targets) > 0:
logFile = io.MultiWriter(targets...)
default:
prepLogDir()
CurrentLogFile = path.Join(logDir, logFileName+".log")
/* #nosec */
if logFile, err = os.OpenFile(CurrentLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666); err != nil {
//nolint:lll
logFile, err = os.OpenFile(CurrentLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666) // #nosec G304 G302 -- we are not using user input to create the file
if err != nil {
println("cannot create log file: " + err.Error())
os.Exit(1)
}
Expand All @@ -62,7 +66,7 @@ func StartLogger(pretty bool, targets ...io.Writer) zerolog.Logger {
return logger
}

// GetLogger retrieves our global logger object
// GetLogger retrieves our global logger object.
func GetLogger() *zerolog.Logger {
// future logic here
return &logger
Expand Down
237 changes: 237 additions & 0 deletions internal/util/speedometer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
package util

import (
"errors"
"fmt"
"io"
"sync"
"sync/atomic"
"time"
)

var ErrLimitReached = errors.New("limit reached")

// Speedometer is an io.Writer wrapper that will limit the rate at which data is written to the underlying target.
//
// It is safe for concurrent use, but writers will block when slowed down.
//
// Optionally, it can be given;
//
// - a capacity, which will cause it to return an error if the capacity is exceeded.
//
// - a speed limit, causing slow downs of data written to the underlying writer if the speed limit is exceeded.
type Speedometer struct {
ceiling int64
speedLimit *SpeedLimit
internal atomics
w io.Writer
}

type atomics struct {
count *atomic.Int64
closed *atomic.Bool
start *sync.Once
stop *sync.Once
birth *atomic.Pointer[time.Time]
duration *atomic.Pointer[time.Duration]
slow *atomic.Bool
}

func newAtomics() atomics {
manhattan := atomics{
count: new(atomic.Int64),
closed: new(atomic.Bool),
start: new(sync.Once),
stop: new(sync.Once),
birth: new(atomic.Pointer[time.Time]),
duration: new(atomic.Pointer[time.Duration]),
slow: new(atomic.Bool),
}
manhattan.birth.Store(&time.Time{})
manhattan.closed.Store(false)
manhattan.count.Store(0)
return manhattan
}

// SpeedLimit is used to limit the rate at which data is written to the underlying writer.
type SpeedLimit struct {
// Burst is the number of bytes that can be written to the underlying writer per Frame.
Burst int
// Frame is the duration of the frame in which Burst can be written to the underlying writer.
Frame time.Duration
// CheckEveryBytes is the number of bytes written before checking if the speed limit has been exceeded.
CheckEveryBytes int
// Delay is the duration to delay writing if the speed limit has been exceeded during a Write call. (blocking)
Delay time.Duration
}

const fallbackDelay = 100

func regulateSpeedLimit(speedLimit *SpeedLimit) (*SpeedLimit, error) {
if speedLimit.Burst <= 0 || speedLimit.Frame <= 0 {
return nil, errors.New("invalid speed limit")
}
if speedLimit.CheckEveryBytes <= 0 {
speedLimit.CheckEveryBytes = speedLimit.Burst
}
if speedLimit.Delay <= 0 {
speedLimit.Delay = fallbackDelay * time.Millisecond
}
return speedLimit, nil
}

func newSpeedometer(w io.Writer, speedLimit *SpeedLimit, ceiling int64) (*Speedometer, error) {
if w == nil {
return nil, errors.New("writer cannot be nil")
}
var err error
if speedLimit != nil {
if speedLimit, err = regulateSpeedLimit(speedLimit); err != nil {
return nil, err
}
}

return &Speedometer{
w: w,
ceiling: ceiling,
speedLimit: speedLimit,
internal: newAtomics(),
}, nil
}

// NewSpeedometer creates a new Speedometer that wraps the given io.Writer.
// It will not limit the rate at which data is written to the underlying writer, it only measures it.
func NewSpeedometer(w io.Writer) (*Speedometer, error) {
return newSpeedometer(w, nil, -1)
}

// NewLimitedSpeedometer creates a new Speedometer that wraps the given io.Writer.
// If the speed limit is exceeded, writes to the underlying writer will be limited.
// See SpeedLimit for more information.
func NewLimitedSpeedometer(w io.Writer, speedLimit *SpeedLimit) (*Speedometer, error) {
return newSpeedometer(w, speedLimit, -1)
}

// NewCappedSpeedometer creates a new Speedometer that wraps the given io.Writer.
// If len(written) bytes exceeds cap, writes to the underlying writer will be ceased permanently for the Speedometer.
func NewCappedSpeedometer(w io.Writer, capacity int64) (*Speedometer, error) {
return newSpeedometer(w, nil, capacity)
}

// NewCappedLimitedSpeedometer creates a new Speedometer that wraps the given io.Writer.
// It is a combination of NewLimitedSpeedometer and NewCappedSpeedometer.
func NewCappedLimitedSpeedometer(w io.Writer, speedLimit *SpeedLimit, capacity int64) (*Speedometer, error) {
return newSpeedometer(w, speedLimit, capacity)
}

func (s *Speedometer) increment(inc int64) (int, error) {
if s.internal.closed.Load() {
return 0, io.ErrClosedPipe
}
var err error
if s.ceiling > 0 && s.Total()+inc > s.ceiling {
_ = s.Close()
err = ErrLimitReached
inc = s.ceiling - s.Total()
}
s.internal.count.Add(inc)
return int(inc), err
}

// Running returns true if the Speedometer is still running.
func (s *Speedometer) Running() bool {
return !s.internal.closed.Load()
}

// Total returns the total number of bytes written to the underlying writer.
func (s *Speedometer) Total() int64 {
return s.internal.count.Load()
}

// Close stops the Speedometer. No additional writes will be accepted.
func (s *Speedometer) Close() error {
if s.internal.closed.Load() {
return io.ErrClosedPipe
}
s.internal.stop.Do(func() {
s.internal.closed.Store(true)
stopped := time.Now()
birth := s.internal.birth.Load()
duration := stopped.Sub(*birth)
s.internal.duration.Store(&duration)
})
return nil
}

/*func (s *Speedometer) IsSlow() bool {
return s.internal.slow.Load()
}*/

// Rate returns the rate at which data is being written to the underlying writer per second.
func (s *Speedometer) Rate() float64 {
if s.internal.closed.Load() {
return float64(s.Total()) / s.internal.duration.Load().Seconds()
}
return float64(s.Total()) / time.Since(*s.internal.birth.Load()).Seconds()
}

func (s *Speedometer) slowDown() error {
switch {
case s.speedLimit == nil:
return nil
case s.speedLimit.Burst <= 0 || s.speedLimit.Frame <= 0,
s.speedLimit.CheckEveryBytes <= 0, s.speedLimit.Delay <= 0:
return errors.New("invalid speed limit")
default:
//
}
if s.Total()%int64(s.speedLimit.CheckEveryBytes) != 0 {
return nil
}
s.internal.slow.Store(true)
for s.Rate() > float64(s.speedLimit.Burst)/s.speedLimit.Frame.Seconds() {
time.Sleep(s.speedLimit.Delay)
}
s.internal.slow.Store(false)
return nil
}

// Write writes p to the underlying writer, following all defined speed limits.
func (s *Speedometer) Write(p []byte) (n int, err error) {
if s.internal.closed.Load() {
return 0, io.ErrClosedPipe
}
s.internal.start.Do(func() {
now := time.Now()
s.internal.birth.Store(&now)
})

// if no speed limit, just write and record
if s.speedLimit == nil {
n, err = s.w.Write(p)
if err != nil {
return n, fmt.Errorf("error writing to underlying writer: %w", err)
}
return s.increment(int64(len(p)))
}

var (
wErr error
accepted int
)
accepted, wErr = s.increment(int64(len(p)))

if wErr != nil {
return 0, fmt.Errorf("error incrementing: %w", wErr)
}

if sErr := s.slowDown(); sErr != nil {
return 0, fmt.Errorf("error slowing down: %w", sErr)
}

var iErr error
if n, iErr = s.w.Write(p[:accepted]); iErr != nil {
return n, fmt.Errorf("error writing to underlying writer: %w", iErr)
}
return
}
Loading