-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathresponse.go
More file actions
165 lines (143 loc) · 5.12 KB
/
response.go
File metadata and controls
165 lines (143 loc) · 5.12 KB
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
package lambada
import (
"bytes"
"fmt"
"net/http"
"strconv"
"github.com/morelj/httptools/header"
)
// ResponseWriter is an implementation of http.ResponseWriter which stores the data written to it internally.
// Trailers are not supported by this implementation.
//
// You usually access ResponseWriter through the http.ResponseWriter interface.
// If you need to access the underlying ResponseWriter use:
//
// // w is an http.ResponseWriter
// rw, ok := w.(*lambada.ResponseWriter)
type ResponseWriter struct {
outputMode OutputMode
header http.Header
lockedHeader http.Header
body bytes.Buffer
statusCode int
binary bool
ignoreBinaryDetection bool
}
func newResponseWriter(outputMode OutputMode, binary bool) *ResponseWriter {
return &ResponseWriter{
outputMode: outputMode,
header: http.Header{},
binary: binary,
}
}
// Header returns the response's header set.
// Once the WriteHeader has been called, Header returns a new copy of the response's headers, preventing them from
// being modified.
func (w *ResponseWriter) Header() http.Header {
if w.statusCode == 0 {
return w.header
}
return w.lockedHeader.Clone()
}
// Write writes data to the response body buffer.
// If WriteHeader has not been called, Write calls WriteHeader(http.StatusOK) before writing the data.
func (w *ResponseWriter) Write(data []byte) (int, error) {
if w.statusCode == 0 {
w.WriteHeader(http.StatusOK)
}
return w.body.Write(data)
}
// WriteHeader sends an HTTP response header with the provided status code.
// If WriteHeader is called more than once, only the first call takes effect.
// The status code must be between 100 and 599, otherwise WriteHeader panics.
func (w *ResponseWriter) WriteHeader(statusCode int) {
if w.statusCode == 0 {
// WriteHeader has not been called yet
if statusCode < 100 || statusCode >= 600 {
panic(fmt.Errorf("invalid status code %d", statusCode))
}
w.statusCode = statusCode
// Current headers are copied into lockedHeader, so further changed to the header map will not affect headers
w.lockedHeader = w.header.Clone()
}
}
func (w *ResponseWriter) finalize() {
// Ensure the header has been written
if w.statusCode == 0 {
w.WriteHeader(http.StatusOK)
}
body := w.body.Bytes()
// Compute Content-Length
w.lockedHeader.Set(header.ContentLength, strconv.FormatInt(int64(len(body)), 10))
if w.outputMode >= AutoContentType {
// Compute Content-Type if not set
if len(body) > 0 && w.lockedHeader.Get(header.ContentType) == "" {
w.lockedHeader.Set(header.ContentType, http.DetectContentType(body))
}
if w.outputMode >= Automatic && !w.ignoreBinaryDetection {
// Detect if output is binary
// We don't change the mode if we can't determine if the output is binary or not
switch isBinary(w.lockedHeader.Get(header.ContentType), w.lockedHeader.Get(header.ContentEncoding)) {
case bsBinary:
w.SetBinary(true)
case bsText:
w.SetBinary(false)
}
}
}
}
// StatusCode returns w's current status code.
// If WriteHeaders() has not been called yet, returns 200.
func (w *ResponseWriter) StatusCode() int {
if w.statusCode == 0 {
return http.StatusOK
}
return w.statusCode
}
// Body returns the current body's byte.
// If nothing has been written, Body returns nil.
// The returned slice is valid until the next call to Write.
func (w *ResponseWriter) Body() []byte {
return w.body.Bytes()
}
// SetBinary sets whether or not the binary mode should be enabled or not.
// When binary mode is enabled, the response is encoded to Base64 before being returned to API Gateway.
// The mode set through this function is forced - meaning that automatic binary detection will be skipped.
// Use AllowBinaryDetection() to revert this behavior.
func (w *ResponseWriter) SetBinary(binary bool) {
w.binary = binary
w.ignoreBinaryDetection = true
}
// AllowBinaryDetection allows binary detection to happen on w.
// Automatic binary detection will only happen when the OutputMode is set to Automatic.
func (w *ResponseWriter) AllowBinaryDetection() {
w.ignoreBinaryDetection = false
}
// SetOutputMode sets the output mode for w.
func (w *ResponseWriter) SetOutputMode(outputMode OutputMode) {
w.outputMode = outputMode
}
// SetBinary enforces binary mode for the given ResponseWriter.
// That is, the response will be encoded to Base64 when returned to API Gateway.
//
// If the passed ResponseWriter has not been provided by Lambada, this function has no effect.
func SetBinary(w http.ResponseWriter) {
if w, ok := w.(*ResponseWriter); ok {
w.SetBinary(true)
}
}
// SetText enforces text mode for the given ResponseWriter.
// That is, the response will be be set to not be encoded to Base64 when returned to API Gateway.
//
// If the passed ResponseWriter has not been provided by Lambada, this function has no effect.
func SetText(w http.ResponseWriter) {
if w, ok := w.(*ResponseWriter); ok {
w.SetBinary(false)
}
}
// SetOutputMode sets the given output mode to the given ResponseWriter.
func SetOutputMode(w http.ResponseWriter, outputMode OutputMode) {
if w, ok := w.(*ResponseWriter); ok {
w.SetOutputMode(outputMode)
}
}