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

Version 13.12.0 #521

Merged
merged 9 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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