Skip to content

Commit 76ff70a

Browse files
authored
Merge pull request #34 from muir/deferred-body
Add Body() to deferred writer
2 parents bf52241 + f9b9ef0 commit 76ff70a

File tree

5 files changed

+46
-4
lines changed

5 files changed

+46
-4
lines changed

.github/workflows/codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Test and coverage
22

3-
on: [push, pull_request]
3+
on: [push]
44

55
permissions: # added using https://github.com/step-security/secure-workflows
66
contents: read

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
test:
88
strategy:
99
matrix:
10-
go-version: [1.16.x, 1.17.x, 1.18.x]
10+
go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x]
1111
os: [ubuntu-latest, macos-latest, windows-latest]
1212
runs-on: ${{ matrix.os }}
1313
steps:

.github/workflows/golangci-lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: golangci-lint
2-
on: [push ]
2+
on: [push]
33
permissions: # added using https://github.com/step-security/secure-workflows
44
contents: read
55

deferred.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type DeferredWriter struct {
1717
buffer []byte
1818
status int
1919
resetHeader http.Header
20+
flushed bool
2021
}
2122

2223
// NewDeferredWriter returns a DeferredWriter based on a
@@ -85,7 +86,13 @@ func (w *DeferredWriter) PreserveHeader() {
8586
// switches to passthrough mode: all future calls to Write(),
8687
// Header(), etc are passed through to the http.ResponseWriter that
8788
// was used to initialize the DeferredWrited.
89+
//
90+
// Any writes made before the call to UnderlyingWriter are discarded.
91+
// Call Flush() first to preserve writes.
8892
func (w *DeferredWriter) UnderlyingWriter() http.ResponseWriter {
93+
if w.passthrough {
94+
return w.base
95+
}
8996
w.passthrough = true
9097
h := w.base.Header()
9198
for k := range h {
@@ -112,7 +119,7 @@ func (w *DeferredWriter) Flush() error {
112119
if w.passthrough {
113120
return errors.New("Attempt flush deferred writer that is not deferred")
114121
}
115-
w.passthrough = true
122+
w.flushed = true
116123
base := w.UnderlyingWriter()
117124
if w.status != 0 {
118125
base.WriteHeader(w.status)
@@ -147,3 +154,14 @@ func (w *DeferredWriter) FlushIfNotFlushed() error {
147154
func (w *DeferredWriter) Done() bool {
148155
return w.passthrough
149156
}
157+
158+
// Body returns the internal buffer used by DeferredWriter. Do not modify it.
159+
// It also returns the status code (if set).
160+
// If UnderlyingWriter() has been called, then Body() will return an error since
161+
// the underlying buffer does not represent what has been written.
162+
func (w *DeferredWriter) Body() ([]byte, int, error) {
163+
if w.passthrough && !w.flushed {
164+
return nil, 0, errors.New("unable to provide body because DeferredWriter is operating in passthrough mode")
165+
}
166+
return w.buffer, w.status, nil
167+
}

deferred_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,24 @@ func (w *testResponseWriter) Write(b []byte) (int, error) {
4242

4343
func TestUnderlyingWriter(t *testing.T) {
4444
tw := &testResponseWriter{header: make(http.Header)}
45+
tw.header.Set("X", "Y")
4546
w, _ := nvelope.NewDeferredWriter(tw)
47+
tw.header.Set("Foo", "bar")
48+
tw.header.Set("A", "B")
49+
w.Header().Set("Baz", "bap")
50+
w.Header().Set("A", "C")
51+
_, _ = w.Write([]byte("howdy"))
52+
_, _, err := w.Body()
53+
assert.NoError(t, err, "body before Underlying")
54+
assert.Equal(t, tw, w.UnderlyingWriter())
55+
_, _, err = w.Body()
56+
assert.Error(t, err, "body after Underlying")
4657
assert.Equal(t, tw, w.UnderlyingWriter())
58+
assert.Equal(t, []byte(nil), tw.buffer, "underlying buffer after two calls")
59+
assert.Empty(t, tw.header["Foo"], "Foo")
60+
assert.Equal(t, tw.header.Get("Baz"), "bap", "Baz")
61+
assert.Equal(t, tw.header.Get("A"), "C", "A")
62+
assert.Equal(t, tw.header.Get("X"), "Y", "X")
4763
}
4864

4965
func TestFlush(t *testing.T) {
@@ -59,6 +75,10 @@ func TestFlush(t *testing.T) {
5975
assert.Equal(t, "", tw.Header().Get("c"), "original header untouched with existing key")
6076
assert.Equal(t, "d", w.Header().Get("c"), "new header override works though")
6177
w.WriteHeader(303)
78+
body, code, err := w.Body()
79+
assert.NoError(t, err, "body")
80+
assert.Equal(t, 303, code, "body code")
81+
assert.Equal(t, []byte("howdy"), body, code, "body")
6282
assert.Equal(t, 0, tw.code, "code not written before flush")
6383
assert.False(t, w.Done(), "done before flush")
6484
require.NoError(t, w.Flush(), "flush")
@@ -67,6 +87,10 @@ func TestFlush(t *testing.T) {
6787
assert.Equal(t, 303, tw.code, "code written after flush")
6888
assert.Equal(t, "d", tw.Header().Get("c"), "new header written - c")
6989
assert.Equal(t, "d", tw.Header().Get("a"), "new header written - a")
90+
body, code, err = w.Body()
91+
assert.NoError(t, err, "body")
92+
assert.Equal(t, 303, code, "body code")
93+
assert.Equal(t, []byte("howdy"), body, code, "body")
7094
}
7195

7296
func TestReset(t *testing.T) {

0 commit comments

Comments
 (0)