-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhttpfilter_impl.go
More file actions
137 lines (109 loc) · 5.26 KB
/
Copy pathhttpfilter_impl.go
File metadata and controls
137 lines (109 loc) · 5.26 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
package gonvoy
import (
"github.com/envoyproxy/envoy/contrib/golang/common/go/api"
)
var _ api.StreamFilter = &httpFilterImpl{}
// httpFilterImpl is an HTTP Filter implementation for Envoy.
type httpFilterImpl struct {
srv HttpFilterServer
}
func (f *httpFilterImpl) OnLog() { f.srv.Complete() }
func (f *httpFilterImpl) OnDestroy(reason api.DestroyReason) { f.srv = nil }
func (f *httpFilterImpl) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api.StatusType {
result := f.srv.ServeDecodeFilter(f.handleRequestHeader(header))
return result.Status
}
func (f *httpFilterImpl) DecodeData(buffer api.BufferInstance, endStream bool) api.StatusType {
result := f.srv.ServeDecodeFilter(f.handleRequestBody(buffer, endStream))
return result.Status
}
func (f *httpFilterImpl) EncodeHeaders(header api.ResponseHeaderMap, endStream bool) api.StatusType {
result := f.srv.ServeEncodeFilter(f.handleResponseHeader(header))
return result.Status
}
func (f *httpFilterImpl) EncodeData(buffer api.BufferInstance, endStream bool) api.StatusType {
result := f.srv.ServeEncodeFilter(f.handleResponseBody(buffer, endStream))
return result.Status
}
func (f *httpFilterImpl) handleRequestHeader(header api.RequestHeaderMap) HttpFilterDecoderFunc {
return func(c Context, p HttpFilterDecodeProcessor) (HttpFilterAction, error) {
c.LoadRequestHeaders(header)
if err := p.HandleOnRequestHeader(c); err != nil {
return ActionContinue, err
}
if c.IsRequestBodyWritable() {
// If content length is omitted, there's no need for the filter manager to buffer the request headers.
// Therefore, we can continue the flow.
if shouldOmitContentLengthOnRequest(c, header) {
return ActionContinue, nil
}
return ActionPause, nil
}
return ActionContinue, nil
}
}
func (f *httpFilterImpl) handleRequestBody(buffer api.BufferInstance, endStream bool) HttpFilterDecoderFunc {
return func(c Context, p HttpFilterDecodeProcessor) (HttpFilterAction, error) {
if !c.IsRequestBodyAccessible() {
return ActionSkip, nil
}
c.LoadRequestBody(buffer, endStream)
if !endStream {
// Wait -- we'll be called again when the complete body is buffered
// at the Envoy host side.
return ActionWait, nil
}
return ActionContinue, p.HandleOnRequestBody(c)
}
}
func (f *httpFilterImpl) handleResponseHeader(header api.ResponseHeaderMap) HttpFilterEncoderFunc {
return func(c Context, p HttpFilterEncodeProcessor) (HttpFilterAction, error) {
c.LoadResponseHeaders(header)
if err := p.HandleOnResponseHeader(c); err != nil {
return ActionContinue, err
}
// During the Encode phases or HTTP Response flows,
// if a user needs access to the HTTP Response Body, whether for reading or writing,
// the EncodeHeaders phase should return with ActionPause (StopAndBuffer status) action.
// This is necessary because the Response Header must be buffered in Envoy's filter-manager.
// If this buffering is not done, the Response Header might be sent to the downstream client prematurely,
// preventing the filter from returning a custom error response in case of unexpected events during processing.
//
// Hence, we opted for the IsResponseBodyReadable check instead of IsResponseBodyWritable.
// It's worth noting that the behavior differs in the Decode phase because the stream flow is directed towards the upstream.
// This means that even if DecodeHeaders has returned with ActionContinue (Continue status),
// DecodeData is still under supervision within Envoy's filter-manager state.
if c.IsResponseBodyReadable() {
// Regardless of whether the content length is omitted, the filter manager needs to buffer the response headers.
// This is to safeguard against unforeseen events during processing, allowing us to interrupt it with a custom error response.
if c.IsResponseBodyWritable() {
// Ignore the return value of shouldOmitContentLengthOnResponse.
_ = shouldOmitContentLengthOnResponse(c, header)
}
return ActionPause, nil
}
return ActionContinue, nil
}
}
// Attention! Please be mindful of the Listener or Cluster per_connection_buffer_limit_bytes value
// when enabling the response body access on ConfigOptions (EnableResponseBodyRead or EnableResponseBodyWrite).
// The default value set by Envoy is 1MB. If the response body size exceeds this limit, the process will be halted.
// TODO(ardikabs): Upgrade to recent version where the issue is resolved, ref(https://github.com/envoyproxy/envoy/pull/34240).
func (f *httpFilterImpl) handleResponseBody(buffer api.BufferInstance, endStream bool) HttpFilterEncoderFunc {
return func(c Context, p HttpFilterEncodeProcessor) (HttpFilterAction, error) {
if !c.IsResponseBodyAccessible() {
return ActionSkip, nil
}
c.LoadResponseBody(buffer, endStream)
if !endStream {
// Wait -- we'll be called again when the complete body is buffered
// at the Envoy host side.
return ActionWait, nil
}
return ActionContinue, p.HandleOnResponseBody(c)
}
}
func (*httpFilterImpl) DecodeTrailers(api.RequestTrailerMap) api.StatusType { return api.Continue }
func (*httpFilterImpl) EncodeTrailers(api.ResponseTrailerMap) api.StatusType { return api.Continue }
func (*httpFilterImpl) OnLogDownstreamPeriodic() {}
func (*httpFilterImpl) OnLogDownstreamStart() {}