philiprehberger-webhook_builder 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +33 -0
- data/lib/philiprehberger/webhook_builder/backoff.rb +29 -1
- data/lib/philiprehberger/webhook_builder/client.rb +12 -0
- data/lib/philiprehberger/webhook_builder/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b3008661457c2c68b820bfb48c133cc513040bb2f5ec35af8e62a4c0318241c9
|
|
4
|
+
data.tar.gz: 461cbff339dfd9fab1bccf97e68c855080ee48f3faa685fc92b2aab81c56aeb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f68594c755f2995c19390964a63ae42ef14832eefca6a0de3c54deb74a6fd1641908bccbfd11e00b7bc4592a303fd95c552fbb7adfe652a94e7a6dac49c724bd
|
|
7
|
+
data.tar.gz: 8f6b0914eab043887af35ea57a03a7f85b2d72d0c7cbc3f8d4eecb7746a31c98ba49c0dbe6983784a07f1cb5e06bfc34094b6e302039ee2af77edf2a2ab1a29b
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-05-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Backoff::Decorrelated` — AWS-style decorrelated jitter strategy (`delay = base + rand * (min(cap, prev * 3) - base)`); spreads retries to avoid thundering-herd patterns
|
|
14
|
+
- `Client#new(backoff: :decorrelated)` — `:decorrelated` is now a recognized symbol alongside `:exponential`, `:linear`, `:fixed`, and any callable Proc
|
|
15
|
+
|
|
16
|
+
## [0.4.0] - 2026-04-22
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `Client#signature_for(body:)` — compute the HMAC-SHA256 signature this client would send for a given body without performing a delivery.
|
|
20
|
+
|
|
10
21
|
## [0.3.0] - 2026-04-18
|
|
11
22
|
|
|
12
23
|
### Added
|
data/README.md
CHANGED
|
@@ -86,6 +86,13 @@ client = Philiprehberger::WebhookBuilder.new(
|
|
|
86
86
|
backoff: :fixed
|
|
87
87
|
)
|
|
88
88
|
|
|
89
|
+
# Decorrelated jitter (AWS-style): randomized within [base, min(cap, prev*3)]
|
|
90
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
91
|
+
url: "https://example.com/webhooks",
|
|
92
|
+
secret: "secret",
|
|
93
|
+
backoff: :decorrelated
|
|
94
|
+
)
|
|
95
|
+
|
|
89
96
|
# Custom Proc backoff
|
|
90
97
|
client = Philiprehberger::WebhookBuilder.new(
|
|
91
98
|
url: "https://example.com/webhooks",
|
|
@@ -134,6 +141,24 @@ receiver.verify_signature(body: body, signature: signature) # => true
|
|
|
134
141
|
receiver.verify_signature(body: body, signature: "tampered") # => false
|
|
135
142
|
```
|
|
136
143
|
|
|
144
|
+
You can also compute the signature the client would send for a body without
|
|
145
|
+
performing a delivery — useful for preparing payloads offline or mirroring
|
|
146
|
+
`verify_signature`:
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
require "philiprehberger/webhook_builder"
|
|
150
|
+
|
|
151
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
152
|
+
url: "https://example.com/webhooks",
|
|
153
|
+
secret: "shared-signing-secret"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
body = '{"event":"order.created","payload":{"id":1}}'
|
|
157
|
+
signature = client.signature_for(body: body)
|
|
158
|
+
|
|
159
|
+
client.verify_signature(body: body, signature: signature) # => true
|
|
160
|
+
```
|
|
161
|
+
|
|
137
162
|
### Delivery Tracking
|
|
138
163
|
|
|
139
164
|
```ruby
|
|
@@ -164,6 +189,7 @@ delivery.error # => nil or error message
|
|
|
164
189
|
| `#deliver(event:, payload:, headers:)` | Deliver a webhook event and return a Delivery |
|
|
165
190
|
| `#deliver_batch(events)` | Deliver multiple events concurrently and return an array of Delivery results |
|
|
166
191
|
| `#verify_signature(body:, signature:)` | Constant-time HMAC-SHA256 verification of an incoming signature; returns `true`/`false` and never raises |
|
|
192
|
+
| `#signature_for(body:)` | Compute the HMAC-SHA256 signature for a body without sending |
|
|
167
193
|
|
|
168
194
|
### `Delivery`
|
|
169
195
|
|
|
@@ -197,6 +223,13 @@ delivery.error # => nil or error message
|
|
|
197
223
|
| `.new(delay:)` | Create fixed strategy (default: delay=1) |
|
|
198
224
|
| `#call(attempt)` | Returns constant delay |
|
|
199
225
|
|
|
226
|
+
### `Backoff::Decorrelated`
|
|
227
|
+
|
|
228
|
+
| Method | Description |
|
|
229
|
+
|--------|-------------|
|
|
230
|
+
| `.new(base:, max_delay:)` | Create decorrelated jitter strategy (defaults: base=1, max_delay=30) |
|
|
231
|
+
| `#call(attempt)` | Returns randomized delay in `[base, min(max_delay, prev * 3)]` |
|
|
232
|
+
|
|
200
233
|
## Development
|
|
201
234
|
|
|
202
235
|
```bash
|
|
@@ -53,6 +53,30 @@ module Philiprehberger
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
# Decorrelated jitter backoff (AWS-style): each delay is a random value
|
|
57
|
+
# in `[base, [cap, prev * 3].min]`, which spreads retries to avoid
|
|
58
|
+
# thundering-herd effects while still trending toward the cap.
|
|
59
|
+
#
|
|
60
|
+
# See: https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
|
|
61
|
+
class Decorrelated
|
|
62
|
+
# @param base [Numeric] minimum delay in seconds (default: 1)
|
|
63
|
+
# @param max_delay [Numeric] maximum delay in seconds (default: 30)
|
|
64
|
+
def initialize(base: 1, max_delay: 30)
|
|
65
|
+
@base = base.to_f
|
|
66
|
+
@max_delay = max_delay.to_f
|
|
67
|
+
@prev = @base
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @param _attempt [Integer] ignored (state is carried in @prev)
|
|
71
|
+
# @return [Float] delay in seconds
|
|
72
|
+
def call(_attempt)
|
|
73
|
+
upper = [@max_delay, @prev * 3].min
|
|
74
|
+
upper = @base if upper < @base
|
|
75
|
+
@prev = @base + (rand * (upper - @base))
|
|
76
|
+
@prev
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
56
80
|
# Resolve a backoff option into a callable strategy.
|
|
57
81
|
#
|
|
58
82
|
# @param option [Symbol, Proc, nil] the backoff strategy
|
|
@@ -65,10 +89,14 @@ module Philiprehberger
|
|
|
65
89
|
Linear.new
|
|
66
90
|
when :fixed
|
|
67
91
|
Fixed.new
|
|
92
|
+
when :decorrelated
|
|
93
|
+
Decorrelated.new
|
|
68
94
|
when Proc
|
|
69
95
|
option
|
|
70
96
|
else
|
|
71
|
-
raise ArgumentError,
|
|
97
|
+
raise ArgumentError,
|
|
98
|
+
"Unknown backoff strategy: #{option.inspect}. " \
|
|
99
|
+
'Use :exponential, :linear, :fixed, :decorrelated, or a Proc.'
|
|
72
100
|
end
|
|
73
101
|
end
|
|
74
102
|
end
|
|
@@ -153,6 +153,18 @@ module Philiprehberger
|
|
|
153
153
|
OpenSSL.fixed_length_secure_compare(expected, signature)
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
+
# Compute the HMAC-SHA256 hex signature this client would send for the given body.
|
|
157
|
+
#
|
|
158
|
+
# Mirrors +verify_signature+: the signature returned by +signature_for(body:)+
|
|
159
|
+
# will verify against the same body. Useful when preparing payloads offline or
|
|
160
|
+
# re-computing signatures without sending a request.
|
|
161
|
+
#
|
|
162
|
+
# @param body [String] the raw request body
|
|
163
|
+
# @return [String] the hex-encoded HMAC signature
|
|
164
|
+
def signature_for(body:)
|
|
165
|
+
sign(body)
|
|
166
|
+
end
|
|
167
|
+
|
|
156
168
|
private
|
|
157
169
|
|
|
158
170
|
# Sign the request body with HMAC-SHA256.
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-webhook_builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A webhook delivery client that signs payloads with HMAC-SHA256, retries
|
|
14
14
|
failed deliveries with configurable backoff strategies, supports batch delivery,
|