Skip to content

Commit 9e58483

Browse files
Merge pull request #13 from signalsciences/21534-alt-response-codes
21534 alt response codes
2 parents e974b1f + 351305f commit 9e58483

File tree

9 files changed

+680
-210
lines changed

9 files changed

+680
-210
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# GoLang Module Release Notes
22

3+
## 1.7.0 2020-3-11
4+
5+
* Cleaned up configuration and added an `AltResponseCodes` option to configure
6+
alternative (other than 406) response codes that can be used for blocking
7+
38
## 1.6.5 2020-01-06
49

510
* Updated the `http.ResponseWriter` wrapper to allow `CloseNotify()` calls to pass through

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.6.5
1+
1.7.0

config.go

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
package sigsci
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net"
7+
"net/http"
8+
"path/filepath"
9+
"runtime"
10+
"time"
11+
)
12+
13+
var (
14+
// DefaultAllowUnknownContentLength is the default value
15+
DefaultAllowUnknownContentLength = false
16+
// DefaultAnomalyDuration is the default value
17+
DefaultAnomalyDuration = 1 * time.Second
18+
// DefaultAnomalySize is the default value
19+
DefaultAnomalySize = int64(512 * 1024)
20+
// DefaultDebug is the default value
21+
DefaultDebug = false
22+
// DefaultInspector is the default value
23+
DefaultInspector = Inspector(nil)
24+
// DefaultMaxContentLength is the default value
25+
DefaultMaxContentLength = int64(100000)
26+
// DefaultModuleIdentifier is the default value
27+
DefaultModuleIdentifier = "sigsci-module-golang " + version
28+
// DefaultRPCAddress is the default value
29+
DefaultRPCAddress = "/var/run/sigsci.sock"
30+
// DefaultRPCNetwork is the default value
31+
DefaultRPCNetwork = "unix"
32+
// DefaultTimeout is the default value
33+
DefaultTimeout = 100 * time.Millisecond
34+
// DefaultServerIdentifier is the default value
35+
DefaultServerIdentifier = runtime.Version()
36+
)
37+
38+
// HeaderExtractorFunc is a header extraction function
39+
type HeaderExtractorFunc func(*http.Request) (http.Header, error)
40+
41+
// ModuleConfig is a configuration object for a Module
42+
type ModuleConfig struct {
43+
allowUnknownContentLength bool
44+
altResponseCodes map[int]bool
45+
anomalyDuration time.Duration
46+
anomalySize int64
47+
debug bool
48+
headerExtractor HeaderExtractorFunc
49+
inspector Inspector
50+
inspInit InspectorInitFunc
51+
inspFini InspectorFiniFunc
52+
maxContentLength int64
53+
moduleIdentifier string
54+
rpcAddress string
55+
rpcNetwork string
56+
serverIdentifier string
57+
timeout time.Duration
58+
}
59+
60+
// NewModuleConfig returns an object with any options set
61+
func NewModuleConfig(options ...ModuleConfigOption) (*ModuleConfig, error) {
62+
c := &ModuleConfig{
63+
allowUnknownContentLength: DefaultAllowUnknownContentLength,
64+
altResponseCodes: nil,
65+
anomalyDuration: DefaultAnomalyDuration,
66+
anomalySize: DefaultAnomalySize,
67+
debug: DefaultDebug,
68+
headerExtractor: nil,
69+
inspector: DefaultInspector,
70+
inspInit: nil,
71+
inspFini: nil,
72+
maxContentLength: DefaultMaxContentLength,
73+
moduleIdentifier: DefaultModuleIdentifier,
74+
rpcAddress: DefaultRPCAddress,
75+
rpcNetwork: DefaultRPCNetwork,
76+
serverIdentifier: DefaultServerIdentifier,
77+
timeout: DefaultTimeout,
78+
}
79+
if err := c.SetOptions(options...); err != nil {
80+
return nil, err
81+
}
82+
return c, nil
83+
}
84+
85+
// SetOptions sets options specified as functional arguments
86+
func (c *ModuleConfig) SetOptions(options ...ModuleConfigOption) error {
87+
for _, opt := range options {
88+
err := opt(c)
89+
if err != nil {
90+
return err
91+
}
92+
}
93+
return nil
94+
}
95+
96+
// IsBlockCode returns true if the code is a configured block code
97+
func (c *ModuleConfig) IsBlockCode(code int) bool {
98+
if code == 406 {
99+
return true
100+
}
101+
return c.altResponseCodes[code]
102+
}
103+
104+
// IsAllowCode returns true if the code is an allow code
105+
func (c *ModuleConfig) IsAllowCode(code int) bool {
106+
return code == 200
107+
}
108+
109+
// AllowUnknownContentLength returns the configuration value
110+
func (c *ModuleConfig) AllowUnknownContentLength() bool {
111+
return c.allowUnknownContentLength
112+
}
113+
114+
// returns the configuration value
115+
func (c *ModuleConfig) AltResponseCodes() []int {
116+
if len(c.altResponseCodes) == 0 {
117+
return nil
118+
}
119+
codes := make([]int, 0, len(c.altResponseCodes))
120+
for k := range c.altResponseCodes {
121+
codes = append(codes, k)
122+
}
123+
return codes
124+
}
125+
126+
// AnomalyDuration returns the configuration value
127+
func (c *ModuleConfig) AnomalyDuration() time.Duration {
128+
return c.anomalyDuration
129+
}
130+
131+
// AnomalySize returns the configuration value
132+
func (c *ModuleConfig) AnomalySize() int64 {
133+
return c.anomalySize
134+
}
135+
136+
// Debug returns the configuration value
137+
func (c *ModuleConfig) Debug() bool {
138+
return c.debug
139+
}
140+
141+
// HeaderExtractor returns the configuration value
142+
func (c *ModuleConfig) HeaderExtractor() func(r *http.Request) (http.Header, error) {
143+
return c.headerExtractor
144+
}
145+
146+
func (c *ModuleConfig) Inspector() Inspector {
147+
return c.inspector
148+
}
149+
150+
func (c *ModuleConfig) InspectorInit() InspectorInitFunc {
151+
return c.inspInit
152+
}
153+
154+
func (c *ModuleConfig) InspectorFini() InspectorFiniFunc {
155+
return c.inspFini
156+
}
157+
158+
// MaxContentLength returns the configuration value
159+
func (c *ModuleConfig) MaxContentLength() int64 {
160+
return c.maxContentLength
161+
}
162+
163+
// ModuleIdentifier returns the configuration value
164+
func (c *ModuleConfig) ModuleIdentifier() string {
165+
return c.moduleIdentifier
166+
}
167+
168+
// returns the configuration value
169+
func (c *ModuleConfig) RPCAddress() string {
170+
return c.rpcAddress
171+
}
172+
173+
// RPCNetwork returns the configuration value
174+
func (c *ModuleConfig) RPCNetwork() string {
175+
return c.rpcNetwork
176+
}
177+
178+
// RPCAddressString returns the RPCNetwork/RPCAddress as a combined string
179+
func (c *ModuleConfig) RPCAddressString() string {
180+
return c.rpcNetwork + ":" + c.rpcAddress
181+
}
182+
183+
// ServerIdentifier returns the configuration value
184+
func (c *ModuleConfig) ServerIdentifier() string {
185+
return c.serverIdentifier
186+
}
187+
188+
// Timeout returns the configuration value
189+
func (c *ModuleConfig) Timeout() time.Duration {
190+
return c.timeout
191+
}
192+
193+
// Functional Config Options
194+
195+
// ModuleConfigOption is a functional config option for configuring the module
196+
// See: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
197+
type ModuleConfigOption func(*ModuleConfig) error
198+
199+
// AllowUnknownContentLength is a function argument to set the ability
200+
// to read the body when the content length is not specified.
201+
//
202+
// NOTE: This can be dangerous (fill RAM) if set when the max content
203+
// length is not limited by the server itself. This is intended
204+
// for use with gRPC where the max message receive length is limited.
205+
// Do NOT enable this if there is no limit set on the request
206+
// content length!
207+
func AllowUnknownContentLength(allow bool) ModuleConfigOption {
208+
return func(c *ModuleConfig) error {
209+
c.allowUnknownContentLength = allow
210+
return nil
211+
}
212+
}
213+
214+
// AltResponseCodes is a function argument to set the alternative
215+
// response codes from the agent that are considered "blocking"
216+
func AltResponseCodes(codes ...int) ModuleConfigOption {
217+
return func(c *ModuleConfig) error {
218+
c.altResponseCodes = make(map[int]bool)
219+
for _, code := range codes {
220+
c.altResponseCodes[code] = true
221+
}
222+
return nil
223+
}
224+
}
225+
226+
// AnomalyDuration is a function argument to indicate when to send data
227+
// to the inspector if the response was abnormally slow
228+
func AnomalyDuration(dur time.Duration) ModuleConfigOption {
229+
return func(c *ModuleConfig) error {
230+
c.anomalyDuration = dur
231+
return nil
232+
}
233+
}
234+
235+
// AnomalySize is a function argument to indicate when to send data to
236+
// the inspector if the response was abnormally large
237+
func AnomalySize(size int64) ModuleConfigOption {
238+
return func(c *ModuleConfig) error {
239+
c.anomalySize = size
240+
return nil
241+
}
242+
}
243+
244+
// CustomInspector is a function argument that sets a custom inspector,
245+
// an optional inspector initializer to decide if inspection should occur, and
246+
// an optional inspector finalizer that can perform any post-inspection steps
247+
func CustomInspector(insp Inspector, init InspectorInitFunc, fini InspectorFiniFunc) ModuleConfigOption {
248+
return func(c *ModuleConfig) error {
249+
c.inspector = insp
250+
c.inspInit = init
251+
c.inspFini = fini
252+
return nil
253+
}
254+
}
255+
256+
// CustomHeaderExtractor is a function argument that sets a function to extract
257+
// an alternative header object from the request. It is primarily intended only
258+
// for internal use.
259+
func CustomHeaderExtractor(fn func(r *http.Request) (http.Header, error)) ModuleConfigOption {
260+
return func(c *ModuleConfig) error {
261+
c.headerExtractor = fn
262+
return nil
263+
}
264+
}
265+
266+
// Debug turns on debug logging
267+
func Debug(enable bool) ModuleConfigOption {
268+
return func(c *ModuleConfig) error {
269+
c.debug = enable
270+
return nil
271+
}
272+
}
273+
274+
// MaxContentLength is a function argument to set the maximum post
275+
// body length that will be processed
276+
func MaxContentLength(size int64) ModuleConfigOption {
277+
return func(c *ModuleConfig) error {
278+
c.maxContentLength = size
279+
return nil
280+
}
281+
}
282+
283+
// Socket is a function argument to set where to send data to the
284+
// Signal Sciences Agent. The network argument should be `unix`
285+
// or `tcp` and the corresponding address should be either an absolute
286+
// path or an `address:port`, respectively.
287+
func Socket(network, address string) ModuleConfigOption {
288+
return func(c *ModuleConfig) error {
289+
switch network {
290+
case "unix":
291+
if !filepath.IsAbs(address) {
292+
return errors.New(`address must be an absolute path for network="unix"`)
293+
}
294+
case "tcp":
295+
if _, _, err := net.SplitHostPort(address); err != nil {
296+
return fmt.Errorf(`address must be in "address:port" form for network="tcp": %s`, err)
297+
}
298+
default:
299+
return errors.New(`network must be "tcp" or "unix"`)
300+
}
301+
302+
c.rpcNetwork = network
303+
c.rpcAddress = address
304+
305+
return nil
306+
}
307+
}
308+
309+
// Timeout is a function argument that sets the maximum time to wait until
310+
// receiving a reply from the inspector. Once this timeout is reached, the
311+
// module will fail open.
312+
func Timeout(t time.Duration) ModuleConfigOption {
313+
return func(c *ModuleConfig) error {
314+
c.timeout = t
315+
return nil
316+
}
317+
}
318+
319+
// ModuleIdentifier is a function argument that sets the module name
320+
// and version for custom setups.
321+
// The version should be a sem-version (e.g., "1.2.3")
322+
func ModuleIdentifier(name, version string) ModuleConfigOption {
323+
return func(c *ModuleConfig) error {
324+
c.moduleIdentifier = name + " " + version
325+
return nil
326+
}
327+
}
328+
329+
// ServerIdentifier is a function argument that sets the server
330+
// identifier for custom setups
331+
func ServerIdentifier(id string) ModuleConfigOption {
332+
return func(c *ModuleConfig) error {
333+
c.serverIdentifier = id
334+
return nil
335+
}
336+
}
337+
338+
func FromModuleConfig(mcfg *ModuleConfig) ModuleConfigOption {
339+
return func(c *ModuleConfig) error {
340+
*c = *mcfg
341+
return nil
342+
}
343+
}

0 commit comments

Comments
 (0)