Skip to content

Commit e506985

Browse files
authored
feat(config): add body_size_limit to TOML config (#42)
* feat(config): add body_size_limit to toml config * docs: document body_size_limit in readme and example config
1 parent 522fe48 commit e506985

8 files changed

Lines changed: 127 additions & 3 deletions

File tree

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ headers = ["Authorization: Bearer token"]
289289
**Global settings** (apply to all targets unless overridden):
290290

291291
- `refresh_interval`, `timeout`, `follow_redirects`, `accept_redirects`, `receive_alert`, `count`
292+
- `body_size_limit`: Response body cap in bytes (default `1048576` = 1 MiB; `0` means no limit)
292293
- `webhook_url`, `webhook_headers`: Default webhook settings
293294
- `only`, `skip`: Target filtering arrays
294295
- `regions`: AWS regions for remote executors
@@ -299,9 +300,12 @@ headers = ["Authorization: Bearer token"]
299300
- `method`, `headers`, `body`: HTTP request options
300301
- `assert_text`, `should_fail`: Response validation
301302
- `skip_ssl`, `follow_redirects`, `accept_redirects`: Connection options
303+
- `body_size_limit`: Per-target response body cap (set to `0` to disable capping for this target)
302304
- `webhook_url`, `webhook_headers`: Per-target notifications
303305
- `regions`: Target-specific AWS regions
304306

307+
> **Note:** Response bodies are capped at `body_size_limit` bytes when evaluating `assert_text`. If your asserted text appears beyond the cap, the assertion fails and the probe logs a warning (visible in the Recent Logs widget in TUI mode, or on stderr in simple mode). Raise `body_size_limit` or set it to `0` for targets returning large payloads.
308+
305309
## Multi-Region Monitoring
306310

307311
Deploy remote executors as AWS Lambda functions across 13 global regions for distributed monitoring from multiple geographic locations.

‎aws/invocation.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type LambdaRequest struct {
2626
SkipSSL bool `json:"skip_ssl"`
2727
AssertText string `json:"assert_text"`
2828
ShouldFail bool `json:"should_fail"`
29+
BodySizeLimit int64 `json:"body_size_limit,omitempty"`
2930
}
3031

3132
type LambdaResponse struct {
@@ -111,6 +112,7 @@ func invokeLambdaInRegion(url string, config net.NetworkConfig, region string, p
111112
SkipSSL: config.SkipSSL,
112113
AssertText: config.AssertText,
113114
ShouldFail: config.ShouldFail,
115+
BodySizeLimit: config.BodySizeLimit,
114116
}
115117

116118
payload, err := json.Marshal(request)

‎config/config.go‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package config
33
import (
44
"time"
55

6+
"github.com/Owloops/updo/net"
67
"github.com/spf13/viper"
78
)
89

@@ -29,6 +30,7 @@ type Target struct {
2930
WebhookURL string `mapstructure:"webhook_url"`
3031
WebhookHeaders []string `mapstructure:"webhook_headers"`
3132
Regions []string `mapstructure:"regions"`
33+
BodySizeLimit *int64 `mapstructure:"body_size_limit"`
3234
}
3335

3436
// BoolVal returns the value of a *bool, or the fallback if nil.
@@ -39,6 +41,14 @@ func BoolVal(p *bool, fallback bool) bool {
3941
return fallback
4042
}
4143

44+
// Int64Val returns the value of an *int64, or the fallback if nil.
45+
func Int64Val(p *int64, fallback int64) int64 {
46+
if p != nil {
47+
return *p
48+
}
49+
return fallback
50+
}
51+
4252
type Global struct {
4353
RefreshInterval int `mapstructure:"refresh_interval"`
4454
Timeout int `mapstructure:"timeout"`
@@ -55,6 +65,7 @@ type Global struct {
5565
WebhookURL string `mapstructure:"webhook_url"`
5666
WebhookHeaders []string `mapstructure:"webhook_headers"`
5767
Regions []string `mapstructure:"regions"`
68+
BodySizeLimit int64 `mapstructure:"body_size_limit"`
5869
}
5970

6071
type Config struct {
@@ -72,6 +83,7 @@ func LoadConfig(configFile string) (*Config, error) {
7283
viper.SetDefault("global.receive_alert", true)
7384
viper.SetDefault("global.count", 0)
7485
viper.SetDefault("global.method", _defaultMethod)
86+
viper.SetDefault("global.body_size_limit", net.DefaultBodySizeLimit)
7587

7688
if err := viper.ReadInConfig(); err != nil {
7789
return nil, err
@@ -110,6 +122,10 @@ func LoadConfig(configFile string) (*Config, error) {
110122
v := config.Global.SkipSSL
111123
target.SkipSSL = &v
112124
}
125+
if target.BodySizeLimit == nil {
126+
v := config.Global.BodySizeLimit
127+
target.BodySizeLimit = &v
128+
}
113129
if target.WebhookURL == "" && config.Global.WebhookURL != "" {
114130
target.WebhookURL = config.Global.WebhookURL
115131
}

‎config/config_test.go‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"os"
55
"testing"
66
"time"
7+
8+
"github.com/Owloops/updo/net"
79
)
810

911
func TestLoadConfig(t *testing.T) {
@@ -151,6 +153,104 @@ name = "Inherit"
151153
}
152154
}
153155

156+
// TestBodySizeLimitInheritance verifies that a target inherits
157+
// global.body_size_limit when unset, overrides when explicitly set,
158+
// and supports 0 as "unlimited".
159+
func TestBodySizeLimitInheritance(t *testing.T) {
160+
configContent := `
161+
[global]
162+
body_size_limit = 2097152
163+
164+
[[targets]]
165+
url = "https://inherit.example.com"
166+
name = "Inherit"
167+
168+
[[targets]]
169+
url = "https://override.example.com"
170+
name = "Override"
171+
body_size_limit = 524288
172+
173+
[[targets]]
174+
url = "https://unlimited.example.com"
175+
name = "Unlimited"
176+
body_size_limit = 0
177+
`
178+
179+
tmpFile, err := os.CreateTemp("", "test-config-bodysize-*.toml")
180+
if err != nil {
181+
t.Fatalf("Failed to create temp file: %v", err)
182+
}
183+
defer func() {
184+
if err := os.Remove(tmpFile.Name()); err != nil {
185+
t.Logf("Failed to remove temp file: %v", err)
186+
}
187+
}()
188+
189+
if _, err := tmpFile.WriteString(configContent); err != nil {
190+
t.Fatalf("Failed to write config: %v", err)
191+
}
192+
if err := tmpFile.Close(); err != nil {
193+
t.Fatalf("Failed to close temp file: %v", err)
194+
}
195+
196+
cfg, err := LoadConfig(tmpFile.Name())
197+
if err != nil {
198+
t.Fatalf("LoadConfig failed: %v", err)
199+
}
200+
201+
if cfg.Global.BodySizeLimit != 2097152 {
202+
t.Errorf("Global.BodySizeLimit = %d, want 2097152", cfg.Global.BodySizeLimit)
203+
}
204+
205+
if got := Int64Val(cfg.Targets[0].BodySizeLimit, -1); got != 2097152 {
206+
t.Errorf("Inherit target: BodySizeLimit = %d, want 2097152 (inherited)", got)
207+
}
208+
if got := Int64Val(cfg.Targets[1].BodySizeLimit, -1); got != 524288 {
209+
t.Errorf("Override target: BodySizeLimit = %d, want 524288", got)
210+
}
211+
if got := Int64Val(cfg.Targets[2].BodySizeLimit, -1); got != 0 {
212+
t.Errorf("Unlimited target: BodySizeLimit = %d, want 0 (explicit unlimited)", got)
213+
}
214+
}
215+
216+
// TestBodySizeLimitDefault verifies that when neither global nor target
217+
// set body_size_limit, the viper default of net.DefaultBodySizeLimit applies.
218+
func TestBodySizeLimitDefault(t *testing.T) {
219+
configContent := `
220+
[[targets]]
221+
url = "https://example.com"
222+
`
223+
224+
tmpFile, err := os.CreateTemp("", "test-config-bodysize-default-*.toml")
225+
if err != nil {
226+
t.Fatalf("Failed to create temp file: %v", err)
227+
}
228+
defer func() {
229+
if err := os.Remove(tmpFile.Name()); err != nil {
230+
t.Logf("Failed to remove temp file: %v", err)
231+
}
232+
}()
233+
234+
if _, err := tmpFile.WriteString(configContent); err != nil {
235+
t.Fatalf("Failed to write config: %v", err)
236+
}
237+
if err := tmpFile.Close(); err != nil {
238+
t.Fatalf("Failed to close temp file: %v", err)
239+
}
240+
241+
cfg, err := LoadConfig(tmpFile.Name())
242+
if err != nil {
243+
t.Fatalf("LoadConfig failed: %v", err)
244+
}
245+
246+
if cfg.Global.BodySizeLimit != net.DefaultBodySizeLimit {
247+
t.Errorf("Global.BodySizeLimit = %d, want %d (default)", cfg.Global.BodySizeLimit, net.DefaultBodySizeLimit)
248+
}
249+
if got := Int64Val(cfg.Targets[0].BodySizeLimit, -1); got != net.DefaultBodySizeLimit {
250+
t.Errorf("Target BodySizeLimit = %d, want %d (default inherited)", got, net.DefaultBodySizeLimit)
251+
}
252+
}
253+
154254
func TestLoadConfigDefaults(t *testing.T) {
155255
configContent := `
156256
[[targets]]

‎example-config.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ accept_redirects = false
66
receive_alert = false
77
count = 0
88
skip = ["GitHub-API"]
9+
# body_size_limit = 1048576 # Cap response body reads at 1 MiB (default); 0 = no limit
910
# regions = ["us-east-1", "eu-central-1", "ap-southeast-1"]
1011

1112
[[targets]]

‎lambda/lambda.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type CheckRequest struct {
4848
SkipSSL bool `json:"skip_ssl"`
4949
AssertText string `json:"assert_text"`
5050
ShouldFail bool `json:"should_fail"`
51+
BodySizeLimit int64 `json:"body_size_limit,omitempty"`
5152
}
5253

5354
type CheckResponse struct {
@@ -110,7 +111,7 @@ func handleRequest(ctx context.Context, req CheckRequest) (CheckResponse, error)
110111
Headers: req.Headers,
111112
Method: req.Method,
112113
Body: req.Body,
113-
BodySizeLimit: net.DefaultBodySizeLimit,
114+
BodySizeLimit: req.BodySizeLimit,
114115
}
115116

116117
result := net.CheckWebsite(req.URL, netConfig)

‎simple/monitoring.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func monitorTargetSimple(ctx context.Context, target config.Target, targetIndex
214214
Headers: target.Headers,
215215
Method: target.Method,
216216
Body: target.Body,
217-
BodySizeLimit: net.DefaultBodySizeLimit,
217+
BodySizeLimit: config.Int64Val(target.BodySizeLimit, net.DefaultBodySizeLimit),
218218
}
219219

220220
regions := target.Regions

‎tui/monitoring.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ func monitorTargetTUI(ctx context.Context, target config.Target, targetIndex int
279279
Headers: target.Headers,
280280
Method: target.Method,
281281
Body: target.Body,
282-
BodySizeLimit: net.DefaultBodySizeLimit,
282+
BodySizeLimit: config.Int64Val(target.BodySizeLimit, net.DefaultBodySizeLimit),
283283
}
284284

285285
regions := target.Regions

0 commit comments

Comments
 (0)