diff --git a/client.go b/client.go index 4456375..25f752b 100644 --- a/client.go +++ b/client.go @@ -20,6 +20,7 @@ type Client struct { baseURL string errorMapper ErrorMapper balancerCloser *balancer.Closer + maxResponseBody int64 } // New creates a new Client with the given options. @@ -77,9 +78,10 @@ func New(opts ...Option) *Client { Transport: rt, Timeout: o.timeout, }, - baseURL: o.baseURL, - errorMapper: o.errorMapper, - balancerCloser: balancerCloser, + baseURL: o.baseURL, + errorMapper: o.errorMapper, + balancerCloser: balancerCloser, + maxResponseBody: o.maxResponseBody, } } @@ -99,6 +101,13 @@ func (c *Client) Do(ctx context.Context, req *http.Request) (*Response, error) { } } + if c.maxResponseBody > 0 { + resp.Body = &limitedReadCloser{ + R: io.LimitedReader{R: resp.Body, N: c.maxResponseBody}, + C: resp.Body, + } + } + r := newResponse(resp) if c.errorMapper != nil { @@ -142,6 +151,15 @@ func (c *Client) Put(ctx context.Context, url string, body io.Reader) (*Response return c.Do(ctx, req) } +// Patch performs a PATCH request to the given URL with the given body. +func (c *Client) Patch(ctx context.Context, url string, body io.Reader) (*Response, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodPatch, url, body) + if err != nil { + return nil, err + } + return c.Do(ctx, req) +} + // Delete performs a DELETE request to the given URL. func (c *Client) Delete(ctx context.Context, url string) (*Response, error) { req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) diff --git a/client_patch_test.go b/client_patch_test.go new file mode 100644 index 0000000..c4e6cf8 --- /dev/null +++ b/client_patch_test.go @@ -0,0 +1,45 @@ +package httpx_test + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "git.codelab.vc/pkg/httpx" +) + +func TestClient_Patch(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPatch { + t.Errorf("expected PATCH, got %s", r.Method) + } + b, _ := io.ReadAll(r.Body) + if string(b) != `{"name":"updated"}` { + t.Errorf("expected body %q, got %q", `{"name":"updated"}`, string(b)) + } + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, "patched") + })) + defer srv.Close() + + client := httpx.New() + resp, err := client.Patch(context.Background(), srv.URL+"/item/1", strings.NewReader(`{"name":"updated"}`)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if resp.StatusCode != http.StatusOK { + t.Errorf("expected status 200, got %d", resp.StatusCode) + } + body, err := resp.String() + if err != nil { + t.Fatalf("reading body: %v", err) + } + if body != "patched" { + t.Errorf("expected body %q, got %q", "patched", body) + } +}