Skip to content

Commit

Permalink
Merge pull request #521 from essentialkaos/develop
Browse files Browse the repository at this point in the history
Version 13.12.0
  • Loading branch information
andyone authored Nov 18, 2024
2 parents 916514c + d9d3ed8 commit 34d5214
Show file tree
Hide file tree
Showing 15 changed files with 369 additions and 65 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
## Changelog

### [13.12.0](https://kaos.sh/ek/13.12.0)

* `[req]` Added custom timeout per request
* `[req]` Added `Retrier`
* `[req]` Make `Limiter` public
* `[log]` Added `WithFullCallerPath` option to enable the output of the full caller path
* `[strutil]` Added support of escaped strings to `Fields`
* `[strutil]` Added fuzz tests for `Fields` method
* `[knf]` Fixed build of fuzz tests

### [13.11.0](https://kaos.sh/ek/13.11.0)

* `[req]` Added request limiter
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ ifdef VERBOSE ## Print verbose information (Flag)
VERBOSE_FLAG = -v
endif

COMPAT ?= 1.19
MAKEDIR = $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
GITREV ?= $(shell test -s $(MAKEDIR)/.git && git rev-parse --short HEAD)

Expand Down
2 changes: 1 addition & 1 deletion knf/fuzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
// ////////////////////////////////////////////////////////////////////////////////// //

