Skip to content

Commit d629b5f

Browse files
authored
Merge branch 'main' into fix-issue-3167
2 parents defa061 + a63bd34 commit d629b5f

File tree

14 files changed

+947
-286
lines changed

14 files changed

+947
-286
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232

3333
- name: Upload coverage reports to Codecov
3434
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.23.x' }}
35-
uses: codecov/codecov-action@v5.0.7
35+
uses: codecov/codecov-action@v5.1.1
3636
with:
3737
token: ${{ secrets.CODECOV_TOKEN }}
3838
file: ./coverage.txt

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ coverage:
2727
format:
2828
go run mvdan.cc/gofumpt@latest -w -l .
2929

30-
## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli)
30+
## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli2)
3131
.PHONY: markdown
3232
markdown:
3333
markdownlint-cli2 "**/*.md" "#vendor"

client/hooks.go

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package client
22

33
import (
4-
"errors"
54
"fmt"
65
"io"
76
"math/rand"
@@ -241,8 +240,8 @@ func parserRequestBodyFile(req *Request) error {
241240
return fmt.Errorf("write formdata error: %w", err)
242241
}
243242

244-
// add file
245-
b := make([]byte, 512)
243+
// add files
244+
fileBuf := make([]byte, 1<<20) // Allocate 1MB buffer
246245
for i, v := range req.files {
247246
if v.name == "" && v.path == "" {
248247
return ErrFileNoName
@@ -273,24 +272,12 @@ func parserRequestBodyFile(req *Request) error {
273272
return fmt.Errorf("create file error: %w", err)
274273
}
275274

276-
for {
277-
n, err := v.reader.Read(b)
278-
if err != nil && !errors.Is(err, io.EOF) {
279-
return fmt.Errorf("read file error: %w", err)
280-
}
281-
282-
if errors.Is(err, io.EOF) {
283-
break
284-
}
285-
286-
_, err = w.Write(b[:n])
287-
if err != nil {
288-
return fmt.Errorf("write file error: %w", err)
289-
}
275+
// Copy the file from reader to multipart writer
276+
if _, err := io.CopyBuffer(w, v.reader, fileBuf); err != nil {
277+
return fmt.Errorf("failed to copy file data: %w", err)
290278
}
291279

292-
err = v.reader.Close()
293-
if err != nil {
280+
if err := v.reader.Close(); err != nil {
294281
return fmt.Errorf("close file error: %w", err)
295282
}
296283
}

client/request.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"context"
66
"errors"
77
"io"
8+
"iter"
89
"path/filepath"
910
"reflect"
11+
"slices"
1012
"strconv"
1113
"sync"
1214
"time"
@@ -129,6 +131,31 @@ func (r *Request) Header(key string) []string {
129131
return r.header.PeekMultiple(key)
130132
}
131133

134+
// Headers returns all headers in the request using an iterator.
135+
// You can use maps.Collect() to collect all headers into a map.
136+
//
137+
// The returned value is valid until the request object is released.
138+
// Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead.
139+
func (r *Request) Headers() iter.Seq2[string, []string] {
140+
return func(yield func(string, []string) bool) {
141+
peekKeys := r.header.PeekKeys()
142+
keys := make([][]byte, len(peekKeys))
143+
copy(keys, peekKeys) // It is necessary to have immutable byte slice.
144+
145+
for _, key := range keys {
146+
vals := r.header.PeekAll(utils.UnsafeString(key))
147+
valsStr := make([]string, len(vals))
148+
for i, v := range vals {
149+
valsStr[i] = utils.UnsafeString(v)
150+
}
151+
152+
if !yield(utils.UnsafeString(key), valsStr) {
153+
return
154+
}
155+
}
156+
}
157+
}
158+
132159
// AddHeader method adds a single header field and its value in the request instance.
133160
func (r *Request) AddHeader(key, val string) *Request {
134161
r.header.Add(key, val)
@@ -168,6 +195,33 @@ func (r *Request) Param(key string) []string {
168195
return res
169196
}
170197

198+
// Params returns all params in the request using an iterator.
199+
// You can use maps.Collect() to collect all params into a map.
200+
//
201+
// The returned value is valid until the request object is released.
202+
// Any future calls to Params method will return the modified value. Do not store references to returned value. Make copies instead.
203+
func (r *Request) Params() iter.Seq2[string, []string] {
204+
return func(yield func(string, []string) bool) {
205+
keys := r.params.Keys()
206+
207+
for _, key := range keys {
208+
if key == "" {
209+
continue
210+
}
211+
212+
vals := r.params.PeekMulti(key)
213+
valsStr := make([]string, len(vals))
214+
for i, v := range vals {
215+
valsStr[i] = utils.UnsafeString(v)
216+
}
217+
218+
if !yield(key, valsStr) {
219+
return
220+
}
221+
}
222+
}
223+
}
224+
171225
// AddParam method adds a single param field and its value in the request instance.
172226
func (r *Request) AddParam(key, val string) *Request {
173227
r.params.Add(key, val)
@@ -254,6 +308,18 @@ func (r *Request) Cookie(key string) string {
254308
return ""
255309
}
256310

311+
// Cookies returns all cookies in the cookies using an iterator.
312+
// You can use maps.Collect() to collect all cookies into a map.
313+
func (r *Request) Cookies() iter.Seq2[string, string] {
314+
return func(yield func(string, string) bool) {
315+
r.cookies.VisitAll(func(key, val string) {
316+
if !yield(key, val) {
317+
return
318+
}
319+
})
320+
}
321+
}
322+
257323
// SetCookie method sets a single cookie field and its value in the request instance.
258324
// It will override cookie which set in client instance.
259325
func (r *Request) SetCookie(key, val string) *Request {
@@ -291,6 +357,18 @@ func (r *Request) PathParam(key string) string {
291357
return ""
292358
}
293359

360+
// PathParams returns all path params in request instance.
361+
// You can use maps.Collect() to collect all cookies into a map.
362+
func (r *Request) PathParams() iter.Seq2[string, string] {
363+
return func(yield func(string, string) bool) {
364+
r.path.VisitAll(func(key, val string) {
365+
if !yield(key, val) {
366+
return
367+
}
368+
})
369+
}
370+
}
371+
294372
// SetPathParam method sets a single path param field and its value in the request instance.
295373
// It will override path param which set in client instance.
296374
func (r *Request) SetPathParam(key, val string) *Request {
@@ -376,6 +454,33 @@ func (r *Request) FormData(key string) []string {
376454
return res
377455
}
378456

457+
// AllFormData method returns all form datas in request instance.
458+
// You can use maps.Collect() to collect all cookies into a map.
459+
//
460+
// The returned value is valid until the request object is released.
461+
// Any future calls to FormDatas method will return the modified value. Do not store references to returned value. Make copies instead.
462+
func (r *Request) AllFormData() iter.Seq2[string, []string] {
463+
return func(yield func(string, []string) bool) {
464+
keys := r.formData.Keys()
465+
466+
for _, key := range keys {
467+
if key == "" {
468+
continue
469+
}
470+
471+
vals := r.formData.PeekMulti(key)
472+
valsStr := make([]string, len(vals))
473+
for i, v := range vals {
474+
valsStr[i] = utils.UnsafeString(v)
475+
}
476+
477+
if !yield(key, valsStr) {
478+
return
479+
}
480+
}
481+
}
482+
}
483+
379484
// AddFormData method adds a single form data field and its value in the request instance.
380485
func (r *Request) AddFormData(key, val string) *Request {
381486
r.formData.AddData(key, val)
@@ -435,6 +540,14 @@ func (r *Request) File(name string) *File {
435540
return nil
436541
}
437542

543+
// Files method returns all files in request instance.
544+
//
545+
// The returned value is valid until the request object is released.
546+
// Any future calls to Files method will return the modified value. Do not store references to returned value. Make copies instead.
547+
func (r *Request) Files() []*File {
548+
return r.files
549+
}
550+
438551
// FileByPath returns file ptr store in request obj by path.
439552
func (r *Request) FileByPath(path string) *File {
440553
for _, v := range r.files {
@@ -617,6 +730,16 @@ type QueryParam struct {
617730
*fasthttp.Args
618731
}
619732

733+
// Keys method returns all keys in the query params.
734+
func (p *QueryParam) Keys() []string {
735+
keys := make([]string, 0, p.Len())
736+
p.VisitAll(func(key, _ []byte) {
737+
keys = append(keys, utils.UnsafeString(key))
738+
})
739+
740+
return slices.Compact(keys)
741+
}
742+
620743
// AddParams receive a map and add each value to param.
621744
func (p *QueryParam) AddParams(r map[string][]string) {
622745
for k, v := range r {
@@ -747,6 +870,16 @@ type FormData struct {
747870
*fasthttp.Args
748871
}
749872

873+
// Keys method returns all keys in the form data.
874+
func (f *FormData) Keys() []string {
875+
keys := make([]string, 0, f.Len())
876+
f.VisitAll(func(key, _ []byte) {
877+
keys = append(keys, utils.UnsafeString(key))
878+
})
879+
880+
return slices.Compact(keys)
881+
}
882+
750883
// AddData method is a wrapper of Args's Add method.
751884
func (f *FormData) AddData(key, val string) {
752885
f.Add(key, val)

0 commit comments

Comments
 (0)