Repository: gavv/httpexpect
Pull request #414 (2023-04-27 03:58:43)

CLose https://github.com/gavv/httpexpect/issues/343
This wil close feature: https://github.com/gavv/httpexpect/issues/343


Patch:
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index cd16f96..dd2452e 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -1,11 +1,12 @@
 name: build
 
 on:
+  workflow_dispatch:
+
   pull_request:
     branches:
       - master
       - v*
-
   push:
     branches:
       - master
@@ -15,21 +16,22 @@ on:
 
 jobs:
   build:
-    runs-on: ubuntu-latest
-
     strategy:
       matrix:
-        go: [1.17, 1.x]
+        go: [1.19, 1.x]
+        os: [ubuntu-latest, windows-latest, macos-latest]
 
-    name: Go ${{ matrix.go }}
+    runs-on: ${{ matrix.os }}
+    name: Go ${{ matrix.go }} ${{ matrix.os }}
     steps:
       - name: Checkout
         uses: actions/checkout@v3
 
       - name: Install Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4.0.1
         with:
           go-version: ${{ matrix.go }}
+          cache: true
 
       - name: Build
         run: go get -v ./...
@@ -38,17 +40,21 @@ jobs:
         run: go test ./...
 
   examples:
-    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        os: [ubuntu-latest, windows-latest, macos-latest]
 
-    name: Examples
+    runs-on: ${{ matrix.os }}
+    name: Examples ${{ matrix.os }}
     steps:
       - name: Checkout
         uses: actions/checkout@v3
 
       - name: Install Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4.0.1
         with:
           go-version: 1.x
+          cache: true
 
       - name: Build
         run: cd _examples && go get -v .
@@ -104,15 +110,18 @@ jobs:
         uses: actions/checkout@v3
 
       - name: Install Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4.0.1
         with:
           go-version: 1.x
+          cache: false
 
       - name: Run linters
         uses: golangci/golangci-lint-action@v3
+        timeout-minutes: 20
         with:
           version: v1.51.2
           working-directory: ${{ matrix.dir }}
+          args: --timeout=20m
 
   coverage:
     runs-on: ubuntu-latest
@@ -124,9 +133,10 @@ jobs:
         uses: actions/checkout@v3
 
       - name: Install Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4.0.1
         with:
           go-version: 1.x
+          cache: true
 
       - name: Run tests
         run: go test -covermode=count -coverprofile=coverage.out -coverpkg=. ./...
