From 23ef6d230deee5fc73b6f548fbbc8df1ef2bb9cc Mon Sep 17 00:00:00 2001 From: instabledesign Date: Mon, 7 Dec 2020 14:22:54 +0100 Subject: [PATCH] Fix response ContentLength metrics tripperware (#37) --- tripperware/metrics.go | 8 +++++++- tripperware/metrics_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/tripperware/metrics.go b/tripperware/metrics.go index a7bd64d..1602138 100644 --- a/tripperware/metrics.go +++ b/tripperware/metrics.go @@ -26,7 +26,13 @@ func Metrics(recorder metrics.Recorder, options ... metrics.Option) httpware.Tri contentLength := int64(0) if resp != nil { statusCode = resp.StatusCode - contentLength = resp.ContentLength + // ContentLength records the length of the associated content. The + // value -1 indicates that the length is unknown. Unless Request.Method + // is "HEAD", values >= 0 indicate that the given number of bytes may + // be read from Body. + if resp.ContentLength > 0 { + contentLength = resp.ContentLength + } } code := strconv.Itoa(statusCode) if !config.SplitStatus { diff --git a/tripperware/metrics_test.go b/tripperware/metrics_test.go index 90a1b72..21cc784 100644 --- a/tripperware/metrics_test.go +++ b/tripperware/metrics_test.go @@ -52,6 +52,42 @@ func TestMetrics(t *testing.T) { _, _ = stack.DecorateRoundTripper(roundTripperMock).RoundTrip(req) } +func TestMetricsContentLengthUnknown(t *testing.T) { + var recorderMock = &mocks.Recorder{} + var roundTripperMock = &mocks.RoundTripper{} + var req *http.Request + var requestTimeDuration = 10 * time.Millisecond + var resp = &http.Response{ + Status: "OK", + StatusCode: http.StatusOK, + ContentLength: -1, + } + expectedContentLength := int64(0) + var baseTime = time.Unix(513216000, 0) + + req = httptest.NewRequest(http.MethodGet, "http://fake-addr", nil) + // mock roundTripper calls + roundTripperMock.On("RoundTrip", req).Return(resp, nil) + // assert recorder calls + recorderMock.On("AddInflightRequests", req.Context(), req.URL.String(), 1).Once() + recorderMock.On("AddInflightRequests", req.Context(), req.URL.String(), -1).Once() + recorderMock.On("ObserveHTTPRequestDuration", req.Context(), req.URL.String(), requestTimeDuration, http.MethodGet, "2xx") + recorderMock.On("ObserveHTTPResponseSize", req.Context(), req.URL.String(), expectedContentLength, http.MethodGet, "2xx") + // mock time.Now method in order to return always the same time whenever the test is launched + monkey.Patch(time.Now, func() time.Time { return baseTime }) + monkey.Patch(time.Since, func(since time.Time) time.Duration { + assert.Equal(t, baseTime, since) + return requestTimeDuration + }) + defer monkey.UnpatchAll() + + // create metrics httpClient middleware + stack := httpware.TripperwareStack( + tripperware.Metrics(recorderMock), + ) + _, _ = stack.DecorateRoundTripper(roundTripperMock).RoundTrip(req) +} + // ===================================================================================================================== // ========================================= EXAMPLES ================================================================== // =====================================================================================================================