Skip to content

Commit 99f9893

Browse files
authored
Merge branch 'main' into feat/cbor
2 parents 23ef579 + 1bfbb50 commit 99f9893

28 files changed

+655
-145
lines changed

Diff for: .github/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ Fiber is an open-source project that runs on donations to pay the bills, e.g., o
746746
| ---------------------------------------------------------- | ------------------------------------------------ | -------- |
747747
| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 |
748748
| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 |
749-
| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 |
749+
| <img src="https://avatars.githubusercontent.com/u/56607882?s=25" alt="thomasvvugt" style="width: 25px; height: 25px;"> | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 |
750750
| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 |
751751
| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 |
752752
| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 |

Diff for: .github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232

3333
- name: Upload coverage reports to Codecov
3434
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.23.x' }}
35-
uses: codecov/[email protected].4
35+
uses: codecov/[email protected].7
3636
with:
3737
token: ${{ secrets.CODECOV_TOKEN }}
3838
file: ./coverage.txt

Diff for: app.go

+37-9
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import (
1414
"encoding/xml"
1515
"errors"
1616
"fmt"
17+
"io"
1718
"net"
1819
"net/http"
1920
"net/http/httputil"
21+
"os"
2022
"reflect"
2123
"strconv"
2224
"strings"
@@ -921,13 +923,33 @@ func (app *App) Hooks() *Hooks {
921923
return app.hooks
922924
}
923925

926+
// TestConfig is a struct holding Test settings
927+
type TestConfig struct {
928+
// Timeout defines the maximum duration a
929+
// test can run before timing out.
930+
// Default: time.Second
931+
Timeout time.Duration
932+
933+
// FailOnTimeout specifies whether the test
934+
// should return a timeout error if the HTTP response
935+
// exceeds the Timeout duration.
936+
// Default: true
937+
FailOnTimeout bool
938+
}
939+
924940
// Test is used for internal debugging by passing a *http.Request.
925-
// Timeout is optional and defaults to 1s, -1 will disable it completely.
926-
func (app *App) Test(req *http.Request, timeout ...time.Duration) (*http.Response, error) {
927-
// Set timeout
928-
to := 1 * time.Second
929-
if len(timeout) > 0 {
930-
to = timeout[0]
941+
// Config is optional and defaults to a 1s error on timeout,
942+
// 0 timeout will disable it completely.
943+
func (app *App) Test(req *http.Request, config ...TestConfig) (*http.Response, error) {
944+
// Default config
945+
cfg := TestConfig{
946+
Timeout: time.Second,
947+
FailOnTimeout: true,
948+
}
949+
950+
// Override config if provided
951+
if len(config) > 0 {
952+
cfg = config[0]
931953
}
932954

933955
// Add Content-Length if not provided with body
@@ -966,12 +988,15 @@ func (app *App) Test(req *http.Request, timeout ...time.Duration) (*http.Respons
966988
}()
967989

968990
// Wait for callback
969-
if to >= 0 {
991+
if cfg.Timeout > 0 {
970992
// With timeout
971993
select {
972994
case err = <-channel:
973-
case <-time.After(to):
974-
return nil, fmt.Errorf("test: timeout error after %s", to)
995+
case <-time.After(cfg.Timeout):
996+
conn.Close() //nolint:errcheck, revive // It is fine to ignore the error here
997+
if cfg.FailOnTimeout {
998+
return nil, os.ErrDeadlineExceeded
999+
}
9751000
}
9761001
} else {
9771002
// Without timeout
@@ -989,6 +1014,9 @@ func (app *App) Test(req *http.Request, timeout ...time.Duration) (*http.Respons
9891014
// Convert raw http response to *http.Response
9901015
res, err := http.ReadResponse(buffer, req)
9911016
if err != nil {
1017+
if errors.Is(err, io.ErrUnexpectedEOF) {
1018+
return nil, errors.New("test: got empty response")
1019+
}
9921020
return nil, fmt.Errorf("failed to read response: %w", err)
9931021
}
9941022

Diff for: app_test.go

+32-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"net"
1717
"net/http"
1818
"net/http/httptest"
19+
"os"
1920
"reflect"
2021
"regexp"
2122
"runtime"
@@ -1124,7 +1125,9 @@ func Test_Test_Timeout(t *testing.T) {
11241125

11251126
app.Get("/", testEmptyHandler)
11261127

1127-
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), -1)
1128+
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), TestConfig{
1129+
Timeout: 0,
1130+
})
11281131
require.NoError(t, err, "app.Test(req)")
11291132
require.Equal(t, 200, resp.StatusCode, "Status code")
11301133

@@ -1133,7 +1136,10 @@ func Test_Test_Timeout(t *testing.T) {
11331136
return nil
11341137
})
11351138

1136-
_, err = app.Test(httptest.NewRequest(MethodGet, "/timeout", nil), 20*time.Millisecond)
1139+
_, err = app.Test(httptest.NewRequest(MethodGet, "/timeout", nil), TestConfig{
1140+
Timeout: 20 * time.Millisecond,
1141+
FailOnTimeout: true,
1142+
})
11371143
require.Error(t, err, "app.Test(req)")
11381144
}
11391145

@@ -1432,7 +1438,9 @@ func Test_App_Test_no_timeout_infinitely(t *testing.T) {
14321438
})
14331439

14341440
req := httptest.NewRequest(MethodGet, "/", nil)
1435-
_, err = app.Test(req, -1)
1441+
_, err = app.Test(req, TestConfig{
1442+
Timeout: 0,
1443+
})
14361444
}()
14371445

14381446
tk := time.NewTimer(5 * time.Second)
@@ -1460,8 +1468,27 @@ func Test_App_Test_timeout(t *testing.T) {
14601468
return nil
14611469
})
14621470

