diff --git a/README.md b/README.md index 4f1c3da..5168765 100644 --- a/README.md +++ b/README.md @@ -10,45 +10,53 @@ [![Release](https://img.shields.io/github/tag/go-zoox/fetch.svg?label=Release)](https://github.com/go-zoox/fetch/releases) ## Features + ### Main API + - [x] Make HTTP requests - [x] Easy JSON Response -- [ ] GZip support +- [x] GZip support - [x] Decode GZip response - - [ ] Encode GZip request (Upload File with GZip) + - [x] Encode GZip request (Upload File with GZip) - [x] HTTP/2 support - [x] TLS - [x] Custom TLS Ca Certificate (Self signed certificate) [Example](https://github.com/go-zoox/examples/tree/master/https/fetch) - - [x] Custom Client Cert and Key for two-way authentication (Client Cert and Key) + - [x] Custom Client Cert and Key for two-way authentication (Client Cert and Key) - [x] Simple Auth Methods - [x] Basic Auth - [x] Bearer Auth - [x] Support cancel (using context) ### Timeouts and retries + - [x] Support timeout - [x] Support retry on failure ### Progress + - [x] Support progress and progress events ### File upload and download + - [x] Download files easily - [x] Upload files easily ### Cache, Proxy and UNIX sockets + - [ ] [RFC compliant caching](https://github.com/sindresorhus/got/blob/main/documentation/cache.md) - [x] Proxy support - [x] Environment variables (HTTP_PROXY/HTTPS_PROXY/SOCKS_PROXY) - [x] Custom proxy - [x] UNIX Domain Sockets - - [Example: HTTP](https://github.com/go-zoox/examples/tree/master/unix-domain-socket/http) - - [Example: HTTPs](https://github.com/go-zoox/examples/tree/master/unix-domain-socket/https) + - [Example: HTTP](https://github.com/go-zoox/examples/tree/master/unix-domain-socket/http) + - [Example: HTTPs](https://github.com/go-zoox/examples/tree/master/unix-domain-socket/https) ### WebDAV + - [ ] WebDAV protocol support ### Advanced creation + - [ ] Plugin system - [ ] Middleware system @@ -61,6 +69,7 @@ go get github.com/go-zoox/fetch ``` ## Methods + - [x] GET - [x] POST - [x] PUT @@ -176,6 +185,7 @@ func main() { fmt.Println(response.JSON()) } ``` + ### Delete ```go diff --git a/config.go b/config.go index 4a59217..aeef5df 100644 --- a/config.go +++ b/config.go @@ -53,6 +53,8 @@ type Config struct { // Username string Password string + // + CompressRequest bool } // BasicAuth is the basic auth @@ -193,6 +195,10 @@ func (c *Config) Merge(config *Config) { if config.Context != nil { c.Context = config.Context } + + if config.CompressRequest { + c.CompressRequest = config.CompressRequest + } } // Clone returns a clone of the config diff --git a/default.go b/default.go index cabf198..9362a92 100644 --- a/default.go +++ b/default.go @@ -5,11 +5,12 @@ import "github.com/go-zoox/headers" // DefaultConfig returns the default config func DefaultConfig() *Config { config := &Config{ - Headers: make(Headers), - Query: make(Query), - Params: make(Params), - BaseURL: BaseURL, - Timeout: Timeout, + Headers: make(Headers), + Query: make(Query), + Params: make(Params), + BaseURL: BaseURL, + Timeout: Timeout, + CompressRequest: false, } config.Headers[headers.UserAgent] = DefaultUserAgent() diff --git a/execute.go b/execute.go index 4d723f6..4bb5b16 100644 --- a/execute.go +++ b/execute.go @@ -16,6 +16,7 @@ import ( "net/textproto" "net/url" "os" + "strconv" "strings" "time" @@ -351,6 +352,23 @@ func (f *Fetch) Execute() (*Response, error) { } } + if config.CompressRequest { + var buf bytes.Buffer + gz := gzip.NewWriter(&buf) + + // Compress the request body + if _, err := io.Copy(gz, req.Body); err != nil { + return nil, fmt.Errorf("failed to compress request body: %v", err) + } + if err := gz.Close(); err != nil { + return nil, fmt.Errorf("failed to close gzip writer: %v", err) + } + + req.Header.Set(headers.ContentEncoding, "gzip") + req.Body = io.NopCloser(&buf) + req.Header.Set(headers.ContentLength, strconv.Itoa(buf.Len())) + } + resp, err := client.Do(req) if err != nil { diff --git a/execute_test.go b/execute_test.go new file mode 100644 index 0000000..af0ec5b --- /dev/null +++ b/execute_test.go @@ -0,0 +1,57 @@ +package fetch_test + +import ( + "compress/gzip" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/go-zoox/fetch" +) + +func TestRequestBodyCompression(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Content-Encoding") != "gzip" { + t.Fatalf("Expected Content-Encoding to be gzip, got: %s", r.Header.Get("Content-Encoding")) + } + + gr, err := gzip.NewReader(r.Body) + if err != nil { + t.Fatalf("Failed to create gzip reader: %v", err) + } + defer gr.Close() + + body, err := io.ReadAll(gr) + if err != nil { + t.Fatalf("Failed to read compressed body: %v", err) + } + + expected := `{"message":"hello"}` + if string(body) != expected { + t.Errorf("Decompressed body mismatch.\nExpected: %s\nGot: %s", expected, string(body)) + } + + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + })) + defer server.Close() + + res, err := fetch.Post(server.URL, &fetch.Config{ + Body: map[string]string{"message": "hello"}, + // Enable compression + CompressRequest: true, + }) + if err != nil { + t.Fatalf("Request failed: %v", err) + } + + if res.Status != http.StatusOK { + t.Errorf("Expected status 200 OK, got: %d", res.Status) + } + + if strings.TrimSpace(string(res.Body)) != "ok" { + t.Errorf("Expected response body 'ok', got: %s", res.Body) + } +}