-
Notifications
You must be signed in to change notification settings - Fork 1
/
deferred.go
167 lines (153 loc) · 4.63 KB
/
deferred.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package nvelope
import (
"io"
"net/http"
"github.com/pkg/errors"
)
// DeferredWriter that wraps an underlying http.ResponseWriter.
// DeferredWriter buffers writes and headers. The buffer can be
// reset. When it's time to actually write, use Flush().
type DeferredWriter struct {
base http.ResponseWriter
passthrough bool
header http.Header
buffer []byte
status int
resetHeader http.Header
flushed bool
}
// NewDeferredWriter returns a DeferredWriter based on a
// base ResponseWriter. It re-injects the base writer
// so that in effect, there is only one writer present.
func NewDeferredWriter(w http.ResponseWriter) (*DeferredWriter, http.ResponseWriter) {
dw := &DeferredWriter{
base: w,
header: w.Header().Clone(),
resetHeader: w.Header().Clone(),
buffer: make([]byte, 0, 4*1024),
}
return dw, dw
}
// Header is the same as http.ResponseWriter.Header
func (w *DeferredWriter) Header() http.Header {
if w.passthrough {
return w.base.Header()
}
return w.header
}
// Write is the same as http.ResponseWriter.Write
// except that the action is delayed until Flush() is called.
func (w *DeferredWriter) Write(b []byte) (int, error) {
if w.passthrough {
return w.base.Write(b)
}
w.buffer = append(w.buffer, b...)
return len(b), nil
}
// WriteHeader is the same as http.ResponseWriter.WriteHeader
// except that the action is delayed until Flush() is called.
func (w *DeferredWriter) WriteHeader(statusCode int) {
if w.passthrough {
w.base.WriteHeader(statusCode)
} else {
w.status = statusCode
}
}
// Reset empties the DeferredWriter's buffers and resets its Header
// back to its original state. Reset returns error if UnderlyingWriter()
// or Flush() have been called.
func (w *DeferredWriter) Reset() error {
if w.passthrough {
return errors.New("Attempt to reset a DeferredWriter after it is in passthrough mode")
}
w.buffer = nil
w.status = 0
w.header = w.resetHeader.Clone()
return nil
}
// PreserveHeader saves the current Header so that a Reset will revert
// back to the header just saved.
func (w *DeferredWriter) PreserveHeader() {
w.resetHeader = w.header.Clone()
}
// UnderlyingWriter returns the underlying writer. Any header
// modifications made with the DeferredWriter are copied to the
// base writer. After a call to UnderlyingWriter, the DeferredWriter
// switches to passthrough mode: all future calls to Write(),
// Header(), etc are passed through to the http.ResponseWriter that
// was used to initialize the DeferredWrited.
//
// Any writes made before the call to UnderlyingWriter are discarded.
// Call Flush() first to preserve writes.
func (w *DeferredWriter) UnderlyingWriter() http.ResponseWriter {
if w.passthrough {
return w.base
}
w.passthrough = true
h := w.base.Header()
for k := range h {
if v, ok := w.header[k]; ok {
h[k] = v
} else {
delete(h, k)
}
}
for k, v := range w.header {
if _, ok := h[k]; ok {
continue
}
h[k] = v
}
return w.base
}
// Flush pushes the buffered write content through to the base writer.
// You can only flush once. After a flush, all further calls are passed
// through to be base writer. WriteHeader() will be called on the base
// writer even if there is no buffered data.
func (w *DeferredWriter) Flush() error {
if w.passthrough {
return errors.New("Attempt flush deferred writer that is not deferred")
}
w.flushed = true
base := w.UnderlyingWriter()
if w.status != 0 {
base.WriteHeader(w.status)
}
for i := 0; i < len(w.buffer)-1; {
amt, err := base.Write(w.buffer[i:])
if err != nil {
// Is this handling of short writes necessary? Perhaps
// so since a follow-up write will probably give a
// more accurate error.
if errors.Is(err, io.ErrShortWrite) {
i += amt
continue
}
return errors.Wrap(err, "flush buffered writer")
}
break
}
return nil
}
// FlushIfNotFlushed calls Flush if the DeferredWriter is not in
// passthrough mode.
func (w *DeferredWriter) FlushIfNotFlushed() error {
if !w.passthrough {
return w.Flush()
}
return nil
}
// Done returns true if the DeferredWriter is in passthrough mode.
func (w *DeferredWriter) Done() bool {
return w.passthrough
}
// Body returns the internal buffer used by DeferredWriter. Do not modify it.
// It also returns the status code (if set).
// If UnderlyingWriter() has been called, then Body() will return an error since
// the underlying buffer does not represent what has been written.
func (w *DeferredWriter) Body() ([]byte, int, error) {
if w.passthrough && !w.flushed {
return nil, 0, errors.New("unable to provide body because DeferredWriter is operating in passthrough mode")
}
return w.buffer, w.status, nil
}