mirror of
https://github.com/project-zot/zot.git
synced 2026-06-18 05:28:07 +08:00
934b22d124
* fix(security): enhance timeout configurations and body size limits for HTTP requests Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(tests): refactor backend result handling in proxyHTTPRequest test Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): preserve ContentLength in proxied requests to prevent server hang Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): preserve explicit zero-length request bodies in proxyHTTPRequest fix(tests): add test for normalizedTimeout function to ensure default fallback Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): prevent default HTTP timeout values from being set unless explicitly configured Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): refactor timeout handling to use explicit checks for nil and non-positive values Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(tests): add wait_for_event_count function to ensure expected event generation Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): improve timeout handling and update error responses for large requests Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): enhance HTTP timeout handling with explicit accessors and default values Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): increase default API key body size and timeout values for improved performance Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): unify timeout handling by replacing specific read/write timeouts with a single default timeout Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): consolidate HTTP timeout accessors and enhance timeout handling Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix(security): simplify HTTP timeout accessors and set default values for read/write timeouts Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> --------- Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> Co-authored-by: Copilot <copilot@github.com>
114 lines
3.3 KiB
Go
114 lines
3.3 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.dev/zot/v2/pkg/api/config"
|
|
"zotregistry.dev/zot/v2/pkg/api/constants"
|
|
)
|
|
|
|
func TestProxyHTTPRequestStreamsBodyAndResponse(t *testing.T) {
|
|
Convey("proxyHTTPRequest forwards request body/headers and returns streamed response", t, func() {
|
|
requestPayload := strings.Repeat("payload-", 1024)
|
|
responsePayload := strings.Repeat("response-", 2048)
|
|
|
|
type backendResult struct {
|
|
body string
|
|
hopCount string
|
|
err error
|
|
}
|
|
|
|
resultCh := make(chan backendResult, 1)
|
|
|
|
backend := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
|
body, err := io.ReadAll(request.Body)
|
|
resultCh <- backendResult{
|
|
body: string(body),
|
|
hopCount: request.Header.Get(constants.ScaleOutHopCountHeader),
|
|
err: err,
|
|
}
|
|
|
|
response.WriteHeader(http.StatusCreated)
|
|
_, _ = io.WriteString(response, responsePayload)
|
|
}))
|
|
defer backend.Close()
|
|
|
|
backendURL, err := url.Parse(backend.URL)
|
|
So(err, ShouldBeNil)
|
|
|
|
conf := config.New()
|
|
conf.Cluster = &config.ClusterConfig{Members: []string{backendURL.Host}, HashKey: "loremipsumdolors"}
|
|
|
|
ctrlr := &Controller{Config: conf}
|
|
|
|
req, err := http.NewRequestWithContext(context.Background(), http.MethodPut,
|
|
"http://example.com/v2/repo/manifests/latest", strings.NewReader(requestPayload))
|
|
So(err, ShouldBeNil)
|
|
|
|
resp, err := proxyHTTPRequest(context.Background(), req, backendURL.Host, ctrlr)
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
defer resp.Body.Close()
|
|
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
So(err, ShouldBeNil)
|
|
|
|
result := <-resultCh
|
|
So(result.err, ShouldBeNil)
|
|
|
|
remainingReqBody, err := io.ReadAll(req.Body)
|
|
So(err, ShouldBeNil)
|
|
|
|
So(resp.StatusCode, ShouldEqual, http.StatusCreated)
|
|
So(string(respBody), ShouldEqual, responsePayload)
|
|
So(result.body, ShouldEqual, requestPayload)
|
|
So(result.hopCount, ShouldEqual, "1")
|
|
So(len(remainingReqBody), ShouldEqual, 0)
|
|
})
|
|
}
|
|
|
|
func TestProxyHTTPRequestPreservesExplicitEmptyBody(t *testing.T) {
|
|
Convey("proxyHTTPRequest preserves explicit zero-length request bodies", t, func() {
|
|
resultCh := make(chan *http.Request, 1)
|
|
|
|
backend := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
|
resultCh <- request
|
|
response.WriteHeader(http.StatusNoContent)
|
|
}))
|
|
defer backend.Close()
|
|
|
|
backendURL, err := url.Parse(backend.URL)
|
|
So(err, ShouldBeNil)
|
|
|
|
conf := config.New()
|
|
conf.Cluster = &config.ClusterConfig{Members: []string{backendURL.Host}, HashKey: "loremipsumdolors"}
|
|
|
|
ctrlr := &Controller{Config: conf}
|
|
|
|
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost,
|
|
"http://example.com/v2/repo/manifests/latest", http.NoBody)
|
|
So(err, ShouldBeNil)
|
|
So(req.ContentLength, ShouldEqual, 0)
|
|
|
|
resp, err := proxyHTTPRequest(context.Background(), req, backendURL.Host, ctrlr)
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
defer resp.Body.Close()
|
|
|
|
backendReq := <-resultCh
|
|
|
|
So(resp.StatusCode, ShouldEqual, http.StatusNoContent)
|
|
So(backendReq.ContentLength, ShouldEqual, 0)
|
|
So(backendReq.Body, ShouldEqual, http.NoBody)
|
|
So(backendReq.TransferEncoding, ShouldBeEmpty)
|
|
})
|
|
}
|