func Fuzz(data []byte) int {
_, err := readKNFData(bytes.NewReader(data))
_, err := readData(bytes.NewReader(data))

if err != nil {
return 0
Expand Down
25 changes: 15 additions & 10 deletions log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ type Logger struct {
PrefixError bool // Show prefix for error messages
PrefixCrit bool // Show prefix for critical/fatal messages

TimeLayout string // Date and time layout used for rendering dates
UseColors bool // Enable ANSI escape codes for colors in output
UseJSON bool // Encode messages to JSON
WithCaller bool // Show caller info
TimeLayout string // Date and time layout used for rendering dates
UseColors bool // Enable ANSI escape codes for colors in output
UseJSON bool // Encode messages to JSON
WithCaller bool // Show caller info
WithFullCallerPath bool // Show full path of caller

file string
buf bytes.Buffer
Expand Down Expand Up @@ -517,9 +518,9 @@ func (l *Logger) writeText(level uint8, f string, a ...any) error {

if l.WithCaller {
if l.UseColors {
fmtc.Fprintf(&l.buf, "{s-}(%s){!} ", getCallerFromStack())
fmtc.Fprintf(&l.buf, "{s-}(%s){!} ", getCallerFromStack(l.WithFullCallerPath))
} else {
l.buf.WriteString("(" + getCallerFromStack() + ") ")
l.buf.WriteString("(" + getCallerFromStack(l.WithFullCallerPath) + ") ")
}
}

Expand Down Expand Up @@ -577,7 +578,7 @@ func (l *Logger) writeJSON(level uint8, msg string, a ...any) error {
l.writeJSONTimestamp()

if l.WithCaller {
l.buf.WriteString(`"caller":"` + getCallerFromStack() + `",`)
l.buf.WriteString(`"caller":"` + getCallerFromStack(l.WithFullCallerPath) + `",`)
}

operands, fields := splitPayload(a)
Expand Down Expand Up @@ -880,7 +881,7 @@ func splitPayload(payload []any) ([]any, []any) {
}

// getCallerFromStack returns caller function and line from stack
func getCallerFromStack() string {
func getCallerFromStack(full bool) string {
pcs := make([]uintptr, 64)
n := runtime.Callers(2, pcs)

Expand All @@ -902,14 +903,18 @@ func getCallerFromStack() string {
continue
}

return extractCallerFromFrame(frame)
return extractCallerFromFrame(frame, full)
}

return "unknown"
}

// extractCallerFromFrame extracts caller info from frame
func extractCallerFromFrame(f runtime.Frame) string {
func extractCallerFromFrame(f runtime.Frame, full bool) string {
if full {
return f.File + ":" + strconv.Itoa(f.Line)
}

index := strutil.IndexByteSkip(f.File, '/', -1)
return f.File[index+1:] + ":" + strconv.Itoa(f.Line)
}
9 changes: 7 additions & 2 deletions log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package log
import (
"encoding/json"
"os"
"runtime"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -485,8 +486,12 @@ func (ls *LogSuite) TestWithCaller(c *C) {

c.Assert(len(dataSlice), Equals, 3)

c.Assert(dataSlice[0][28:], Equals, "(log/log_test.go:470) Test info 1")
c.Assert(dataSlice[1][28:], Equals, "(log/log_test.go:475) Test info 2")
c.Assert(dataSlice[0][28:], Equals, "(log/log_test.go:471) Test info 1")
c.Assert(dataSlice[1][28:], Equals, "(log/log_test.go:476) Test info 2")

frm := runtime.Frame{File: "/path/to/my/app/code/test.go", Line: 10}
c.Assert(extractCallerFromFrame(frm, true), Equals, "/path/to/my/app/code/test.go:10")
c.Assert(extractCallerFromFrame(frm, false), Equals, "code/test.go:10")
}

func (ls *LogSuite) TestWithFields(c *C) {
Expand Down
18 changes: 18 additions & 0 deletions req/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package req

import (
"fmt"
"time"
)

// ////////////////////////////////////////////////////////////////////////////////// //
Expand Down Expand Up @@ -153,3 +154,20 @@ func ExampleRequest_PostFile() {

fmt.Println("File successfully uploaded!")
}

func ExampleNewRetrier() {
r := NewRetrier()

resp, err := r.Get(
Request{URL: "https://my.domain.com"},
Retry{Num: 5, Status: STATUS_OK, Pause: time.Second},
)

if err != nil {
fmt.Printf("Error: %v\n", err)
return
}

// print status code
fmt.Printf("Status code: %d\n", resp.StatusCode)
}
12 changes: 6 additions & 6 deletions req/limiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,29 @@ import "time"

// ////////////////////////////////////////////////////////////////////////////////// //

// limiter is request limiter
type limiter struct {
// Limiter is request limiter
type Limiter struct {
lastCall time.Time
delay time.Duration
}

// ////////////////////////////////////////////////////////////////////////////////// //

// createLimiter creates new limiter
func createLimiter(rps float64) *limiter {
// NewLimiter creates a new limiter. If rps is less than or equal to 0, it returns nil.
func NewLimiter(rps float64) *Limiter {
if rps <= 0 {
return nil
}

return &limiter{
return &Limiter{
delay: time.Duration(float64(time.Second) / rps),
}
}

// ////////////////////////////////////////////////////////////////////////////////// //

// Wait blocks current goroutine execution until next time slot become available
func (l *limiter) Wait() {
func (l *Limiter) Wait() {
if l == nil {
return
}
Expand Down
66 changes: 41 additions & 25 deletions req/req.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package req

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -189,19 +190,20 @@ type Headers map[string]string

// Request is basic struct
type Request struct {
Method string // Request method
URL string // Request URL
Query Query // Map with query params
Body any // Request body
Headers Headers // Map with headers
ContentType string // Content type header
Accept string // Accept header
BasicAuthUsername string // Basic auth username
BasicAuthPassword string // Basic auth password
BearerAuth string // Bearer auth token
AutoDiscard bool // Automatically discard all responses with status code > 299
FollowRedirect bool // Follow redirect
Close bool // Close indicates whether to close the connection after sending request
Method string // Request method
URL string // Request URL
Query Query // Map with query params
Body any // Request body
Headers Headers // Map with headers
ContentType string // Content type header
Accept string // Accept header
BasicAuthUsername string // Basic auth username
BasicAuthPassword string // Basic auth password
BearerAuth string // Bearer auth token
Timeout time.Duration // Request timeout
AutoDiscard bool // Automatically discard all responses with status code > 299
FollowRedirect bool // Follow redirect
Close bool // Close indicates whether to close the connection after sending request
}

// Response is struct contains response data and properties
Expand All @@ -224,7 +226,7 @@ type Engine struct {
Transport *http.Transport // Transport is default transport struct
Client *http.Client // Client is default client struct

limiter *limiter // Request limiter
limiter *Limiter // Request limiter
dialTimeout float64 // dialTimeout is dial timeout in seconds
requestTimeout float64 // requestTimeout is request timeout in seconds

Expand Down Expand Up @@ -367,6 +369,11 @@ func (e *Engine) Patch(r Request) (*Response, error) {
return e.doRequest(r, PATCH)
}

// Delete sends DELETE request and process response
func (e *Engine) Delete(r Request) (*Response, error) {
return e.doRequest(r, DELETE)
}

// PostFile sends multipart POST request with file data
func (e *Engine) PostFile(r Request, file, fieldName string, extraFields map[string]string) (*Response, error) {
err := configureMultipartRequest(&r, file, fieldName, extraFields)
Expand All @@ -378,11 +385,6 @@ func (e *Engine) PostFile(r Request, file, fieldName string, extraFields map[str
return e.doRequest(r, POST)
}

// Delete sends DELETE request and process response
func (e *Engine) Delete(r Request) (*Response, error) {
return e.doRequest(r, DELETE)
}

// SetUserAgent sets user agent based on app name and version
func (e *Engine) SetUserAgent(app, version string, subs ...string) {
if e == nil {
Expand Down Expand Up @@ -437,7 +439,7 @@ func (e *Engine) SetLimit(rps float64) {
return
}

e.limiter = createLimiter(rps)
e.limiter = NewLimiter(rps)
}

// ////////////////////////////////////////////////////////////////////////////////// //
Expand Down Expand Up @@ -617,7 +619,11 @@ func (e *Engine) doRequest(r Request, method string) (*Response, error) {
r.ContentType = contentType
}

req, err := createRequest(e, r, bodyReader)
req, cancel, err := createRequest(e, r, bodyReader)

if cancel != nil {
defer cancel()
}

if err != nil {
return nil, err
Expand Down Expand Up @@ -678,11 +684,21 @@ func checkEngine(e *Engine) error {
return nil
}

func createRequest(e *Engine, r Request, bodyReader io.Reader) (*http.Request, error) {
req, err := http.NewRequest(r.Method, r.URL, bodyReader)
func createRequest(e *Engine, r Request, bodyReader io.Reader) (*http.Request, context.CancelFunc, error) {
var err error
var req *http.Request
var cancel context.CancelFunc

if r.Timeout != 0 {
var ctx context.Context
ctx, cancel = context.WithTimeout(context.TODO(), r.Timeout)
req, err = http.NewRequestWithContext(ctx, r.Method, r.URL, bodyReader)
} else {
req, err = http.NewRequest(r.Method, r.URL, bodyReader)
}

if err != nil {
return nil, RequestError{ERROR_CREATE_REQUEST, err.Error()}
return nil, nil, RequestError{ERROR_CREATE_REQUEST, err.Error()}
}

if r.Headers != nil && len(r.Headers) != 0 {
Expand Down Expand Up @@ -715,7 +731,7 @@ func createRequest(e *Engine, r Request, bodyReader io.Reader) (*http.Request, e
req.Close = true
}

return req, nil
return req, cancel, nil
}

func configureMultipartRequest(r *Request, file, fieldName string, extraFields map[string]string) error {
Expand Down
Loading

0 comments on commit 34d5214

Please sign in to comment.