Skip to content

Commit 1c36c0f

Browse files
authored
Fixes the captured response writer miscounting the body size (#61)
2 parents dd7c4e9 + 8c5c3c1 commit 1c36c0f

5 files changed

Lines changed: 89 additions & 34 deletions

File tree

backend/internal/web/captured/rw_test.go

Lines changed: 0 additions & 32 deletions
This file was deleted.

backend/internal/web/captured/rw.go renamed to backend/internal/web/reception/captured/rw.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package captured
22

33
import (
44
"cmp"
5+
"fmt"
6+
"math"
57
"net/http"
68
"strconv"
79
)
@@ -10,6 +12,7 @@ type ResponseWriter struct {
1012
wrapped http.ResponseWriter
1113
statusCode int
1214
written bool
15+
size uint
1316
}
1417

1518
func New(w http.ResponseWriter) *ResponseWriter {
@@ -22,6 +25,7 @@ func (rw *ResponseWriter) Header() http.Header {
2225

2326
func (rw *ResponseWriter) Write(n []byte) (int, error) {
2427
rw.written = true
28+
rw.size += uint(len(n))
2529
return rw.wrapped.Write(n)
2630
}
2731

@@ -45,3 +49,20 @@ func (rw ResponseWriter) StatusRepresentation() string {
4549
}
4650
return strconv.Itoa(rw.statusCode)
4751
}
52+
53+
func bytes(i uint) string {
54+
units := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
55+
j := 0
56+
for k := i; k >= 1024 && j+1 < len(units); k /= 1024 {
57+
j++
58+
}
59+
d := float64(i) / math.Pow(1024, float64(j))
60+
if d != float64(int(d)) {
61+
return fmt.Sprintf("%.1f%s", d, units[j])
62+
}
63+
return fmt.Sprintf("%d%s", int(d), units[j])
64+
}
65+
66+
func (rw ResponseWriter) SizeRepresentation() string {
67+
return bytes(rw.size)
68+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package captured
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"net"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
)
11+
12+
// fake
13+
type hijacker struct {
14+
http.ResponseWriter
15+
}
16+
17+
func (h hijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) {
18+
panic("not to call")
19+
}
20+
21+
var _ http.Hijacker = (*hijacker)(nil)
22+
23+
func ExampleResponseWriter_additionalMethods() {
24+
var rw http.ResponseWriter = New(hijacker{})
25+
if _, ok := rw.(http.Hijacker); !ok {
26+
fmt.Println("the non-ResponseWriter methods are not available directly")
27+
}
28+
if _, ok := rw.(interface{ Unwrap() http.ResponseWriter }).Unwrap().(http.Hijacker); ok {
29+
fmt.Println("become so, after unwrapping")
30+
}
31+
// Output:
32+
// the non-ResponseWriter methods are not available directly
33+
// become so, after unwrapping
34+
}
35+
36+
func TestBytes(t *testing.T) {
37+
tcs := map[uint]string{
38+
0: "0B",
39+
10: "10B",
40+
20: "20B",
41+
1000: "1000B",
42+
1024: "1KB",
43+
1024 + 102: "1.1KB",
44+
1000 * 1024: "1000KB",
45+
1024 * 1024: "1MB",
46+
1.5 * 1024 * 1024: "1.5MB",
47+
1<<63 - 1: "8EB",
48+
}
49+
for input, expected := range tcs {
50+
t.Run(fmt.Sprintf("%d", input), func(t *testing.T) {
51+
got := bytes(input)
52+
if got != expected {
53+
t.Errorf("expected %q got %q", expected, got)
54+
}
55+
})
56+
}
57+
}
58+
59+
func TestResponseWriter_SizeRepresentation(t *testing.T) {
60+
crw := New(httptest.NewRecorder())
61+
crw.Write([]byte("lorem ipsum dolor sit amet consectetur adipscing elit"))
62+
expected, got := "53B", crw.SizeRepresentation()
63+
if expected != got {
64+
t.Errorf("expected %q got %q", expected, got)
65+
}
66+
}

backend/internal/web/reception/receptionist.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818

1919
"logbook/config/deployment"
2020
"logbook/internal/logger"
21-
"logbook/internal/web/captured"
21+
"logbook/internal/web/reception/captured"
2222
"logbook/internal/web/reception/summarizer"
2323
"logbook/models/columns"
2424
)

backend/internal/web/reception/summarizer/summarizer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"net/http"
66
"time"
77

8-
"logbook/internal/web/captured"
8+
"logbook/internal/web/reception/captured"
99
)
1010

1111
type colorizer interface {

0 commit comments

Comments
 (0)