1463-
_, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), 100*time.Millisecond)
1464-
require.Equal(t, errors.New("test: timeout error after 100ms"), err)
1471+
_, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), TestConfig{
1472+
Timeout: 100 * time.Millisecond,
1473+
FailOnTimeout: true,
1474+
})
1475+
require.Equal(t, os.ErrDeadlineExceeded, err)
1476+
}
1477+
1478+
func Test_App_Test_timeout_empty_response(t *testing.T) {
1479+
t.Parallel()
1480+
1481+
app := New()
1482+
app.Get("/", func(_ Ctx) error {
1483+
time.Sleep(1 * time.Second)
1484+
return nil
1485+
})
1486+
1487+
_, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), TestConfig{
1488+
Timeout: 100 * time.Millisecond,
1489+
FailOnTimeout: false,
1490+
})
1491+
require.Equal(t, errors.New("test: got empty response"), err)
14651492
}
14661493

14671494
func Test_App_SetTLSHandler(t *testing.T) {

Diff for: bind.go

+17-16
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,35 @@ type StructValidator interface {
1919

2020
// Bind struct
2121
type Bind struct {
22-
ctx Ctx
23-
should bool
22+
ctx Ctx
23+
dontHandleErrs bool
2424
}
2525

26-
// Should To handle binder errors manually, you can prefer Should method.
26+
// If you want to handle binder errors manually, you can use `WithoutAutoHandling`.
2727
// It's default behavior of binder.
28-
func (b *Bind) Should() *Bind {
29-
b.should = true
28+
func (b *Bind) WithoutAutoHandling() *Bind {
29+
b.dontHandleErrs = true
3030

3131
return b
3232
}
3333

34-
// Must If you want to handle binder errors automatically, you can use Must.
35-
// If there's an error it'll return error and 400 as HTTP status.
36-
func (b *Bind) Must() *Bind {
37-
b.should = false
34+
// If you want to handle binder errors automatically, you can use `WithAutoHandling`.
35+
// If there's an error, it will return the error and set HTTP status to `400 Bad Request`.
36+
// You must still return on error explicitly
37+
func (b *Bind) WithAutoHandling() *Bind {
38+
b.dontHandleErrs = false
3839

3940
return b
4041
}
4142

42-
// Check Should/Must errors and return it by usage.
43+
// Check WithAutoHandling/WithoutAutoHandling errors and return it by usage.
4344
func (b *Bind) returnErr(err error) error {
44-
if !b.should {
45-
b.ctx.Status(StatusBadRequest)
46-
return NewError(StatusBadRequest, "Bad request: "+err.Error())
45+
if err == nil || b.dontHandleErrs {
46+
return err
4747
}
4848

49-
return err
49+
b.ctx.Status(StatusBadRequest)
50+
return NewError(StatusBadRequest, "Bad request: "+err.Error())
5051
}
5152

5253
// Struct validation.
@@ -62,7 +63,7 @@ func (b *Bind) validateStruct(out any) error {
6263
// Custom To use custom binders, you have to use this method.
6364
// You can register them from RegisterCustomBinder method of Fiber instance.
6465
// They're checked by name, if it's not found, it will return an error.
65-
// NOTE: Should/Must is still valid for Custom binders.
66+
// NOTE: WithAutoHandling/WithAutoHandling is still valid for Custom binders.
6667
func (b *Bind) Custom(name string, dest any) error {
6768
binders := b.ctx.App().customBinders
6869
for _, customBinder := range binders {
@@ -92,7 +93,7 @@ func (b *Bind) RespHeader(out any) error {
9293
return b.validateStruct(out)
9394
}
9495

95-
// Cookie binds the requesr cookie strings into the struct, map[string]string and map[string][]string.
96+
// Cookie binds the request cookie strings into the struct, map[string]string and map[string][]string.
9697
// NOTE: If your cookie is like key=val1,val2; they'll be binded as an slice if your map is map[string][]string. Else, it'll use last element of cookie.
9798
func (b *Bind) Cookie(out any) error {
9899
if err := b.returnErr(binder.CookieBinder.Bind(b.ctx.RequestCtx(), out)); err != nil {

Diff for: bind_test.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ import (
2020

2121
const helloWorld = "hello world"
2222

23+
// go test -run Test_returnErr -v
24+
func Test_returnErr(t *testing.T) {
25+
app := New()
26+
c := app.AcquireCtx(&fasthttp.RequestCtx{})
27+
28+
err := c.Bind().WithAutoHandling().returnErr(nil)
29+
require.NoError(t, err)
30+
}
31+
2332
// go test -run Test_Bind_Query -v
2433
func Test_Bind_Query(t *testing.T) {
2534
t.Parallel()
@@ -1653,8 +1662,8 @@ func Test_Bind_CustomBinder(t *testing.T) {
16531662
require.Equal(t, "john", d.Name)
16541663
}
16551664

1656-
// go test -run Test_Bind_Must
1657-
func Test_Bind_Must(t *testing.T) {
1665+
// go test -run Test_Bind_WithAutoHandling
1666+
func Test_Bind_WithAutoHandling(t *testing.T) {
16581667
app := New()
16591668
c := app.AcquireCtx(&fasthttp.RequestCtx{})
16601669

@@ -1663,7 +1672,7 @@ func Test_Bind_Must(t *testing.T) {
16631672
}
16641673
rq := new(RequiredQuery)
16651674
c.Request().URI().SetQueryString("")
1666-
err := c.Bind().Must().Query(rq)
1675+
err := c.Bind().WithAutoHandling().Query(rq)
16671676
require.Equal(t, StatusBadRequest, c.Response().StatusCode())
16681677
require.Equal(t, "Bad request: bind: name is empty", err.Error())
16691678
}

0 commit comments

Comments
 (0)