diff --git a/README.md b/README.md
index 5a62436..6a9971e 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ Workflow:
 * Failures are reported using [`testify`](https://github.com/stretchr/testify/) (`assert` or `require` package) or standard `testing` package.
 * JSON values are pretty-printed using `encoding/json`, Go values are pretty-printed using [`litter`](https://github.com/sanity-io/litter).
 * Dumping requests and responses in various formats, using [`httputil`](https://golang.org/pkg/net/http/httputil/), [`http2curl`](https://github.com/moul/http2curl), or simple compact logger.
-* Color support using [`fatih/color`](github.com/fatih/color).
+* Color support using [`fatih/color`](https://github.com/fatih/color).
 
 ##### Tuning
 
diff --git a/_examples/go.mod b/_examples/go.mod
index b9e3bd3..3803baf 100644
--- a/_examples/go.mod
+++ b/_examples/go.mod
@@ -6,12 +6,12 @@ require (
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/fasthttp/websocket v1.5.0
 	github.com/gavv/httpexpect/v2 v2.15.0
-	github.com/gin-gonic/gin v1.8.1
+	github.com/gin-gonic/gin v1.9.1
 	github.com/go-oauth2/oauth2/v4 v4.5.1
 	github.com/gorilla/websocket v1.5.0
 	github.com/kataras/iris/v12 v12.2.0
 	github.com/labstack/echo/v4 v4.10.0
-	github.com/valyala/fasthttp v1.46.0
+	github.com/valyala/fasthttp v1.50.0
 	golang.org/x/oauth2 v0.4.0
 	google.golang.org/appengine v1.6.7
 )
@@ -25,17 +25,20 @@ require (
 	github.com/ajg/form v1.5.1 // indirect
 	github.com/andybalholm/brotli v1.0.5 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
+	github.com/bytedance/sonic v1.9.1 // indirect
+	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect
 	github.com/fatih/color v1.15.0 // indirect
 	github.com/fatih/structs v1.1.0 // indirect
 	github.com/flosch/pongo2/v4 v4.0.2 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
-	github.com/go-playground/locales v0.14.0 // indirect
-	github.com/go-playground/universal-translator v0.18.0 // indirect
-	github.com/go-playground/validator/v10 v10.11.1 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.14.0 // indirect
 	github.com/gobwas/glob v0.2.3 // indirect
-	github.com/goccy/go-json v0.9.11 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/golang/snappy v0.0.4 // indirect
@@ -51,18 +54,19 @@ require (
 	github.com/kataras/pio v0.0.11 // indirect
 	github.com/kataras/sitemap v0.0.6 // indirect
 	github.com/kataras/tunnel v0.0.4 // indirect
-	github.com/klauspost/compress v1.16.5 // indirect
+	github.com/klauspost/compress v1.17.0 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
 	github.com/labstack/gommon v0.4.0 // indirect
-	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/leodido/go-urn v1.2.4 // indirect
 	github.com/mailgun/raymond/v2 v2.0.48 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
-	github.com/mattn/go-isatty v0.0.18 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
 	github.com/microcosm-cc/bluemonday v1.0.23 // indirect
 	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
-	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
+	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sanity-io/litter v1.5.5 // indirect
@@ -71,7 +75,7 @@ require (
 	github.com/sergi/go-diff v1.3.1 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/smartystreets/goconvey v1.7.2 // indirect
-	github.com/stretchr/testify v1.8.2 // indirect
+	github.com/stretchr/testify v1.8.4 // indirect
 	github.com/tdewolff/minify/v2 v2.12.4 // indirect
 	github.com/tdewolff/parse/v2 v2.6.4 // indirect
 	github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 // indirect
@@ -82,7 +86,8 @@ require (
 	github.com/tidwall/pretty v1.2.0 // indirect
 	github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e // indirect
 	github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 // indirect
-	github.com/ugorji/go/codec v1.2.7 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.11 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/valyala/fasttemplate v1.2.2 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
@@ -94,14 +99,14 @@ require (
 	github.com/yosssi/ace v0.0.5 // indirect
 	github.com/yudai/gojsondiff v1.0.0 // indirect
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
-	golang.org/x/crypto v0.7.0 // indirect
-	golang.org/x/net v0.9.0 // indirect
-	golang.org/x/sys v0.7.0 // indirect
-	golang.org/x/text v0.9.0 // indirect
+	golang.org/x/arch v0.3.0 // indirect
+	golang.org/x/crypto v0.13.0 // indirect
+	golang.org/x/net v0.15.0 // indirect
+	golang.org/x/sys v0.12.0 // indirect
+	golang.org/x/text v0.13.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
-	google.golang.org/protobuf v1.29.1 // indirect
+	google.golang.org/protobuf v1.30.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	moul.io/http2curl/v2 v2.3.0 // indirect
 )
diff --git a/_examples/go.sum b/_examples/go.sum
index 1d37b9a..31c9008 100644
--- a/_examples/go.sum
+++ b/_examples/go.sum
@@ -18,8 +18,13 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
 github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
 github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
+github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
 github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
 github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -43,29 +48,30 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
 github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
 github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
 github.com/gavv/httpexpect/v2 v2.15.0 h1:CCnFk9of4l4ijUhnMxyoEpJsIIBKcuWIFLMwwGTZxNs=
 github.com/gavv/httpexpect/v2 v2.15.0/go.mod h1:7myOP3A3VyS4+qnA4cm8DAad8zMN+7zxDB80W9f8yIc=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
-github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
+github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
 github.com/go-oauth2/oauth2/v4 v4.5.1 h1:3vxp+cjLqDe1TbogbwtMyeHRHr1tD+ksrK7xNppYRDs=
 github.com/go-oauth2/oauth2/v4 v4.5.1/go.mod h1:wk/2uLImWIa9VVQDgxz99H2GDbhmfi/9/Xr+GvkSUSQ=
-github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
-github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
-github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
-github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
-github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
-github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
-github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
 github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
 github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
-github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
@@ -129,22 +135,21 @@ github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
 github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
 github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
-github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
+github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
 github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ=
 github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
-github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
-github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
 github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
 github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@@ -157,8 +162,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY=
 github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgStuPzlK76QuruE/z4=
 github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
@@ -170,6 +175,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
 github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -180,16 +186,12 @@ github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1ls
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
-github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
+github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
 github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
-github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
@@ -225,8 +227,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
 github.com/tdewolff/minify/v2 v2.12.4 h1:kejsHQMM17n6/gwdw53qsi6lg0TGddZADVyQOz1KMdE=
 github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk=
@@ -253,15 +257,16 @@ github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2K
 github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao=
 github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE=
 github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ=
-github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
-github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
-github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 github.com/valyala/fasthttp v1.33.0/go.mod h1:KJRK/MXx0J+yd0c5hlR+s1tIHD72sniU8ZJjl97LIw4=
 github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0=
-github.com/valyala/fasthttp v1.46.0 h1:6ZRhrFg8zBXTRYY6vdzbFhqsBd7FVv123pV2m9V87U4=
-github.com/valyala/fasthttp v1.46.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
+github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
+github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
 github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@@ -289,14 +294,16 @@ github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcm
 github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
@@ -314,8 +321,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
 golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
@@ -340,7 +347,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -348,10 +354,11 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -359,8 +366,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -385,15 +392,12 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=
-google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -411,3 +415,4 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
 moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/assertion.go b/assertion.go
index a66a007..d8dcb77 100644
--- a/assertion.go
+++ b/assertion.go
@@ -192,6 +192,9 @@ type AssertionFailure struct {
 
 	// Allowed delta between actual and expected
 	Delta *AssertionValue
+
+	// Stacktrace of the failure
+	Stacktrace []StacktraceEntry
 }
 
 // AssertionValue holds expected or actual value
diff --git a/binder.go b/binder.go
index 64de974..f1b3d17 100644
--- a/binder.go
+++ b/binder.go
@@ -4,7 +4,7 @@ import (
 	"bytes"
 	"crypto/tls"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net"
 	"net/http"
 	"net/http/httptest"
@@ -78,7 +78,7 @@ func (binder Binder) RoundTrip(origReq *http.Request) (*http.Response, error) {
 	}
 
 	if recorder.Body != nil {
-		resp.Body = ioutil.NopCloser(recorder.Body)
+		resp.Body = io.NopCloser(recorder.Body)
 	}
 
 	return &resp, nil
@@ -148,7 +148,7 @@ func (binder FastBinder) RoundTrip(stdreq *http.Request) (*http.Response, error)
 	}
 
 	if stdreq.Body != nil {
-		b, err := ioutil.ReadAll(stdreq.Body)
+		b, err := io.ReadAll(stdreq.Body)
 		if err == nil {
 			ctx.Request.SetBody(b)
 		}
@@ -219,9 +219,9 @@ func fast2std(stdreq *http.Request, fastresp *fasthttp.Response) *http.Response
 	}
 
 	if body != nil {
-		stdresp.Body = ioutil.NopCloser(bytes.NewReader(body))
+		stdresp.Body = io.NopCloser(bytes.NewReader(body))
 	} else {
-		stdresp.Body = ioutil.NopCloser(bytes.NewReader(nil))
+		stdresp.Body = io.NopCloser(bytes.NewReader(nil))
 	}
 
 	return stdresp
diff --git a/body_wrapper.go b/body_wrapper.go
index 28b0502..cf733e1 100644
--- a/body_wrapper.go
+++ b/body_wrapper.go
@@ -5,7 +5,6 @@ import (
 	"context"
 	"errors"
 	"io"
-	"io/ioutil"
 	"runtime"
 	"sync"
 )
@@ -45,7 +44,7 @@ type bodyWrapper struct {
 
 	// Reader for HTTP response body stored in memory.
 	// Rewind() resets this reader to start from the beginning.
-	memReader io.Reader
+	memReader *bytes.Reader
 
 	// HTTP response body stored in memory.
 	memBytes []byte
@@ -123,6 +122,11 @@ func (bw *bodyWrapper) Close() error {
 	// Reset memory reader.
 	bw.memReader = bytes.NewReader(nil)
 
+	// Free memory when rewind is disabled.
+	if bw.isRewindDisabled {
+		bw.memBytes = nil
+	}
+
 	return err
 }
 
@@ -178,7 +182,7 @@ func (bw *bodyWrapper) GetBody() (io.ReadCloser, error) {
 	}
 
 	// Return fresh reader for memory chunk.
-	return ioutil.NopCloser(bytes.NewReader(bw.memBytes)), nil
+	return io.NopCloser(bytes.NewReader(bw.memBytes)), nil
 }
 
 // Disables storing body contents in memory and clears the cache.
@@ -186,14 +190,31 @@ func (bw *bodyWrapper) DisableRewinds() {
 	bw.mu.Lock()
 	defer bw.mu.Unlock()
 
+	// Free memory if reading from original HTTP response, or reading from memory
+	// and memory reader has nothing left to read.
+	// Otherwise, i.e. when we're reading from memory, and there is more to read,
+	// memReadNext() will free memory later when it hits EOF.
+	if !bw.isFullyRead || bw.memReader.Len() == 0 {
+		bw.memReader = bytes.NewReader(nil)
+		bw.memBytes = nil
+	}
+
 	bw.isRewindDisabled = true
 }
 
 func (bw *bodyWrapper) memReadNext(p []byte) (int, error) {
 	n, err := bw.memReader.Read(p)
 
-	if err == io.EOF && bw.readErr != nil {
-		err = bw.readErr
+	if err == io.EOF {
+		// Free memory after we hit EOF when reading from memory,
+		// if rewinds were disabled while we were reading from it.
+		if bw.isRewindDisabled {
+			bw.memReader = bytes.NewReader(nil)
+			bw.memBytes = nil
+		}
+		if bw.readErr != nil {
+			err = bw.readErr
+		}
 	}
 
 	return n, err
@@ -223,7 +244,7 @@ func (bw *bodyWrapper) httpReadNext(p []byte) (int, error) {
 }
 
 func (bw *bodyWrapper) httpReadFull() error {
-	b, err := ioutil.ReadAll(bw.httpReader)
+	b, err := io.ReadAll(bw.httpReader)
 
 	// Switch to reading from memory.
 	bw.isFullyRead = true
diff --git a/chain.go b/chain.go
index cee3fcf..94fc11e 100644
--- a/chain.go
+++ b/chain.go
@@ -449,6 +449,9 @@ func (c *chain) fail(failure AssertionFailure) {
 	if c.severity == SeverityError {
 		failure.IsFatal = true
 	}
+
+	failure.Stacktrace = stacktrace()
+
 	c.failure = &failure
 }
 
diff --git a/formatter.go b/formatter.go
index f0f3729..7b84396 100644
--- a/formatter.go
+++ b/formatter.go
@@ -8,6 +8,7 @@ import (
 	"math"
 	"net/http/httputil"
 	"os"
+	"path/filepath"
 	"regexp"
 	"strconv"
 	"strings"
@@ -15,6 +16,7 @@ import (
 	"testing"
 	"text/template"
 
+	"github.com/TylerBrock/colorjson"
 	"github.com/fatih/color"
 	"github.com/mattn/go-isatty"
 	"github.com/mitchellh/go-wordwrap"
@@ -67,6 +69,10 @@ type DefaultFormatter struct {
 	// Default is FloatFormatAuto.
 	FloatFormat FloatFormat
 
+	// Defines whether to print stacktrace on failure and in what format.
+	// Default is StacktraceModeDisabled.
+	StacktraceMode StacktraceMode
+
 	// Colorization mode.
 	// Default is ColorModeAuto.
 	ColorMode ColorMode
@@ -151,6 +157,20 @@ const (
 	FloatFormatScientific
 )
 
+// StacktraceMode defines the format of stacktrace.
+type StacktraceMode int
+
+const (
+	// Don't print stacktrace.
+	StacktraceModeDisabled StacktraceMode = iota
+
+	// Standard, verbose format.
+	StacktraceModeStandard
+
+	// Compact format.
+	StacktraceModeCompact
+)
+
 // ColorMode defines how the text color is enabled.
 type ColorMode int
 
@@ -210,6 +230,9 @@ type FormatData struct {
 	HaveResponse bool
 	Response     string
 
+	HaveStacktrace bool
+	Stacktrace     []string
+
 	EnableColors bool
 	LineWidth    int
 }
@@ -285,6 +308,7 @@ func (f *DefaultFormatter) buildFormatData(
 
 		f.fillRequest(&data, ctx, failure)
 		f.fillResponse(&data, ctx, failure)
+		f.fillStacktrace(&data, ctx, failure)
 	}
 
 	return &data
@@ -306,12 +330,6 @@ func (f *DefaultFormatter) fillGeneral(
 		}
 	}
 
-	if f.LineWidth != 0 {
-		data.LineWidth = f.LineWidth
-	} else {
-		data.LineWidth = defaultLineWidth
-	}
-
 	switch f.ColorMode {
 	case ColorModeAuto:
 		switch colorMode() {
@@ -327,6 +345,12 @@ func (f *DefaultFormatter) fillGeneral(
 	case ColorModeNever:
 		data.EnableColors = false
 	}
+
+	if f.LineWidth != 0 {
+		data.LineWidth = f.LineWidth
+	} else {
+		data.LineWidth = defaultLineWidth
+	}
 }
 
 func (f *DefaultFormatter) fillErrors(
@@ -547,6 +571,38 @@ func (f *DefaultFormatter) fillResponse(
 	}
 }
 
+func (f *DefaultFormatter) fillStacktrace(
+	data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
+) {
+	data.Stacktrace = []string{}
+
+	switch f.StacktraceMode {
+	case StacktraceModeDisabled:
+		break
+
+	case StacktraceModeStandard:
+		for _, entry := range failure.Stacktrace {
+			data.HaveStacktrace = true
+			data.Stacktrace = append(data.Stacktrace,
+				fmt.Sprintf("%s()\n\t%s:%d +0x%x",
+					entry.Func.Name(), entry.File, entry.Line, entry.FuncOffset))
+
+		}
+
+	case StacktraceModeCompact:
+		for _, entry := range failure.Stacktrace {
+			if entry.IsEntrypoint {
+				break
+			}
+			data.HaveStacktrace = true
+			data.Stacktrace = append(data.Stacktrace,
+				fmt.Sprintf("%s() at %s:%d (%s)",
+					entry.FuncName, filepath.Base(entry.File), entry.Line, entry.FuncPackage))
+
+		}
+	}
+}
+
 func (f *DefaultFormatter) formatValue(value interface{}) string {
 	if flt := extractFloat32(value); flt != nil {
 		return f.reformatNumber(f.formatFloatValue(*flt, 32))
@@ -953,6 +1009,57 @@ var defaultTemplateFuncs = template.FuncMap{
 		}
 		return color.New(colorAttr).Sprint(input)
 	},
+	"colorhttp": func(enable bool, colorName string, isResponse bool, input string) string {
+		if !enable {
+			return input
+		}
+
+		colorAttr := color.Reset
+		present := true
+		var sb strings.Builder
+		if ca, ok := defaultColors[colorName]; ok {
+			colorAttr = ca
+		}
+
+		httpMethod := map[string]bool{
+			"GET":    present,
+			"POST":   present,
+			"PATCH":  present,
+			"DELETE": present,
+			"PUT":    present,
+		}
+
+		getColor := func(word string, forceApplyColor bool) color.Attribute {
+			if v := httpMethod[word]; v == present {
+				return colorAttr
+			}
+			if word[len(word)-1] == ':' {
+				return colorAttr
+			}
+			if forceApplyColor {
+				return colorAttr
+			}
+
+			return color.Reset
+		}
+		isFirstLine := true
+		for _, line := range strings.Split(input, "\n") {
+			if sb.Len() != 0 {
+				sb.WriteString("\n")
+			}
+
+			words := strings.Fields(line)
+			wordLen := len(words)
+			for index, word := range words {
+				forceApplyColor := index != wordLen-1 && isResponse && isFirstLine
+				sb.WriteString(color.New(getColor(word, forceApplyColor)).Sprint(word))
+				sb.WriteString(" ")
+			}
+			isFirstLine = false
+		}
+
+		return sb.String()
+	},
 	"colordiff": func(enable bool, input string) string {
 		if !enable {
 			return input
@@ -989,6 +1096,37 @@ var defaultTemplateFuncs = template.FuncMap{
 
 		return sb.String()
 	},
+	"colorjson": func(enable bool, colorName, input string) string {
+		if !enable {
+			return input
+		}
+
+		fallbackColor := color.Reset
+		if attr, ok := defaultColors[colorName]; ok {
+			fallbackColor = attr
+		}
+
+		var parsedInput interface{}
+		err := json.Unmarshal([]byte(input), &parsedInput)
+		if err != nil {
+			return color.New(fallbackColor).Sprint(input)
+		}
+
+		formatter := colorjson.NewFormatter()
+		formatter.KeyColor = color.New(color.Reset)
+		formatter.StringColor = color.New(defaultColors["HiMagenta"])
+		formatter.NumberColor = color.New(defaultColors["Cyan"])
+		formatter.BoolColor = color.New(defaultColors["Cyan"])
+		formatter.NullColor = color.New(defaultColors["Cyan"])
+		formatter.Indent = 2
+
+		b, err := formatter.Marshal(parsedInput)
+		if err != nil {
+			return color.New(fallbackColor).Sprint(input)
+		}
+
+		return string(b)
+	},
 }
 
 var defaultSuccessTemplate = `[OK] {{ join .LineWidth .AssertPath }}`
@@ -1011,11 +1149,18 @@ request name: {{ .RequestName | color $.EnableColors "Cyan" }}
 {{- end -}}
 {{- if .HaveRequest }}
 
-request: {{ .Request | indent | trim | color $.EnableColors "HiMagenta" }}
+request: {{ .Request | indent | trim | colorhttp $.EnableColors "HiMagenta" false}}
 {{- end -}}
 {{- if .HaveResponse }}
 
-response: {{ .Response | indent | trim | color $.EnableColors "HiMagenta" }}
+response: {{ .Response | indent | trim | colorhttp $.EnableColors "HiMagenta" true}}
+{{- end -}}
+{{- if .HaveStacktrace }}
+
+trace:
+{{- range $n, $call := .Stacktrace }}
+{{ $call | indent }}
+{{- end -}}
 {{- end -}}
 {{- if .AssertPath }}
 
@@ -1029,23 +1174,23 @@ assertion:
 {{- else }}expected
 {{- end }} {{ .ExpectedKind }}:
 {{- range $n, $exp := .Expected }}
-{{ $exp | indent | color $.EnableColors "HiMagenta" }}
+{{ $exp | colorjson $.EnableColors "HiMagenta" | indent }}
 {{- end -}}
 {{- end -}}
 {{- if .HaveActual }}
 
 actual value:
-{{ .Actual | indent | color .EnableColors "HiMagenta" }}
+{{ .Actual | colorjson .EnableColors "HiMagenta" | indent }}
 {{- end -}}
 {{- if .HaveReference }}
 
 reference value:
-{{ .Reference | indent | color .EnableColors "HiMagenta" }}
+{{ .Reference | colorjson .EnableColors "HiMagenta" | indent }}
 {{- end -}}
 {{- if .HaveDelta }}
 
 allowed delta:
-{{ .Delta | indent | color .EnableColors "HiMagenta" }}
+{{ .Delta | indent | color .EnableColors "Cyan" }}
 {{- end -}}
 {{- if .HaveDiff }}
 
diff --git a/go.mod b/go.mod
index 9758db0..e9db141 100644
--- a/go.mod
+++ b/go.mod
@@ -1,17 +1,18 @@
 module github.com/gavv/httpexpect/v2
 
-go 1.17
+go 1.19
 
 require (
+	github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2
 	github.com/ajg/form v1.5.1
 	github.com/fasthttp/websocket v1.4.3-rc.6
-	github.com/fatih/color v1.13.0
+	github.com/fatih/color v1.15.0
 	github.com/fatih/structs v1.1.0
 	github.com/gobwas/glob v0.2.3
 	github.com/google/go-querystring v1.1.0
 	github.com/gorilla/websocket v1.4.2
 	github.com/imkira/go-interpol v1.1.0
-	github.com/mattn/go-isatty v0.0.17
+	github.com/mattn/go-isatty v0.0.18
 	github.com/mitchellh/go-wordwrap v1.0.1
 	github.com/sanity-io/litter v1.5.5
 	github.com/stretchr/testify v1.5.0
@@ -26,6 +27,7 @@ require (
 require (
 	github.com/andybalholm/brotli v1.0.4 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect
 	github.com/klauspost/compress v1.15.0 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/onsi/ginkgo v1.10.1 // indirect
@@ -38,6 +40,6 @@ require (
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	github.com/yudai/pp v2.0.1+incompatible // indirect
-	golang.org/x/sys v0.5.0 // indirect
+	golang.org/x/sys v0.7.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 9abd0a1..e46ba11 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
+github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
 github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
 github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
@@ -9,8 +11,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/fasthttp/websocket v1.4.3-rc.6 h1:omHqsl8j+KXpmzRjF8bmzOSYJ8GnS0E3efi1wYT+niY=
 github.com/fasthttp/websocket v1.4.3-rc.6/go.mod h1:43W9OM2T8FeXpCWMsBd9Cb7nE2CACNqNvCqQCoty/Lc=
-github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
-github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
+github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
 github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
 github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -25,6 +27,8 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
 github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8=
+github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
@@ -32,14 +36,11 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C
 github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
 github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
 github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
+github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
 github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -86,16 +87,13 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf
 github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
 github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -104,44 +102,35 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/request.go b/request.go
index 280bfb0..7e2312e 100644
--- a/request.go
+++ b/request.go
@@ -7,7 +7,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"mime/multipart"
 	"net"
 	"net/http"
@@ -1889,7 +1888,7 @@ func (r *Request) WithFile(key, path string, reader ...io.Reader) *Request {
 //
 //	req := NewRequestC(config, "PUT", "http://example.com/path")
 //	fh, _ := os.Open("./john.png")
-//	b, _ := ioutil.ReadAll(fh)
+//	b, _ := io.ReadAll(fh)
 //	req.WithMultipart().
 //		WithFileBytes("avatar", "john.png", b)
 //	fh.Close()
@@ -2427,7 +2426,8 @@ func (r *Request) setupRedirects(opChain *chain) {
 			if _, ok := r.httpReq.Body.(*bodyWrapper); !ok {
 				r.httpReq.Body = newBodyWrapper(r.httpReq.Body, nil)
 			}
-			r.httpReq.GetBody = r.httpReq.Body.(*bodyWrapper).GetBody
+			wrapper := r.httpReq.Body.(*bodyWrapper)
+			r.httpReq.GetBody = wrapper.GetBody
 		} else {
 			r.httpReq.GetBody = func() (io.ReadCloser, error) {
 				return http.NoBody, nil
@@ -2495,7 +2495,7 @@ func (r *Request) setBody(
 		r.httpReq.Body = http.NoBody
 		r.httpReq.ContentLength = 0
 	} else {
-		r.httpReq.Body = ioutil.NopCloser(reader)
+		r.httpReq.Body = io.NopCloser(reader)
 		r.httpReq.ContentLength = int64(len)
 	}
 
diff --git a/response.go b/response.go
index bfa92b5..7d32ef0 100644
--- a/response.go
+++ b/response.go
@@ -5,7 +5,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"mime"
 	"net/http"
 	"reflect"
@@ -169,7 +169,7 @@ func (r *Response) getContent(opChain *chain) ([]byte, bool) {
 		bw.Rewind()
 	}
 
-	content, err := ioutil.ReadAll(resp.Body)
+	content, err := io.ReadAll(resp.Body)
 
 	closeErr := resp.Body.Close()
 	if err == nil {
diff --git a/stacktrace.go b/stacktrace.go
new file mode 100644
index 0000000..3e6c175
--- /dev/null
+++ b/stacktrace.go
@@ -0,0 +1,66 @@
+package httpexpect
+
+import (
+	"regexp"
+	"runtime"
+)
+
+// Stacktrace entry.
+type StacktraceEntry struct {
+	Pc uintptr // Program counter
+
+	File string // File path
+	Line int    // Line number
+
+	Func *runtime.Func // Function information
+
+	FuncName    string  // Function name (without package and parenthesis)
+	FuncPackage string  // Function package
+	FuncOffset  uintptr // Program counter offset relative to function start
+
+	// True if this is program entry point
+	// (like main.main or testing.tRunner)
+	IsEntrypoint bool
+}
+
+var stacktraceFuncRe = regexp.MustCompile(`^(.+/[^.]+)\.(.+)$`)
+
+func stacktrace() []StacktraceEntry {
+	callers := []StacktraceEntry{}
+	for i := 1; ; i++ {
+		pc, file, line, ok := runtime.Caller(i)
+		if !ok {
+			break
+		}
+
+		f := runtime.FuncForPC(pc)
+		if f == nil {
+			break
+		}
+
+		entry := StacktraceEntry{
+			Pc:   pc,
+			File: file,
+			Line: line,
+			Func: f,
+		}
+
+		if m := stacktraceFuncRe.FindStringSubmatch(f.Name()); m != nil {
+			entry.FuncName = m[2]
+			entry.FuncPackage = m[1]
+		} else {
+			entry.FuncName = f.Name()
+		}
+
+		entry.FuncOffset = pc - f.Entry()
+
+		switch f.Name() {
+		case "main.main", "testing.tRunner":
+			entry.IsEntrypoint = true
+		}
+
+		callers = append(callers, entry)
+	}
+
+	return callers
+}


Test patch:
diff --git a/binder_test.go b/binder_test.go
index f312492..e5729b5 100644
--- a/binder_test.go
+++ b/binder_test.go
@@ -3,7 +3,7 @@ package httpexpect
 import (
 	"bufio"
 	"crypto/tls"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"strings"
 	"testing"
@@ -20,7 +20,7 @@ type mockHandler struct {
 }
 
 func (c *mockHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-	body, err := ioutil.ReadAll(req.Body)
+	body, err := io.ReadAll(req.Body)
 	assert.True(c.t, err == nil)
 
 	if c.http10 {
@@ -81,7 +81,7 @@ func TestBinder_Basic(t *testing.T) {
 		"Content-Type": {"application/json"},
 	}
 
-	b, err := ioutil.ReadAll(resp.Body)
+	b, err := io.ReadAll(resp.Body)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -240,7 +240,7 @@ func TestFastBinder_Basic(t *testing.T) {
 		"Content-Type": {"application/json"},
 	}
 
-	b, err := ioutil.ReadAll(resp.Body)
+	b, err := io.ReadAll(resp.Body)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -278,7 +278,7 @@ func TestFastBinder_RemoteAddr(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	b, err := ioutil.ReadAll(resp.Body)
+	b, err := io.ReadAll(resp.Body)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -312,7 +312,7 @@ func TestFastBinder_Protocol(t *testing.T) {
 			t.Fatal(err)
 		}
 
-		b, err := ioutil.ReadAll(resp.Body)
+		b, err := io.ReadAll(resp.Body)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -476,7 +476,7 @@ func TestFastBinder_EmptyResponse(t *testing.T) {
 
 	assert.False(t, resp.Body == nil)
 
-	b, err := ioutil.ReadAll(resp.Body)
+	b, err := io.ReadAll(resp.Body)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/body_wrapper_test.go b/body_wrapper_test.go
index 8f3ab4f..c80dd71 100644
--- a/body_wrapper_test.go
+++ b/body_wrapper_test.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"errors"
 	"io"
-	"io/ioutil"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -138,10 +137,149 @@ func TestBodyWrapper_Rewind(t *testing.T) {
 		assert.Equal(t, 1, body.closeCount)
 		assert.Equal(t, 1, cancelCount)
 	})
+
+	t.Run("readall - rewind - readall", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		cancelCount := 0
+		cancelFn := func() {
+			cancelCount++
+		}
+
+		wrp := newBodyWrapper(body, cancelFn)
+
+		b, err := io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		readCount := body.readCount
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, cancelCount)
+
+		wrp.Rewind()
+
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		assert.Equal(t, readCount, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, cancelCount)
+	})
+
+	t.Run("close - rewind - readall", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		cancelCount := 0
+		cancelFn := func() {
+			cancelCount++
+		}
+
+		wrp := newBodyWrapper(body, cancelFn)
+
+		err := wrp.Close()
+		assert.NoError(t, err)
+
+		wrp.Rewind()
+
+		b, err := io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, cancelCount)
+	})
+
+	t.Run("rewind - readall - rewind - readall", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		cancelCount := 0
+		cancelFn := func() {
+			cancelCount++
+		}
+
+		wrp := newBodyWrapper(body, cancelFn)
+
+		wrp.Rewind()
+
+		b, err := io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		readCount := body.readCount
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, cancelCount)
+
+		wrp.Rewind()
+
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		assert.Equal(t, readCount, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, cancelCount)
+	})
+
+	t.Run("rewind - close - rewind - readall", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		cancelCount := 0
+		cancelFn := func() {
+			cancelCount++
+		}
+
+		wrp := newBodyWrapper(body, cancelFn)
+
+		wrp.Rewind()
+
+		err := wrp.Close()
+		assert.NoError(t, err)
+
+		wrp.Rewind()
+
+		b, err := io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, cancelCount)
+	})
+
+	t.Run("readall - close - rewind (loop)", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		cancelCount := 0
+		cancelFn := func() {
+			cancelCount++
+		}
+
+		wrp := newBodyWrapper(body, cancelFn)
+
+		n := 3
+		for i := 0; i < n; i++ {
+			b, err := io.ReadAll(wrp)
+			assert.NoError(t, err)
+			assert.Equal(t, "test_body", string(b))
+
+			assert.NotEqual(t, 0, body.readCount)
+			assert.Equal(t, 1, body.closeCount)
+			assert.Equal(t, 1, cancelCount)
+
+			err = wrp.Close()
+			assert.NoError(t, err)
+
+			wrp.Rewind()
+		}
+	})
 }
 
 func TestBodyWrapper_GetBody(t *testing.T) {
-	t.Run("independent readers", func(t *testing.T) {
+	t.Run("parallel readers", func(t *testing.T) {
 		body := newMockBody("test_body")
 
 		wrp := newBodyWrapper(body, nil)
@@ -152,14 +290,197 @@ func TestBodyWrapper_GetBody(t *testing.T) {
 		rd2, err := wrp.GetBody()
 		assert.NoError(t, err)
 
-		b, err := ioutil.ReadAll(rd1)
+		b, err := io.ReadAll(rd1)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		b, err = io.ReadAll(rd2)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+	})
+
+	t.Run("start read - get body - finish read", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		wrp := newBodyWrapper(body, nil)
+
+		// Start reading body
+		b := make([]byte, len("test"))
+		n, err := wrp.Read(b)
+		assert.Equal(t, len(b), n)
+		assert.NoError(t, err)
+		assert.Equal(t, "test", string(b))
+
+		// Call GetBody and read from it
+		rd, err := wrp.GetBody()
+		assert.NoError(t, err)
+		b, err = io.ReadAll(rd)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Finish reading body
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "_body", string(b))
+
+		// Check body read count and close count
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+	})
+
+	t.Run("start read - rewind - get body - read again", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		wrp := newBodyWrapper(body, nil)
+
+		// Start reading body
+		b := make([]byte, len("test"))
+		n, err := wrp.Read(b)
+		assert.Equal(t, len(b), n)
+		assert.NoError(t, err)
+		assert.Equal(t, "test", string(b))
+
+		// Rewind
+		wrp.Rewind()
+
+		// Call GetBody and read from it
+		rd, err := wrp.GetBody()
+		assert.NoError(t, err)
+		b, err = io.ReadAll(rd)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Re-read body until EOF
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Check body read count and close count
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+	})
+
+	t.Run("read all - get body - read again", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		wrp := newBodyWrapper(body, nil)
+
+		// Read body until EOF
+		b, err := io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Call GetBody and read from it
+		rd, err := wrp.GetBody()
+		assert.NoError(t, err)
+		b, err = io.ReadAll(rd)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Try to read more
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "", string(b))
+
+		// Check body read count and close count
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+	})
+
+	t.Run("read all - rewind - get body - read again", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		wrp := newBodyWrapper(body, nil)
+
+		// Read body until EOF
+		b, err := io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Rewind
+		wrp.Rewind()
+
+		// Call GetBody and read from it
+		rd, err := wrp.GetBody()
+		assert.NoError(t, err)
+		b, err = io.ReadAll(rd)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Re-read body until EOF
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Check body read count and close count
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+	})
+
+	t.Run("read all - close - get body - read again", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		wrp := newBodyWrapper(body, nil)
+
+		// Read body until EOF
+		b, err := io.ReadAll(wrp)
 		assert.NoError(t, err)
 		assert.Equal(t, "test_body", string(b))
 
-		b, err = ioutil.ReadAll(rd2)
+		// Close
+		err = wrp.Close()
+		assert.NoError(t, err)
+
+		// Call GetBody and read from it
+		rd, err := wrp.GetBody()
+		assert.NoError(t, err)
+		b, err = io.ReadAll(rd)
 		assert.NoError(t, err)
 		assert.Equal(t, "test_body", string(b))
 
+		// Try to read more
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "", string(b))
+
+		// Check body read count and close count
+		assert.NotEqual(t, 0, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+	})
+
+	t.Run("read all - close - rewind - get body - read again", func(t *testing.T) {
+		body := newMockBody("test_body")
+
+		wrp := newBodyWrapper(body, nil)
+
+		// Read body until EOF
+		b, err := io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Close
+		err = wrp.Close()
+		assert.NoError(t, err)
+
+		// Rewind
+		wrp.Rewind()
+
+		// Call GetBody and read from it
+		rd, err := wrp.GetBody()
+		assert.NoError(t, err)
+		b, err = io.ReadAll(rd)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Try to read more
+		b, err = io.ReadAll(wrp)
+		assert.NoError(t, err)
+		assert.Equal(t, "test_body", string(b))
+
+		// Check body read count and close count
 		assert.NotEqual(t, 0, body.readCount)
 		assert.Equal(t, 1, body.closeCount)
 	})
@@ -601,7 +922,7 @@ func TestBodyWrapper_Memory(t *testing.T) {
 		assert.Equal(t, 1, body.eofCount)
 
 		// check body
-		c, err := ioutil.ReadAll(reader)
+		c, err := io.ReadAll(reader)
 		assert.NoError(t, err)
 		assert.Equal(t, "123456789", string(c))
 
@@ -742,7 +1063,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		wrp.DisableRewinds()
 
 		assert.False(t, wrp.isFullyRead)
-		assert.Equal(t, "1234", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 1, body.readCount)
 		assert.Equal(t, 0, body.closeCount)
@@ -757,7 +1078,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		wrp.Rewind()
 
 		assert.False(t, wrp.isFullyRead)
-		assert.Equal(t, "1234", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 1, body.readCount)
 		assert.Equal(t, 0, body.closeCount)
@@ -770,7 +1091,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		assert.Equal(t, "5678", string(b))
 
 		assert.False(t, wrp.isFullyRead)
-		assert.Equal(t, "1234", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 2, body.readCount)
 		assert.Equal(t, 0, body.closeCount)
@@ -782,7 +1103,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		assert.Equal(t, 0, n)
 
 		assert.False(t, wrp.isFullyRead)
-		assert.Equal(t, "1234", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 3, body.readCount)
 		assert.Equal(t, 0, body.closeCount)
@@ -793,7 +1114,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		assert.NoError(t, err)
 
 		assert.False(t, wrp.isFullyRead)
-		assert.Equal(t, "1234", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 3, body.readCount)
 		assert.Equal(t, 1, body.closeCount)
@@ -840,7 +1161,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		wrp.DisableRewinds()
 
 		assert.True(t, wrp.isFullyRead)
-		assert.Equal(t, "12345678", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 2, body.readCount)
 		assert.Equal(t, 1, body.closeCount)
@@ -860,7 +1181,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		assert.Equal(t, 0, n)
 
 		assert.True(t, wrp.isFullyRead)
-		assert.Equal(t, "12345678", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 2, body.readCount)
 		assert.Equal(t, 1, body.closeCount)
@@ -871,7 +1192,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		assert.NoError(t, err)
 
 		assert.True(t, wrp.isFullyRead)
-		assert.Equal(t, "12345678", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 2, body.readCount)
 		assert.Equal(t, 1, body.closeCount)
@@ -967,7 +1288,7 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		assert.Equal(t, 0, n)
 
 		assert.True(t, wrp.isFullyRead)
-		assert.Equal(t, "12345678", string(wrp.memBytes))
+		assert.Nil(t, wrp.memBytes)
 
 		assert.Equal(t, 2, body.readCount)
 		assert.Equal(t, 1, body.closeCount)
@@ -981,11 +1302,157 @@ func TestBodyWrapper_DisableRewinds(t *testing.T) {
 		assert.Error(t, io.EOF, err)
 		assert.Equal(t, 0, n)
 
+		assert.True(t, wrp.isFullyRead)
+		assert.Nil(t, wrp.memBytes)
+
+		assert.Equal(t, 2, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, body.eofCount)
+	})
+
+	t.Run("disable rewinds during read from memory before closing", func(t *testing.T) {
+		body := newMockBody("12345678")
+		wrp := newBodyWrapper(body, nil)
+
+		b := make([]byte, 8)
+		var (
+			err error
+			n   int
+		)
+
+		// first read
+		n, err = wrp.Read(b)
+		assert.NoError(t, err)
+		assert.Equal(t, 8, n)
+		assert.Equal(t, "12345678", string(b))
+
+		assert.False(t, wrp.isFullyRead)
+		assert.Equal(t, "12345678", string(wrp.memBytes))
+
+		assert.Equal(t, 1, body.readCount)
+		assert.Equal(t, 0, body.closeCount)
+		assert.Equal(t, 0, body.eofCount)
+
+		// read eof
+		n, err = wrp.Read(b)
+		assert.Error(t, io.EOF, err)
+		assert.Equal(t, 0, n)
+
+		assert.True(t, wrp.isFullyRead)
+		assert.Equal(t, "12345678", string(wrp.memBytes))
+
+		assert.Equal(t, 2, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, body.eofCount)
+
+		// rewind
+		wrp.Rewind()
+
+		// new first read
+		n, err = wrp.Read(b[:4])
+		assert.NoError(t, err)
+		assert.Equal(t, 4, n)
+		assert.Equal(t, "1234", string(b[:4]))
+
 		assert.True(t, wrp.isFullyRead)
 		assert.Equal(t, "12345678", string(wrp.memBytes))
 
 		assert.Equal(t, 2, body.readCount)
 		assert.Equal(t, 1, body.closeCount)
 		assert.Equal(t, 1, body.eofCount)
+
+		// disable rewinds
+		wrp.DisableRewinds()
+
+		assert.True(t, wrp.isFullyRead)
+		assert.Equal(t, "12345678", string(wrp.memBytes))
+
+		assert.Equal(t, 2, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, body.eofCount)
+
+		// close
+		err = wrp.Close()
+		assert.NoError(t, err)
+
+		assert.True(t, wrp.isFullyRead)
+		assert.Nil(t, wrp.memBytes)
+
+		assert.Equal(t, 2, body.readCount)
+		assert.Equal(t, 1, body.closeCount)
+		assert.Equal(t, 1, body.eofCount)
+	})
+}
+
+func TestBodyWrapper_DisableRewindsErrors(t *testing.T) {
+	bodyText := "test_body"
+
+	t.Run("rewinds are disabled before first read", func(t *testing.T) {
+		body := newMockBody(bodyText)
+		wrp := newBodyWrapper(body, nil)
+		bodyErr := errors.New("read_error")
+		body.readErr = bodyErr
+		b := make([]byte, len(bodyText)+1)
+
+		// disable rewinds before first read
+		wrp.DisableRewinds()
+
+		// Read calls body.Read, which fails
+		_, err := wrp.Read(b)
+		assert.EqualError(t, err, bodyErr.Error())
+		assert.Equal(t, 1, body.readCount)
+
+		// Read calls body.Read again, which fails again
+		_, err = wrp.Read(b)
+		assert.EqualError(t, err, bodyErr.Error())
+		assert.Equal(t, 2, body.readCount)
+	})
+
+	t.Run("rewinds are disabled after read error", func(t *testing.T) {
+		body := newMockBody(bodyText)
+		wrp := newBodyWrapper(body, nil)
+		bodyErr := errors.New("read_error")
+		body.readErr = bodyErr
+		b := make([]byte, len(bodyText)+1)
+
+		// Read calls body.Read, which fails
+		_, err := wrp.Read(b)
+		assert.EqualError(t, err, bodyErr.Error())
+		assert.Equal(t, 1, body.readCount)
+
+		// disable rewinds after failed read
+		wrp.DisableRewinds()
+
+		// Read returns cached error from previous call
+		_, err = wrp.Read(b)
+		assert.EqualError(t, err, bodyErr.Error())
+		assert.Equal(t, 1, body.readCount)
+	})
+
+	t.Run("rewinds are disabled after close error", func(t *testing.T) {
+		body := newMockBody(bodyText)
+		wrp := newBodyWrapper(body, nil)
+		bodyErr := errors.New("close_error")
+		body.closeErr = bodyErr
+		b := make([]byte, len(bodyText)+1)
+
+		// Close calls body.Read, then body.Close, which fails
+		err := wrp.Close()
+		assert.True(t, wrp.isFullyRead)
+		assert.EqualError(t, err, bodyErr.Error())
+		assert.Equal(t, 2, body.readCount)
+
+		// disable rewinds after failed close
+		wrp.DisableRewinds()
+
+		// Read returns cached error from Close call
+		_, err = wrp.Read(b)
+		assert.EqualError(t, err, bodyErr.Error())
+		assert.Equal(t, 2, body.readCount)
+
+		// Read returns cached error from Close call, again
+		_, err = wrp.Read(b)
+		assert.EqualError(t, err, bodyErr.Error())
+		assert.Equal(t, 2, body.readCount)
 	})
 }
diff --git a/chain_test.go b/chain_test.go
index a991935..b76765b 100644
--- a/chain_test.go
+++ b/chain_test.go
@@ -717,6 +717,25 @@ func TestChain_Severity(t *testing.T) {
 	})
 }
 
+func TestChain_Stacktrace(t *testing.T) {
+	handler := &mockAssertionHandler{}
+
+	chain := newChainWithConfig("test", Config{
+		AssertionHandler: handler,
+	}.withDefaults())
+
+	opChain := chain.enter("test")
+	opChain.fail(testFailure())
+	opChain.leave()
+
+	assert.True(t, opChain.failed())
+	assert.NotNil(t, handler.failure)
+
+	assert.GreaterOrEqual(t, len(handler.failure.Stacktrace), 2)
+	assert.Contains(t, handler.failure.Stacktrace[0].FuncName, "(*chain).fail")
+	assert.Contains(t, handler.failure.Stacktrace[1].FuncName, "TestChain_Stacktrace")
+}
+
 func TestChain_Reporting(t *testing.T) {
 	handler := &mockAssertionHandler{}
 
@@ -751,6 +770,8 @@ func TestChain_Reporting(t *testing.T) {
 	assert.True(t, chain.failed())   // reported to parent
 	assert.NotNil(t, handler.ctx)    // reported to handler
 	assert.NotNil(t, handler.failure)
+
+	failure.Stacktrace = handler.failure.Stacktrace
 	assert.Equal(t, failure, *handler.failure)
 }
 
diff --git a/e2e/fs_test.go b/e2e/fs_test.go
index 4e8fc71..b4d2549 100644
--- a/e2e/fs_test.go
+++ b/e2e/fs_test.go
@@ -1,7 +1,6 @@
 package e2e
 
 import (
-	"io/ioutil"
 	"net/http"
 	"os"
 	"path"
@@ -12,13 +11,13 @@ import (
 )
 
 func TestE2EFs_FastBinder(t *testing.T) {
-	tempdir, err := ioutil.TempDir("", "httpexpect")
+	tempdir, err := os.MkdirTemp("", "httpexpect")
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(tempdir)
 
-	if err := ioutil.WriteFile(
+	if err := os.WriteFile(
 		path.Join(tempdir, "hello"), []byte("hello, world!"), 0666); err != nil {
 		t.Fatal(err)
 	}
diff --git a/e2e/printer_test.go b/e2e/printer_test.go
index d94fb21..4b280e5 100644
--- a/e2e/printer_test.go
+++ b/e2e/printer_test.go
@@ -1,7 +1,7 @@
 package e2e
 
 import (
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/http/httptest"
 	"testing"
@@ -19,14 +19,14 @@ type mockPrinter struct {
 
 func (p *mockPrinter) Request(req *http.Request) {
 	if req.Body != nil {
-		p.reqBody, _ = ioutil.ReadAll(req.Body)
+		p.reqBody, _ = io.ReadAll(req.Body)
 		req.Body.Close()
 	}
 }
 
 func (p *mockPrinter) Response(resp *http.Response, rtt time.Duration) {
 	if resp.Body != nil {
-		p.respBody, _ = ioutil.ReadAll(resp.Body)
+		p.respBody, _ = io.ReadAll(resp.Body)
 		resp.Body.Close()
 	}
 	p.rtt = rtt
@@ -36,7 +36,7 @@ func createPrinterHandler() http.Handler {
 	mux := http.NewServeMux()
 
 	mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
-		body, _ := ioutil.ReadAll(r.Body)
+		body, _ := io.ReadAll(r.Body)
 		if string(body) != "test_request" {
 			panic("unexpected request body " + string(body))
 		}
diff --git a/e2e/redirect_test.go b/e2e/redirect_test.go
index 3690c41..94bf47d 100644
--- a/e2e/redirect_test.go
+++ b/e2e/redirect_test.go
@@ -1,7 +1,7 @@
 package e2e
 
 import (
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/http/httptest"
 	"testing"
@@ -18,7 +18,7 @@ func createRedirectHandler() http.Handler {
 		if r.Method == "GET" {
 			_, _ = w.Write([]byte(`default_response`))
 		} else {
-			b, _ := ioutil.ReadAll(r.Body)
+			b, _ := io.ReadAll(r.Body)
 			_, _ = w.Write(b)
 		}
 	})
diff --git a/e2e/retry_test.go b/e2e/retry_test.go
index de7332a..6098e22 100644
--- a/e2e/retry_test.go
+++ b/e2e/retry_test.go
@@ -1,7 +1,7 @@
 package e2e
 
 import (
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/http/httptest"
 	"sync"
@@ -75,7 +75,7 @@ func createRetryHandler(rc *retryController) http.Handler {
 	mux := http.NewServeMux()
 
 	mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
-		b, _ := ioutil.ReadAll(r.Body)
+		b, _ := io.ReadAll(r.Body)
 
 		w.WriteHeader(rc.getStatus())
 		_, _ = w.Write(b)
diff --git a/expect_test.go b/expect_test.go
index b7f2fc9..585a0a1 100644
--- a/expect_test.go
+++ b/expect_test.go
@@ -465,6 +465,86 @@ func TestExpect_Propagation(t *testing.T) {
 	})
 }
 
+func TestExpect_Inheritance(t *testing.T) {
+	t.Run("reporter", func(t *testing.T) {
+		rootReporter := newMockReporter(t)
+		req2Reporter := newMockReporter(t)
+
+		e := WithConfig(Config{
+			BaseURL:  "http://example.com",
+			Client:   &mockClient{},
+			Reporter: rootReporter,
+		})
+
+		req1 := e.GET("/")
+		req2 := e.GET("/")
+
+		req2.WithReporter(req2Reporter)
+
+		// So far OK
+		req1.chain.assert(t, success)
+		req2.chain.assert(t, success)
+
+		resp1 := req1.Expect()
+		resp2 := req2.Expect()
+
+		// So far OK
+		resp1.chain.assert(t, success)
+		resp2.chain.assert(t, success)
+
+		// Failure on resp1 should be reported to rootReporter,
+		// which was inherited from config
+		resp1.JSON().Object().Value("foo").chain.assert(t, failure)
+		assert.Equal(t, 1, rootReporter.reportCalled)
+		assert.Equal(t, 0, req2Reporter.reportCalled)
+
+		// Failure on resp2 should be reported to req2Reporter,
+		// which was inherited from req2
+		resp2.JSON().Object().Value("foo").chain.assert(t, failure)
+		assert.Equal(t, 1, rootReporter.reportCalled)
+		assert.Equal(t, 1, req2Reporter.reportCalled)
+	})
+
+	t.Run("assertion handler", func(t *testing.T) {
+		rootHandler := &mockAssertionHandler{}
+		req2Handler := &mockAssertionHandler{}
+
+		e := WithConfig(Config{
+			BaseURL:          "http://example.com",
+			Client:           &mockClient{},
+			AssertionHandler: rootHandler,
+		})
+
+		req1 := e.GET("/")
+		req2 := e.GET("/")
+
+		req2.WithAssertionHandler(req2Handler)
+
+		// So far OK
+		req1.chain.assert(t, success)
+		req2.chain.assert(t, success)
+
+		resp1 := req1.Expect()
+		resp2 := req2.Expect()
+
+		// So far OK
+		resp1.chain.assert(t, success)
+		resp2.chain.assert(t, success)
+
+		// Failure on resp1 should be reported to rootReporter,
+		// which was inherited from config
+		resp1.JSON().Object().Value("foo").chain.assert(t, failure)
+		assert.Equal(t, 1, rootHandler.failureCalled)
+		assert.Equal(t, 0, req2Handler.failureCalled)
+
+		// Failure on resp2 should be reported to req2Reporter,
+		// which was inherited from req2
+		resp2.JSON().Object().Value("foo").chain.assert(t, failure)
+		assert.Equal(t, 1, rootHandler.failureCalled)
+		assert.Equal(t, 1, req2Handler.failureCalled)
+	})
+}
+
 func TestExpect_RequestFactory(t *testing.T) {
 	t.Run("default factory", func(t *testing.T) {
 		e := WithConfig(Config{
diff --git a/formatter_test.go b/formatter_test.go
index 02f36d1..a78d1d2 100644
--- a/formatter_test.go
+++ b/formatter_test.go
@@ -533,6 +533,74 @@ func TestFormatter_FailureErrors(t *testing.T) {
 	}
 }
 
+func TestFormatter_FailureContext(t *testing.T) {
+	ctx := &AssertionContext{
+		TestName:    "MyTestName",
+		RequestName: "MyRequestName",
+		Path:        []string{"MyPath"},
+		AliasedPath: []string{"MyAliasedPath"},
+	}
+
+	cases := []struct {
+		name  string
+		fmt   DefaultFormatter
+		check func(t *testing.T, data *FormatData)
+	}{
+		{
+			name: "default options",
+			fmt:  DefaultFormatter{},
+			check: func(t *testing.T, fd *FormatData) {
+				assert.Equal(t, "MyTestName", fd.TestName)
+				assert.Equal(t, "MyRequestName", fd.RequestName)
+				assert.Equal(t, []string{"MyAliasedPath"}, fd.AssertPath)
+			},
+		},
+		{
+			name: "DisableNames",
+			fmt: DefaultFormatter{
+				DisableNames: true,
+			},
+			check: func(t *testing.T, fd *FormatData) {
+				assert.Equal(t, "", fd.TestName)
+				assert.Equal(t, "", fd.RequestName)
+				assert.Equal(t, []string{"MyAliasedPath"}, fd.AssertPath)
+			},
+		},
+		{
+			name: "DisablePaths",
+			fmt: DefaultFormatter{
+				DisablePaths: true,
+			},
+			check: func(t *testing.T, fd *FormatData) {
+				assert.Equal(t, "MyTestName", fd.TestName)
+				assert.Equal(t, "MyRequestName", fd.RequestName)
+				assert.Equal(t, []string(nil), fd.AssertPath)
+			},
+		},
+		{
+			name: "DisableAliases",
+			fmt: DefaultFormatter{
+				DisableAliases: true,
+			},
+			check: func(t *testing.T, fd *FormatData) {
+				assert.Equal(t, "MyTestName", fd.TestName)
+				assert.Equal(t, "MyRequestName", fd.RequestName)
+				assert.Equal(t, []string{"MyPath"}, fd.AssertPath)
+			},
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			fl := &AssertionFailure{
+				Type: AssertEqual,
+			}
+			fd := tc.fmt.buildFormatData(ctx, fl)
+			tc.check(t, fd)
+		})
+	}
+}
+
 func TestFormatter_FloatFormat(t *testing.T) {
 	cases := []struct {
 		name     string
@@ -1028,6 +1096,51 @@ func TestFormatter_FormatDiff(t *testing.T) {
 	})
 }
 
+func TestFormatter_StacktraceMode(t *testing.T) {
+	cases := []struct {
+		name string
+		mode StacktraceMode
+		want bool
+	}{
+		{
+			name: "disabled",
+			mode: StacktraceModeDisabled,
+			want: false,
+		},
+		{
+			name: "standard",
+			mode: StacktraceModeStandard,
+			want: true,
+		},
+		{
+			name: "compact",
+			mode: StacktraceModeCompact,
+			want: true,
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			f := &DefaultFormatter{
+				StacktraceMode: tc.mode,
+			}
+			fd := f.buildFormatData(&AssertionContext{}, &AssertionFailure{
+				Stacktrace: stacktrace(),
+			})
+
+			if tc.want {
+				require.GreaterOrEqual(t, len(fd.Stacktrace), 1)
+				assert.Contains(t, fd.Stacktrace[0], "TestFormatter_StacktraceMode.func")
+				assert.Contains(t, fd.Stacktrace[0], "formatter_test.go")
+				assert.Contains(t, fd.Stacktrace[0], "github.com/gavv/httpexpect")
+			} else {
+				assert.NotNil(t, fd.Stacktrace)
+				assert.Equal(t, 0, len(fd.Stacktrace))
+			}
+		})
+	}
+}
+
 func TestFormatter_ColorMode(t *testing.T) {
 	cases := []struct {
 		name string
diff --git a/mocks_test.go b/mocks_test.go
index a83f72e..c2a3e6c 100644
--- a/mocks_test.go
+++ b/mocks_test.go
@@ -41,9 +41,10 @@ func (ml *mockLogger) Logf(message string, args ...interface{}) {
 
 // mock reporter
 type mockReporter struct {
-	testing  *testing.T
-	reported bool
-	reportCb func()
+	testing      *testing.T
+	reported     bool
+	reportCalled int
+	reportCb     func()
 }
 
 func newMockReporter(t *testing.T) *mockReporter {
@@ -53,6 +54,7 @@ func newMockReporter(t *testing.T) *mockReporter {
 func (mr *mockReporter) Errorf(message string, args ...interface{}) {
 	mr.testing.Logf("Fail: "+message, args...)
 	mr.reported = true
+	mr.reportCalled++
 
 	if mr.reportCb != nil {
 		mr.reportCb()
diff --git a/printer_test.go b/printer_test.go
index d7cebea..96ce7ef 100644
--- a/printer_test.go
+++ b/printer_test.go
@@ -3,7 +3,7 @@ package httpexpect
 import (
 	"bytes"
 	"errors"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"testing"
 
@@ -33,7 +33,7 @@ func TestPrinter_Compact(t *testing.T) {
 	printer.Request(req2)
 	printer.Request(nil)
 
-	printer.Response(&http.Response{Body: ioutil.NopCloser(body2)}, 0)
+	printer.Response(&http.Response{Body: io.NopCloser(body2)}, 0)
 	printer.Response(&http.Response{}, 0)
 	printer.Response(nil, 0)
 }
@@ -51,7 +51,7 @@ func TestPrinter_Debug(t *testing.T) {
 	printer.Request(req2)
 	printer.Request(nil)
 
-	printer.Response(&http.Response{Body: ioutil.NopCloser(body2)}, 0)
+	printer.Response(&http.Response{Body: io.NopCloser(body2)}, 0)
 	printer.Response(&http.Response{}, 0)
 	printer.Response(nil, 0)
 }
diff --git a/request_test.go b/request_test.go
index 8419ce0..e83a31f 100644
--- a/request_test.go
+++ b/request_test.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"errors"
 	"io"
-	"io/ioutil"
 	"mime"
 	"mime/multipart"
 	"net/http"
@@ -1730,19 +1729,19 @@ func TestRequest_BodyMultipart(t *testing.T) {
 		part1, _ := reader.NextPart()
 		assert.Equal(t, "b", part1.FormName())
 		assert.Equal(t, "", part1.FileName())
-		b1, _ := ioutil.ReadAll(part1)
+		b1, _ := io.ReadAll(part1)
 		assert.Equal(t, "1", string(b1))
 
 		part2, _ := reader.NextPart()
 		assert.Equal(t, "c", part2.FormName())
 		assert.Equal(t, "", part2.FileName())
-		b2, _ := ioutil.ReadAll(part2)
+		b2, _ := io.ReadAll(part2)
 		assert.Equal(t, "2", string(b2))
 
 		part3, _ := reader.NextPart()
 		assert.Equal(t, "a", part3.FormName())
 		assert.Equal(t, "", part3.FileName())
-		b3, _ := ioutil.ReadAll(part3)
+		b3, _ := io.ReadAll(part3)
 		assert.Equal(t, "3", string(b3))
 
 		eof, _ := reader.NextPart()
@@ -1752,7 +1751,7 @@ func TestRequest_BodyMultipart(t *testing.T) {
 	t.Run("multipart file", func(t *testing.T) {
 		req := NewRequestC(config, "POST", "url")
 
-		fh, _ := ioutil.TempFile("", "httpexpect")
+		fh, _ := os.CreateTemp("", "httpexpect")
 		filename2 := fh.Name()
 		_, _ = fh.WriteString("2")
 		fh.Close()
@@ -1782,25 +1781,25 @@ func TestRequest_BodyMultipart(t *testing.T) {
 		part1, _ := reader.NextPart()
 		assert.Equal(t, "a", part1.FormName())
 		assert.Equal(t, "", part1.FileName())
-		b1, _ := ioutil.ReadAll(part1)
+		b1, _ := io.ReadAll(part1)
 		assert.Equal(t, "1", string(b1))
 
 		part2, _ := reader.NextPart()
 		assert.Equal(t, "b", part2.FormName())
 		assert.Equal(t, filepath.Base(filename2), filepath.Base(part2.FileName()))
-		b2, _ := ioutil.ReadAll(part2)
+		b2, _ := io.ReadAll(part2)
 		assert.Equal(t, "2", string(b2))
 
 		part3, _ := reader.NextPart()
 		assert.Equal(t, "c", part3.FormName())
 		assert.Equal(t, "filename3", filepath.Base(part3.FileName()))
-		b3, _ := ioutil.ReadAll(part3)
+		b3, _ := io.ReadAll(part3)
 		assert.Equal(t, "3", string(b3))
 
 		part4, _ := reader.NextPart()
 		assert.Equal(t, "d", part4.FormName())
 		assert.Equal(t, "filename4", filepath.Base(part4.FileName()))
-		b4, _ := ioutil.ReadAll(part4)
+		b4, _ := io.ReadAll(part4)
 		assert.Equal(t, "4", string(b4))
 
 		eof, _ := reader.NextPart()
@@ -2169,7 +2168,7 @@ func TestRequest_RedirectsDontFollow(t *testing.T) {
 
 		tp := newMockRedirectTransport()
 		tp.assertFn = func(r *http.Request) {
-			b, err := ioutil.ReadAll(r.Body)
+			b, err := io.ReadAll(r.Body)
 			assert.NoError(t, err)
 			assert.Equal(t, "test body", string(b))
 		}
@@ -2293,7 +2292,7 @@ func TestRequest_RedirectsFollowAll(t *testing.T) {
 		tp := newMockRedirectTransport()
 		tp.maxRedirects = 1
 		tp.assertFn = func(r *http.Request) {
-			b, err := ioutil.ReadAll(r.Body)
+			b, err := io.ReadAll(r.Body)
 			assert.NoError(t, err)
 			assert.Equal(t, "test body", string(b))
 		}
@@ -2317,7 +2316,7 @@ func TestRequest_RedirectsFollowAll(t *testing.T) {
 		// Should set GetBody
 		gb, err := req.httpReq.GetBody()
 		assert.NoError(t, err)
-		b, err := ioutil.ReadAll(gb)
+		b, err := io.ReadAll(gb)
 		assert.NoError(t, err)
 		assert.Equal(t, "test body", string(b))
 
@@ -2339,7 +2338,7 @@ func TestRequest_RedirectsFollowAll(t *testing.T) {
 
 		tp := newMockRedirectTransport()
 		tp.assertFn = func(r *http.Request) {
-			b, err := ioutil.ReadAll(r.Body)
+			b, err := io.ReadAll(r.Body)
 			assert.NoError(t, err)
 			assert.Equal(t, "test body", string(b))
 		}
@@ -2362,7 +2361,7 @@ func TestRequest_RedirectsFollowAll(t *testing.T) {
 		// Should set GetBody
 		gb, err := req.httpReq.GetBody()
 		assert.NoError(t, err)
-		b, err := ioutil.ReadAll(gb)
+		b, err := io.ReadAll(gb)
 		assert.NoError(t, err)
 		assert.Equal(t, "test body", string(b))
 
@@ -2465,7 +2464,7 @@ func TestRequest_RedirectsFollowWithoutBody(t *testing.T) {
 		tp := newMockRedirectTransport()
 		tp.maxRedirects = 1
 		tp.assertFn = func(r *http.Request) {
-			b, err := ioutil.ReadAll(r.Body)
+			b, err := io.ReadAll(r.Body)
 			assert.NoError(t, err)
 			assert.Equal(t, "test body", string(b))
 		}
@@ -2512,7 +2511,7 @@ func TestRequest_RedirectsFollowWithoutBody(t *testing.T) {
 			if r.URL.String() == "/url" {
 				assert.Equal(t, r.Method, http.MethodPut)
 
-				b, err := ioutil.ReadAll(r.Body)
+				b, err := io.ReadAll(r.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			} else if r.URL.String() == "/redirect" {
@@ -2564,7 +2563,7 @@ func TestRequest_RedirectsFollowWithoutBody(t *testing.T) {
 			if r.URL.String() == "/url" {
 				assert.Equal(t, r.Method, http.MethodPut)
 
-				b, err := ioutil.ReadAll(r.Body)
+				b, err := io.ReadAll(r.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			} else if r.URL.String() == "/redirect" {
@@ -2618,7 +2617,7 @@ func TestRequest_RetriesDisabled(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2652,7 +2651,7 @@ func TestRequest_RetriesDisabled(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2687,7 +2686,7 @@ func TestRequest_RetriesDisabled(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2723,7 +2722,7 @@ func TestRequest_RetriesDisabled(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2761,7 +2760,7 @@ func TestRequest_RetriesTimeout(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2795,7 +2794,7 @@ func TestRequest_RetriesTimeout(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2831,7 +2830,7 @@ func TestRequest_RetriesTimeout(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2867,7 +2866,7 @@ func TestRequest_RetriesTimeout(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2905,7 +2904,7 @@ func TestRequest_RetriesTimeoutAndServer(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2939,7 +2938,7 @@ func TestRequest_RetriesTimeoutAndServer(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -2975,7 +2974,7 @@ func TestRequest_RetriesTimeoutAndServer(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3012,7 +3011,7 @@ func TestRequest_RetriesTimeoutAndServer(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3050,7 +3049,7 @@ func TestRequest_RetriesAll(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3084,7 +3083,7 @@ func TestRequest_RetriesAll(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3120,7 +3119,7 @@ func TestRequest_RetriesAll(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3157,7 +3156,7 @@ func TestRequest_RetriesAll(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3195,7 +3194,7 @@ func TestRequest_RetriesLimit(t *testing.T) {
 		cb: func(req *http.Request) {
 			callCount++
 
-			b, err := ioutil.ReadAll(req.Body)
+			b, err := io.ReadAll(req.Body)
 			assert.NoError(t, err)
 			assert.Equal(t, "test body", string(b))
 		},
@@ -3233,7 +3232,7 @@ func TestRequest_RetriesDelay(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3275,7 +3274,7 @@ func TestRequest_RetriesDelay(t *testing.T) {
 			cb: func(req *http.Request) {
 				callCount++
 
-				b, err := ioutil.ReadAll(req.Body)
+				b, err := io.ReadAll(req.Body)
 				assert.NoError(t, err)
 				assert.Equal(t, "test body", string(b))
 			},
@@ -3321,7 +3320,7 @@ func TestRequest_RetriesCancellation(t *testing.T) {
 
 			assert.Error(t, req.Context().Err(), context.Canceled.Error())
 
-			b, err := ioutil.ReadAll(req.Body)
+			b, err := io.ReadAll(req.Body)
 			assert.NoError(t, err)
 			assert.Equal(t, "test body", string(b))
 		},
diff --git a/response_test.go b/response_test.go
index 9c4e67c..6ed5d9e 100644
--- a/response_test.go
+++ b/response_test.go
@@ -4,7 +4,7 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"strings"
 	"testing"
@@ -370,7 +370,7 @@ func TestResponse_BodyOperations(t *testing.T) {
 
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
-			Body:       ioutil.NopCloser(bytes.NewBufferString("body")),
+			Body:       io.NopCloser(bytes.NewBufferString("body")),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -615,7 +615,7 @@ func TestResponse_NoContent(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString("")),
+			Body:       io.NopCloser(bytes.NewBufferString("")),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -705,7 +705,7 @@ func TestResponse_NoContent(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -931,7 +931,7 @@ func TestResponse_Text(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -998,7 +998,7 @@ func TestResponse_Form(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1043,7 +1043,7 @@ func TestResponse_Form(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1067,7 +1067,7 @@ func TestResponse_Form(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1119,7 +1119,7 @@ func TestResponse_JSON(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1160,7 +1160,7 @@ func TestResponse_JSON(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1184,7 +1184,7 @@ func TestResponse_JSON(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1209,7 +1209,7 @@ func TestResponse_JSON(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1265,7 +1265,7 @@ func TestResponse_JSONP(t *testing.T) {
 					httpResp := &http.Response{
 						StatusCode: http.StatusOK,
 						Header:     http.Header(headers),
-						Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+						Body:       io.NopCloser(bytes.NewBufferString(body)),
 					}
 
 					resp := NewResponse(reporter, httpResp)
@@ -1323,7 +1323,7 @@ func TestResponse_JSONP(t *testing.T) {
 					httpResp := &http.Response{
 						StatusCode: http.StatusOK,
 						Header:     http.Header(headers),
-						Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+						Body:       io.NopCloser(bytes.NewBufferString(body)),
 					}
 
 					resp := NewResponse(reporter, httpResp)
@@ -1349,7 +1349,7 @@ func TestResponse_JSONP(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1374,7 +1374,7 @@ func TestResponse_JSONP(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1430,7 +1430,7 @@ func TestResponse_ContentOpts(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(tc.respBody)),
+			Body:       io.NopCloser(bytes.NewBufferString(tc.respBody)),
 		}
 
 		reporter := newMockReporter(t)
@@ -1608,7 +1608,7 @@ func TestResponse_Usage(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1634,7 +1634,7 @@ func TestResponse_Usage(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body)),
+			Body:       io.NopCloser(bytes.NewBufferString(body)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
@@ -1660,7 +1660,7 @@ func TestResponse_Usage(t *testing.T) {
 		httpResp := &http.Response{
 			StatusCode: http.StatusOK,
 			Header:     http.Header(headers),
-			Body:       ioutil.NopCloser(bytes.NewBufferString(body1)),
+			Body:       io.NopCloser(bytes.NewBufferString(body1)),
 		}
 
 		resp := NewResponse(reporter, httpResp)
diff --git a/value_test.go b/value_test.go
index d30c1a4..8997e48 100644
--- a/value_test.go
+++ b/value_test.go
@@ -2,8 +2,8 @@ package httpexpect
 
 import (
 	"encoding/json"
-	"io/ioutil"
 	"os"
+	"runtime"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -1215,7 +1215,7 @@ func TestValue_Schema(t *testing.T) {
 			"foo": "a",
 			"bar": 1,
 		}
-		tmp, _ := ioutil.TempFile("", "httpexpect")
+		tmp, _ := os.CreateTemp("", "httpexpect")
 		defer os.Remove(tmp.Name())
 
 		_, err := tmp.Write([]byte(schema))
@@ -1224,7 +1224,11 @@ func TestValue_Schema(t *testing.T) {
 		err = tmp.Close()
 		require.Nil(t, err)
 
-		url := "file://" + tmp.Name()
+		url := "file://"
+		if runtime.GOOS == "windows" {
+			url = url + "/"
+		}
+		url = url + tmp.Name()
 		NewValue(reporter, data).Schema(url).
 			chain.assert(t, success)
 	})
@@ -1236,7 +1240,7 @@ func TestValue_Schema(t *testing.T) {
 			"bar": "b",
 		}
 
-		tmp, _ := ioutil.TempFile("", "httpexpect")
+		tmp, _ := os.CreateTemp("", "httpexpect")
 		defer os.Remove(tmp.Name())
 
 		_, err := tmp.Write([]byte(schema))
@@ -1245,7 +1249,11 @@ func TestValue_Schema(t *testing.T) {
 		err = tmp.Close()
 		require.Nil(t, err)
 
-		url := "file://" + tmp.Name()
+		url := "file://"
+		if runtime.GOOS == "windows" {
+			url = url + "/"
+		}
+		url = url + tmp.Name()
 		NewValue(reporter, data).Schema(url).
 			chain.assert(t, failure)
 	})