@bauer-group/n8n-nodes-http-throttled-request 0.1.1
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.
- package/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.d.ts +6 -0
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.d.ts.map +1 -0
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.js +317 -0
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.js.map +1 -0
- package/dist/nodes/HttpRequest/throttling.d.ts +55 -0
- package/dist/nodes/HttpRequest/throttling.d.ts.map +1 -0
- package/dist/nodes/HttpRequest/throttling.js +141 -0
- package/dist/nodes/HttpRequest/throttling.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) BAUER GROUP
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# n8n-nodes-http-throttled-request
|
|
2
|
+
|
|
3
|
+
A custom n8n node that extends the HTTP Request functionality with intelligent rate-limit throttling. The node automatically detects rate-limit responses (429 Too Many Requests, etc.) and waits the appropriate time before retrying, using information from response headers.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Automatic Rate Limit Detection**: Detects HTTP 429, 503, and 504 status codes
|
|
8
|
+
- **Smart Wait Time Calculation**: Parses `Retry-After`, `X-RateLimit-*`, and HubSpot-specific headers
|
|
9
|
+
- **Jitter Support**: Prevents thundering herd with configurable random variance
|
|
10
|
+
- **n8n v2 Compatible**: Uses modern `this.helpers.httpRequest()` API
|
|
11
|
+
- **Shadow-Override Ready**: Can replace the core HTTP Request node transparently
|
|
12
|
+
- **Full Authentication Support**: None, Basic Auth, Header Auth, OAuth1, OAuth2
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Prerequisites
|
|
17
|
+
|
|
18
|
+
- Node.js 20+
|
|
19
|
+
- npm 9+
|
|
20
|
+
- n8n instance (self-hosted)
|
|
21
|
+
|
|
22
|
+
### Install from npm
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install n8n-nodes-http-throttled-request
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Install from source
|
|
29
|
+
|
|
30
|
+
1. Clone or download this repository
|
|
31
|
+
2. Build the package:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
3. Link to your n8n installation:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Navigate to your n8n custom nodes directory
|
|
42
|
+
cd ~/.n8n/nodes
|
|
43
|
+
|
|
44
|
+
# Link the package
|
|
45
|
+
npm link n8n-nodes-http-throttled-request
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
4. Restart your n8n instance
|
|
49
|
+
|
|
50
|
+
### Docker Installation
|
|
51
|
+
|
|
52
|
+
Mount the node package into your n8n container:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
# docker-compose.yml
|
|
56
|
+
services:
|
|
57
|
+
n8n:
|
|
58
|
+
image: n8nio/n8n
|
|
59
|
+
volumes:
|
|
60
|
+
- ./n8n-nodes-http-throttled-request:/home/node/.n8n/nodes/n8n-nodes-http-throttled-request
|
|
61
|
+
environment:
|
|
62
|
+
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/nodes
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
### Node Parameters
|
|
68
|
+
|
|
69
|
+
| Parameter | Type | Default | Description |
|
|
70
|
+
|-----------|------|---------|-------------|
|
|
71
|
+
| **Method** | Options | GET | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
72
|
+
| **URL** | String | - | Target URL for the request |
|
|
73
|
+
| **Authentication** | Options | None | Authentication type |
|
|
74
|
+
| **Send Headers** | Boolean | false | Enable custom headers |
|
|
75
|
+
| **Send Body** | Boolean | false | Enable request body |
|
|
76
|
+
| **Throttling aktivieren** | Boolean | true | Enable automatic rate-limit handling |
|
|
77
|
+
|
|
78
|
+
### Throttling Settings
|
|
79
|
+
|
|
80
|
+
When throttling is enabled, additional options become available:
|
|
81
|
+
|
|
82
|
+
| Setting | Type | Default | Description |
|
|
83
|
+
|---------|------|---------|-------------|
|
|
84
|
+
| **HTTP-Codes** | Multi-select | 429 | Status codes that trigger throttling (429, 503, 504) |
|
|
85
|
+
| **Standard-Wartezeit (ms)** | Number | 10000 | Default wait time when no header provides guidance |
|
|
86
|
+
| **Zufaellige Abweichung (+/-%)** | Number | 25 | Jitter percentage to prevent thundering herd |
|
|
87
|
+
| **Max. Throttle-Versuche** | Number | 10 | Maximum retry attempts before failing |
|
|
88
|
+
|
|
89
|
+
### Authentication Types
|
|
90
|
+
|
|
91
|
+
The node supports the following authentication methods:
|
|
92
|
+
|
|
93
|
+
- **None**: No authentication
|
|
94
|
+
- **Basic Auth**: HTTP Basic Authentication (username/password)
|
|
95
|
+
- **Header Auth**: Custom header-based authentication
|
|
96
|
+
- **OAuth1**: OAuth 1.0 authentication
|
|
97
|
+
- **OAuth2**: OAuth 2.0 authentication
|
|
98
|
+
|
|
99
|
+
## Usage Examples
|
|
100
|
+
|
|
101
|
+
### Basic GET Request with Throttling
|
|
102
|
+
|
|
103
|
+
1. Add the "HTTP Request" node to your workflow
|
|
104
|
+
2. Set the URL to your API endpoint
|
|
105
|
+
3. Enable "Throttling aktivieren" (enabled by default)
|
|
106
|
+
4. Configure throttling settings as needed
|
|
107
|
+
5. Execute the workflow
|
|
108
|
+
|
|
109
|
+
### POST Request with JSON Body
|
|
110
|
+
|
|
111
|
+
1. Add the HTTP Request node
|
|
112
|
+
2. Set Method to "POST"
|
|
113
|
+
3. Enter the target URL
|
|
114
|
+
4. Enable "Send Body"
|
|
115
|
+
5. Select "JSON" as Body Content Type
|
|
116
|
+
6. Enter your JSON payload in the Body field
|
|
117
|
+
|
|
118
|
+
### API Request with Rate Limit Handling
|
|
119
|
+
|
|
120
|
+
For APIs that enforce rate limits (e.g., HubSpot, GitHub, Stripe):
|
|
121
|
+
|
|
122
|
+
1. Enable throttling with appropriate HTTP codes (429, 503)
|
|
123
|
+
2. Set a reasonable default wait time (e.g., 10000ms)
|
|
124
|
+
3. Configure max retries based on your workflow timeout
|
|
125
|
+
4. Add jitter (25%) to distribute retry attempts
|
|
126
|
+
|
|
127
|
+
## How It Works
|
|
128
|
+
|
|
129
|
+
### Rate Limit Detection
|
|
130
|
+
|
|
131
|
+
When the node receives a response with a configured throttle status code (429, 503, or 504), it:
|
|
132
|
+
|
|
133
|
+
1. Extracts wait time from response headers
|
|
134
|
+
2. Applies jitter to prevent thundering herd
|
|
135
|
+
3. Waits the calculated time
|
|
136
|
+
4. Retries the request
|
|
137
|
+
5. Repeats until success or max retries reached
|
|
138
|
+
|
|
139
|
+
### Header Priority
|
|
140
|
+
|
|
141
|
+
The node calculates wait time using this priority:
|
|
142
|
+
|
|
143
|
+
1. **Retry-After** (highest priority)
|
|
144
|
+
- Seconds format: `Retry-After: 30`
|
|
145
|
+
- HTTP-Date format: `Retry-After: Wed, 19 Feb 2025 12:00:00 GMT`
|
|
146
|
+
|
|
147
|
+
2. **Rate Limit with Remaining=0**
|
|
148
|
+
- `X-RateLimit-Remaining: 0` combined with reset timestamp
|
|
149
|
+
|
|
150
|
+
3. **Reset Timestamp alone**
|
|
151
|
+
- `X-RateLimit-Reset: 1739966400`
|
|
152
|
+
|
|
153
|
+
4. **Default fallback** (lowest priority)
|
|
154
|
+
- Uses configured default wait time
|
|
155
|
+
|
|
156
|
+
### Supported Headers
|
|
157
|
+
|
|
158
|
+
| Header | Format | Example |
|
|
159
|
+
|--------|--------|---------|
|
|
160
|
+
| `Retry-After` | Seconds or HTTP-Date | `30` or `Wed, 19 Feb 2025 12:00:00 GMT` |
|
|
161
|
+
| `X-RateLimit-Reset` | Unix timestamp | `1739966400` |
|
|
162
|
+
| `X-RateLimit-Remaining` | Integer | `0` |
|
|
163
|
+
| `X-HubSpot-RateLimit-Reset` | Unix timestamp | `1739966400` |
|
|
164
|
+
| `X-HubSpot-RateLimit-Remaining` | Integer | `0` |
|
|
165
|
+
| `RateLimit-Reset` | Unix timestamp | `1739966400` |
|
|
166
|
+
| `RateLimit-Remaining` | Integer | `0` |
|
|
167
|
+
|
|
168
|
+
### Timestamp Detection
|
|
169
|
+
|
|
170
|
+
The node automatically detects timestamp format:
|
|
171
|
+
- Values > 10^12: Treated as milliseconds
|
|
172
|
+
- Values > 10^9: Treated as seconds (Unix timestamp)
|
|
173
|
+
|
|
174
|
+
### Safety Limits
|
|
175
|
+
|
|
176
|
+
- **Maximum wait time**: 5 minutes (300,000ms)
|
|
177
|
+
- **Jitter range**: 0-100%
|
|
178
|
+
- **Minimum jitter result**: 0ms (never negative)
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
### Build
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm run build
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Test
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm test
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Test with Coverage
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm test -- --coverage
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Project Structure
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
n8n-nodes-http-throttled-request/
|
|
204
|
+
├── package.json # Package configuration
|
|
205
|
+
├── tsconfig.json # TypeScript configuration
|
|
206
|
+
├── README.md # This file
|
|
207
|
+
├── nodes/
|
|
208
|
+
│ └── HttpRequest/
|
|
209
|
+
│ ├── HttpRequestThrottled.node.ts # Main node class
|
|
210
|
+
│ └── throttling.ts # Throttling logic
|
|
211
|
+
└── test/
|
|
212
|
+
└── throttling.test.ts # Unit tests
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Troubleshooting
|
|
216
|
+
|
|
217
|
+
### Node not appearing in n8n
|
|
218
|
+
|
|
219
|
+
1. Verify the package is installed in the correct location
|
|
220
|
+
2. Check n8n logs for loading errors
|
|
221
|
+
3. Ensure `npm run build` completed successfully
|
|
222
|
+
4. Restart n8n after installation
|
|
223
|
+
|
|
224
|
+
### Throttling not working
|
|
225
|
+
|
|
226
|
+
1. Verify "Throttling aktivieren" is enabled
|
|
227
|
+
2. Check that the API returns one of the configured HTTP codes
|
|
228
|
+
3. Review n8n execution logs for throttling messages
|
|
229
|
+
|
|
230
|
+
### Maximum retries exceeded
|
|
231
|
+
|
|
232
|
+
If you see "Maximale Anzahl Versuche erreicht":
|
|
233
|
+
1. Increase "Max. Throttle-Versuche" setting
|
|
234
|
+
2. Increase "Standard-Wartezeit" to wait longer between retries
|
|
235
|
+
3. Check if the API requires authentication or has other restrictions
|
|
236
|
+
|
|
237
|
+
## API Reference
|
|
238
|
+
|
|
239
|
+
### Throttling Module Exports
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// Maximum wait time cap (5 minutes)
|
|
243
|
+
export const MAX_THROTTLE_WAIT_MS = 300_000;
|
|
244
|
+
|
|
245
|
+
// Normalize headers to lowercase keys
|
|
246
|
+
export function normalizeHeaders(raw: Record<string, unknown>): Record<string, string>;
|
|
247
|
+
|
|
248
|
+
// Parse Retry-After header to milliseconds
|
|
249
|
+
export function parseRetryAfterToMs(v: string): number | null;
|
|
250
|
+
|
|
251
|
+
// Parse reset timestamp headers to wait milliseconds
|
|
252
|
+
export function parseResetToWaitMs(h: Record<string, string>): number | null;
|
|
253
|
+
|
|
254
|
+
// Compute wait time from response headers
|
|
255
|
+
export function computeWaitMs(rawHeaders: Record<string, unknown>, defaultWaitMs: number): number;
|
|
256
|
+
|
|
257
|
+
// Apply jitter to wait time
|
|
258
|
+
export function applyJitter(baseMs: number, jitterPct: number): number;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT
|
|
264
|
+
|
|
265
|
+
## Contributing
|
|
266
|
+
|
|
267
|
+
1. Fork the repository
|
|
268
|
+
2. Create a feature branch
|
|
269
|
+
3. Make your changes
|
|
270
|
+
4. Run tests: `npm test`
|
|
271
|
+
5. Submit a pull request
|
|
272
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
|
|
2
|
+
export declare class HttpRequestThrottled implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=HttpRequestThrottled.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpRequestThrottled.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/HttpRequestThrottled.node.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAKrB,MAAM,cAAc,CAAC;AAWtB,qBAAa,oBAAqB,YAAW,SAAS;IACpD,WAAW,EAAE,oBAAoB,CAuL/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAyMxE"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// nodes/HttpRequest/HttpRequestThrottled.node.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.HttpRequestThrottled = void 0;
|
|
5
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
6
|
+
const throttling_1 = require("./throttling");
|
|
7
|
+
// ── Hilfsfunktion ─────────────────────────────────────────────────────────────
|
|
8
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
9
|
+
// ── Node-Klasse ───────────────────────────────────────────────────────────────
|
|
10
|
+
class HttpRequestThrottled {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.description = {
|
|
13
|
+
// Exakten Wert aus Core Node V3 übernommen
|
|
14
|
+
name: "httpRequest",
|
|
15
|
+
displayName: "HTTP Request",
|
|
16
|
+
icon: "fa:at",
|
|
17
|
+
group: ["output"],
|
|
18
|
+
// v2: Versioned nodes – Array erlaubt mehrere Versionen gleichzeitig
|
|
19
|
+
version: [1, 2, 3],
|
|
20
|
+
defaultVersion: 3,
|
|
21
|
+
subtitle: '={{$parameter["method"] + ": " + $parameter["url"]}}',
|
|
22
|
+
description: "Makes an HTTP request and returns the response data (with throttling support)",
|
|
23
|
+
defaults: { name: "HTTP Request", color: "#2200DD" },
|
|
24
|
+
// Kompatibel mit n8n v1/v2: String-Literal 'main'
|
|
25
|
+
inputs: ["main"],
|
|
26
|
+
outputs: ["main"],
|
|
27
|
+
credentials: [
|
|
28
|
+
{
|
|
29
|
+
name: "httpBasicAuth",
|
|
30
|
+
required: false,
|
|
31
|
+
displayOptions: { show: { authentication: ["basicAuth"] } },
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "httpHeaderAuth",
|
|
35
|
+
required: false,
|
|
36
|
+
displayOptions: { show: { authentication: ["headerAuth"] } },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "oAuth1Api",
|
|
40
|
+
required: false,
|
|
41
|
+
displayOptions: { show: { authentication: ["oAuth1"] } },
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "oAuth2Api",
|
|
45
|
+
required: false,
|
|
46
|
+
displayOptions: { show: { authentication: ["oAuth2"] } },
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
properties: [
|
|
50
|
+
// ── Kern-Properties (minimal, für Shadow-Override vollständig aus Core übernehmen) ──
|
|
51
|
+
{
|
|
52
|
+
displayName: "Method",
|
|
53
|
+
name: "method",
|
|
54
|
+
type: "options",
|
|
55
|
+
options: [
|
|
56
|
+
{ name: "DELETE", value: "DELETE" },
|
|
57
|
+
{ name: "GET", value: "GET" },
|
|
58
|
+
{ name: "HEAD", value: "HEAD" },
|
|
59
|
+
{ name: "OPTIONS", value: "OPTIONS" },
|
|
60
|
+
{ name: "PATCH", value: "PATCH" },
|
|
61
|
+
{ name: "POST", value: "POST" },
|
|
62
|
+
{ name: "PUT", value: "PUT" },
|
|
63
|
+
],
|
|
64
|
+
default: "GET",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
displayName: "URL",
|
|
68
|
+
name: "url",
|
|
69
|
+
type: "string",
|
|
70
|
+
default: "",
|
|
71
|
+
placeholder: "https://example.com",
|
|
72
|
+
required: true,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
displayName: "Authentication",
|
|
76
|
+
name: "authentication",
|
|
77
|
+
type: "options",
|
|
78
|
+
options: [
|
|
79
|
+
{ name: "None", value: "none" },
|
|
80
|
+
{ name: "Basic Auth", value: "basicAuth" },
|
|
81
|
+
{ name: "Header Auth", value: "headerAuth" },
|
|
82
|
+
{ name: "OAuth1", value: "oAuth1" },
|
|
83
|
+
{ name: "OAuth2", value: "oAuth2" },
|
|
84
|
+
],
|
|
85
|
+
default: "none",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
displayName: "Send Headers",
|
|
89
|
+
name: "sendHeaders",
|
|
90
|
+
type: "boolean",
|
|
91
|
+
default: false,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
displayName: "Header Parameters",
|
|
95
|
+
name: "headerParameters",
|
|
96
|
+
type: "fixedCollection",
|
|
97
|
+
typeOptions: { multipleValues: true },
|
|
98
|
+
default: { parameters: [] },
|
|
99
|
+
displayOptions: { show: { sendHeaders: [true] } },
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
name: "parameters",
|
|
103
|
+
displayName: "Header",
|
|
104
|
+
values: [
|
|
105
|
+
{ displayName: "Name", name: "name", type: "string", default: "" },
|
|
106
|
+
{ displayName: "Value", name: "value", type: "string", default: "" },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
displayName: "Send Body",
|
|
113
|
+
name: "sendBody",
|
|
114
|
+
type: "boolean",
|
|
115
|
+
default: false,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
displayName: "Body Content Type",
|
|
119
|
+
name: "contentType",
|
|
120
|
+
type: "options",
|
|
121
|
+
displayOptions: { show: { sendBody: [true] } },
|
|
122
|
+
options: [
|
|
123
|
+
{ name: "JSON", value: "json" },
|
|
124
|
+
{ name: "Form Urlencoded", value: "form-urlencoded" },
|
|
125
|
+
{ name: "Raw", value: "raw" },
|
|
126
|
+
],
|
|
127
|
+
default: "json",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
displayName: "Body",
|
|
131
|
+
name: "body",
|
|
132
|
+
type: "string",
|
|
133
|
+
displayOptions: { show: { sendBody: [true] } },
|
|
134
|
+
default: "",
|
|
135
|
+
typeOptions: { rows: 4 },
|
|
136
|
+
},
|
|
137
|
+
// ── Throttling-Erweiterung ────────────────────────────────────────────────
|
|
138
|
+
{
|
|
139
|
+
displayName: "Throttling aktivieren",
|
|
140
|
+
name: "throttlingEnabled",
|
|
141
|
+
type: "boolean",
|
|
142
|
+
default: true,
|
|
143
|
+
description: "Wartet automatisch bei Rate-Limit-Antworten (429 etc.) und wertet Response-Header aus",
|
|
144
|
+
noDataExpression: true,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
displayName: "Throttling-Einstellungen",
|
|
148
|
+
name: "throttling",
|
|
149
|
+
type: "collection",
|
|
150
|
+
placeholder: "Einstellung hinzufügen",
|
|
151
|
+
default: {},
|
|
152
|
+
displayOptions: { show: { throttlingEnabled: [true] } },
|
|
153
|
+
options: [
|
|
154
|
+
{
|
|
155
|
+
displayName: "HTTP-Codes",
|
|
156
|
+
name: "throttleCodes",
|
|
157
|
+
type: "multiOptions",
|
|
158
|
+
default: ["429"],
|
|
159
|
+
description: "Bei diesen HTTP-Statuscodes wird Throttling ausgelöst",
|
|
160
|
+
options: [
|
|
161
|
+
{ name: "429 Too Many Requests", value: "429" },
|
|
162
|
+
{ name: "503 Service Unavailable", value: "503" },
|
|
163
|
+
{ name: "504 Gateway Timeout", value: "504" },
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
displayName: "Standard-Wartezeit (ms)",
|
|
168
|
+
name: "defaultWaitMs",
|
|
169
|
+
type: "number",
|
|
170
|
+
default: 10000,
|
|
171
|
+
description: "Wartezeit in Millisekunden, wenn kein passender Response-Header vorhanden ist",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
displayName: "Zufällige Abweichung (±%)",
|
|
175
|
+
name: "jitterPercent",
|
|
176
|
+
type: "number",
|
|
177
|
+
default: 25,
|
|
178
|
+
description: "Streut die Wartezeit um ±N%, um Thundering-Herd-Effekte bei parallelen Executions zu vermeiden",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
displayName: "Max. Throttle-Versuche",
|
|
182
|
+
name: "maxThrottleTries",
|
|
183
|
+
type: "number",
|
|
184
|
+
default: 10,
|
|
185
|
+
description: "Maximale Anzahl Throttling-Retries bevor ein Fehler geworfen wird",
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async execute() {
|
|
193
|
+
const items = this.getInputData();
|
|
194
|
+
const returnData = [];
|
|
195
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
196
|
+
const throttlingEnabled = this.getNodeParameter("throttlingEnabled", itemIndex, true);
|
|
197
|
+
const throttlingParams = throttlingEnabled
|
|
198
|
+
? this.getNodeParameter("throttling", itemIndex, {})
|
|
199
|
+
: {};
|
|
200
|
+
const throttleCodes = new Set((throttlingParams.throttleCodes ?? ["429"]).map(String));
|
|
201
|
+
const defaultWaitMs = throttlingParams.defaultWaitMs ?? 10000;
|
|
202
|
+
const jitterPercent = throttlingParams.jitterPercent ?? 25;
|
|
203
|
+
const maxThrottleTries = Math.max(1, throttlingParams.maxThrottleTries ?? 10);
|
|
204
|
+
// ── Request-Optionen zusammenbauen ──────────────────────────────────────
|
|
205
|
+
const method = this.getNodeParameter("method", itemIndex, "GET");
|
|
206
|
+
const url = this.getNodeParameter("url", itemIndex);
|
|
207
|
+
const sendHeaders = this.getNodeParameter("sendHeaders", itemIndex, false);
|
|
208
|
+
const sendBody = this.getNodeParameter("sendBody", itemIndex, false);
|
|
209
|
+
// v2: IHttpRequestOptions – kein freies Objekt mehr
|
|
210
|
+
const requestOptions = {
|
|
211
|
+
method: method,
|
|
212
|
+
url,
|
|
213
|
+
returnFullResponse: true, // v2: Gibt { statusCode, headers, body } zurück
|
|
214
|
+
ignoreHttpStatusErrors: true, // verhindert automatisches Throw bei 4xx/5xx
|
|
215
|
+
headers: {},
|
|
216
|
+
};
|
|
217
|
+
if (sendHeaders) {
|
|
218
|
+
const headerParams = this.getNodeParameter("headerParameters.parameters", itemIndex, []);
|
|
219
|
+
for (const h of headerParams) {
|
|
220
|
+
requestOptions.headers[h.name] = h.value;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (sendBody) {
|
|
224
|
+
const contentType = this.getNodeParameter("contentType", itemIndex, "json");
|
|
225
|
+
const bodyRaw = this.getNodeParameter("body", itemIndex, "");
|
|
226
|
+
if (contentType === "json") {
|
|
227
|
+
try {
|
|
228
|
+
requestOptions.body = JSON.parse(bodyRaw);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
requestOptions.body = bodyRaw;
|
|
232
|
+
}
|
|
233
|
+
requestOptions.headers["content-type"] =
|
|
234
|
+
"application/json";
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
requestOptions.body = bodyRaw;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// ── Throttling-Loop ─────────────────────────────────────────────────────
|
|
241
|
+
let throttleAttempt = 0;
|
|
242
|
+
while (true) {
|
|
243
|
+
// v2: this.helpers.httpRequest statt this.helpers.request
|
|
244
|
+
// Authentifizierung über httpRequestWithAuthentication wenn nötig
|
|
245
|
+
const authentication = this.getNodeParameter("authentication", itemIndex, "none");
|
|
246
|
+
let response;
|
|
247
|
+
try {
|
|
248
|
+
if (authentication !== "none") {
|
|
249
|
+
// Credential-Name aus authentication-Option ableiten
|
|
250
|
+
const credMap = {
|
|
251
|
+
basicAuth: "httpBasicAuth",
|
|
252
|
+
headerAuth: "httpHeaderAuth",
|
|
253
|
+
oAuth1: "oAuth1Api",
|
|
254
|
+
oAuth2: "oAuth2Api",
|
|
255
|
+
};
|
|
256
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
257
|
+
response = await this.helpers.httpRequestWithAuthentication(credMap[authentication] ?? authentication, requestOptions);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// v2: this.helpers.httpRequest
|
|
261
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
262
|
+
response = await this.helpers.httpRequest(requestOptions);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
// Netzwerkfehler → direkt werfen, nicht throtteln
|
|
267
|
+
if (err instanceof n8n_workflow_1.NodeApiError)
|
|
268
|
+
throw err;
|
|
269
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Netzwerkfehler: ${err.message}`, { itemIndex });
|
|
270
|
+
}
|
|
271
|
+
const statusStr = String(response.statusCode);
|
|
272
|
+
// ── Throttling-Branch ───────────────────────────────────────────────
|
|
273
|
+
// KRITISCH: Dieser Branch darf Retry-on-Fail NICHT auslösen.
|
|
274
|
+
// Deshalb kein throw, sondern manueller Sleep + continue.
|
|
275
|
+
if (throttlingEnabled && throttleCodes.has(statusStr)) {
|
|
276
|
+
throttleAttempt++;
|
|
277
|
+
if (throttleAttempt >= maxThrottleTries) {
|
|
278
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Throttling: Maximale Anzahl Versuche (${maxThrottleTries}) erreicht. Letzter Status: ${response.statusCode}`, { itemIndex });
|
|
279
|
+
}
|
|
280
|
+
const baseWait = (0, throttling_1.computeWaitMs)(response.headers, defaultWaitMs);
|
|
281
|
+
const wait = (0, throttling_1.applyJitter)(baseWait, jitterPercent);
|
|
282
|
+
this.logger.info(`[Throttling] Status ${response.statusCode} – Item ${itemIndex}, Versuch ${throttleAttempt}/${maxThrottleTries}, warte ${Math.round(wait)}ms`);
|
|
283
|
+
await sleep(wait);
|
|
284
|
+
continue; // Retry – KEIN Retry-on-Fail
|
|
285
|
+
}
|
|
286
|
+
// ── Normaler Fehler (nicht Throttle-Code) ───────────────────────────
|
|
287
|
+
if (response.statusCode >= 400) {
|
|
288
|
+
const continueOnFail = this.continueOnFail();
|
|
289
|
+
if (continueOnFail) {
|
|
290
|
+
returnData.push({
|
|
291
|
+
json: {
|
|
292
|
+
error: `HTTP ${response.statusCode}`,
|
|
293
|
+
body: response.body,
|
|
294
|
+
},
|
|
295
|
+
pairedItem: { item: itemIndex },
|
|
296
|
+
});
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `HTTP ${response.statusCode}: ${JSON.stringify(response.body)}`, { itemIndex });
|
|
300
|
+
}
|
|
301
|
+
// ── Erfolg ──────────────────────────────────────────────────────────
|
|
302
|
+
const body = response.body;
|
|
303
|
+
const json = typeof body === "object" && body !== null
|
|
304
|
+
? body
|
|
305
|
+
: { data: body };
|
|
306
|
+
returnData.push({
|
|
307
|
+
json,
|
|
308
|
+
pairedItem: { item: itemIndex },
|
|
309
|
+
});
|
|
310
|
+
break; // Erfolg → Loop verlassen
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return [returnData];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
exports.HttpRequestThrottled = HttpRequestThrottled;
|
|
317
|
+
//# sourceMappingURL=HttpRequestThrottled.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpRequestThrottled.node.js","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/HttpRequestThrottled.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Sicherheits-Cap: Server-Angaben über 5 Minuten werden auf diesen Wert begrenzt */
|
|
2
|
+
export declare const MAX_THROTTLE_WAIT_MS = 300000;
|
|
3
|
+
/**
|
|
4
|
+
* Vereinheitlicht alle Header-Keys auf Lowercase und wandelt
|
|
5
|
+
* Array-Werte (mehrfache Header) in Strings um.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeHeaders(raw: Record<string, unknown>): Record<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Gibt den ersten gültigen Integer aus den angegebenen Header-Keys zurück.
|
|
10
|
+
* Die Reihenfolge der Keys definiert die Priorität.
|
|
11
|
+
*/
|
|
12
|
+
export declare function firstPresentInt(h: Record<string, string>, keys: string[]): number | null;
|
|
13
|
+
/**
|
|
14
|
+
* Parst den Retry-After-Header.
|
|
15
|
+
*
|
|
16
|
+
* Unterstützte Formate:
|
|
17
|
+
* - Sekunden als Integer: "30"
|
|
18
|
+
* - HTTP-Date (RFC 7231): "Wed, 19 Feb 2025 12:00:00 GMT"
|
|
19
|
+
*
|
|
20
|
+
* Gibt null zurück wenn das Format nicht erkannt wird.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseRetryAfterToMs(v: string): number | null;
|
|
23
|
+
/**
|
|
24
|
+
* Berechnet die Wartezeit aus einem Rate-Limit-Reset-Header.
|
|
25
|
+
*
|
|
26
|
+
* Heuristik für den Timestamp-Typ:
|
|
27
|
+
* > 10^12 → Milliseconds (moderner POSIX-ms)
|
|
28
|
+
* > 10^9 → Seconds (klassischer Unix-Timestamp)
|
|
29
|
+
* sonst → Seconds (Fallback)
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseResetToWaitMs(h: Record<string, string>): number | null;
|
|
32
|
+
/**
|
|
33
|
+
* Berechnet die Wartezeit in Millisekunden aus den Response-Headern.
|
|
34
|
+
*
|
|
35
|
+
* Prioritätsreihenfolge:
|
|
36
|
+
* 1. Retry-After Header (expliziteste Server-Aussage)
|
|
37
|
+
* 2. Remaining = 0 → Reset-Timestamp oder Default
|
|
38
|
+
* 3. Reset-Timestamp allein
|
|
39
|
+
* 4. Konfigurierbarer Default-Wert
|
|
40
|
+
*
|
|
41
|
+
* Das Ergebnis wird auf MAX_THROTTLE_WAIT_MS gecappt.
|
|
42
|
+
*/
|
|
43
|
+
export declare function computeWaitMs(rawHeaders: Record<string, unknown>, defaultWaitMs: number): number;
|
|
44
|
+
/**
|
|
45
|
+
* Wendet einen gleichverteilten Jitter auf die Wartezeit an.
|
|
46
|
+
*
|
|
47
|
+
* Zweck: Thundering-Herd verhindern, wenn viele parallele Executions
|
|
48
|
+
* gleichzeitig nach einem 429 wiederanlaufen.
|
|
49
|
+
*
|
|
50
|
+
* @param baseMs Basiswert in Millisekunden
|
|
51
|
+
* @param jitterPct Maximale Abweichung in Prozent (0–100)
|
|
52
|
+
* @returns Jitter-behafteter Wert, niemals negativ
|
|
53
|
+
*/
|
|
54
|
+
export declare function applyJitter(baseMs: number, jitterPct: number): number;
|
|
55
|
+
//# sourceMappingURL=throttling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttling.d.ts","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/throttling.ts"],"names":[],"mappings":"AAIA,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,SAAU,CAAC;AAI5C;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzB,IAAI,EAAE,MAAM,EAAE,GACb,MAAM,GAAG,IAAI,CAQf;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe5D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACxB,MAAM,GAAG,IAAI,CAWf;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,aAAa,EAAE,MAAM,GACpB,MAAM,CA4BR;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKrE"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// nodes/HttpRequest/throttling.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.MAX_THROTTLE_WAIT_MS = void 0;
|
|
5
|
+
exports.normalizeHeaders = normalizeHeaders;
|
|
6
|
+
exports.firstPresentInt = firstPresentInt;
|
|
7
|
+
exports.parseRetryAfterToMs = parseRetryAfterToMs;
|
|
8
|
+
exports.parseResetToWaitMs = parseResetToWaitMs;
|
|
9
|
+
exports.computeWaitMs = computeWaitMs;
|
|
10
|
+
exports.applyJitter = applyJitter;
|
|
11
|
+
// ── Konstanten ────────────────────────────────────────────────────────────────
|
|
12
|
+
/** Sicherheits-Cap: Server-Angaben über 5 Minuten werden auf diesen Wert begrenzt */
|
|
13
|
+
exports.MAX_THROTTLE_WAIT_MS = 300000;
|
|
14
|
+
// ── Header-Normalisierung ─────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Vereinheitlicht alle Header-Keys auf Lowercase und wandelt
|
|
17
|
+
* Array-Werte (mehrfache Header) in Strings um.
|
|
18
|
+
*/
|
|
19
|
+
function normalizeHeaders(raw) {
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const [k, v] of Object.entries(raw ?? {})) {
|
|
22
|
+
if (v == null)
|
|
23
|
+
continue;
|
|
24
|
+
out[k.toLowerCase()] = Array.isArray(v) ? String(v[0]) : String(v);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
// ── Hilfsfunktionen ───────────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Gibt den ersten gültigen Integer aus den angegebenen Header-Keys zurück.
|
|
31
|
+
* Die Reihenfolge der Keys definiert die Priorität.
|
|
32
|
+
*/
|
|
33
|
+
function firstPresentInt(h, keys) {
|
|
34
|
+
for (const k of keys) {
|
|
35
|
+
const v = h[k];
|
|
36
|
+
if (!v)
|
|
37
|
+
continue;
|
|
38
|
+
const n = parseInt(v, 10);
|
|
39
|
+
if (Number.isFinite(n))
|
|
40
|
+
return n;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parst den Retry-After-Header.
|
|
46
|
+
*
|
|
47
|
+
* Unterstützte Formate:
|
|
48
|
+
* - Sekunden als Integer: "30"
|
|
49
|
+
* - HTTP-Date (RFC 7231): "Wed, 19 Feb 2025 12:00:00 GMT"
|
|
50
|
+
*
|
|
51
|
+
* Gibt null zurück wenn das Format nicht erkannt wird.
|
|
52
|
+
*/
|
|
53
|
+
function parseRetryAfterToMs(v) {
|
|
54
|
+
const trimmed = v.trim();
|
|
55
|
+
if (/^\d+$/.test(trimmed)) {
|
|
56
|
+
const sec = parseInt(trimmed, 10);
|
|
57
|
+
return Number.isFinite(sec) && sec >= 0 ? sec * 1000 : null;
|
|
58
|
+
}
|
|
59
|
+
const dt = Date.parse(trimmed); // JS Date.parse verarbeitet RFC-1123 nativ
|
|
60
|
+
if (!Number.isNaN(dt)) {
|
|
61
|
+
const delta = dt - Date.now();
|
|
62
|
+
return delta > 0 ? delta : 0;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Berechnet die Wartezeit aus einem Rate-Limit-Reset-Header.
|
|
68
|
+
*
|
|
69
|
+
* Heuristik für den Timestamp-Typ:
|
|
70
|
+
* > 10^12 → Milliseconds (moderner POSIX-ms)
|
|
71
|
+
* > 10^9 → Seconds (klassischer Unix-Timestamp)
|
|
72
|
+
* sonst → Seconds (Fallback)
|
|
73
|
+
*/
|
|
74
|
+
function parseResetToWaitMs(h) {
|
|
75
|
+
const reset = firstPresentInt(h, [
|
|
76
|
+
"x-ratelimit-reset",
|
|
77
|
+
"x-hubspot-ratelimit-reset",
|
|
78
|
+
"ratelimit-reset",
|
|
79
|
+
]);
|
|
80
|
+
if (reset === null)
|
|
81
|
+
return null;
|
|
82
|
+
const tsMs = reset > 1000000000000 ? reset : reset * 1000;
|
|
83
|
+
const delta = tsMs - Date.now();
|
|
84
|
+
return delta > 0 ? delta : 0;
|
|
85
|
+
}
|
|
86
|
+
// ── Kernfunktionen ────────────────────────────────────────────────────────────
|
|
87
|
+
/**
|
|
88
|
+
* Berechnet die Wartezeit in Millisekunden aus den Response-Headern.
|
|
89
|
+
*
|
|
90
|
+
* Prioritätsreihenfolge:
|
|
91
|
+
* 1. Retry-After Header (expliziteste Server-Aussage)
|
|
92
|
+
* 2. Remaining = 0 → Reset-Timestamp oder Default
|
|
93
|
+
* 3. Reset-Timestamp allein
|
|
94
|
+
* 4. Konfigurierbarer Default-Wert
|
|
95
|
+
*
|
|
96
|
+
* Das Ergebnis wird auf MAX_THROTTLE_WAIT_MS gecappt.
|
|
97
|
+
*/
|
|
98
|
+
function computeWaitMs(rawHeaders, defaultWaitMs) {
|
|
99
|
+
const h = normalizeHeaders(rawHeaders);
|
|
100
|
+
const cap = (ms) => Math.min(ms, exports.MAX_THROTTLE_WAIT_MS);
|
|
101
|
+
// 1) Retry-After
|
|
102
|
+
const ra = h["retry-after"];
|
|
103
|
+
if (ra) {
|
|
104
|
+
const ms = parseRetryAfterToMs(ra);
|
|
105
|
+
if (ms !== null && ms > 0)
|
|
106
|
+
return cap(ms);
|
|
107
|
+
}
|
|
108
|
+
// 2) Remaining = 0
|
|
109
|
+
const remaining = firstPresentInt(h, [
|
|
110
|
+
"x-ratelimit-remaining",
|
|
111
|
+
"x-hubspot-ratelimit-remaining",
|
|
112
|
+
"ratelimit-remaining",
|
|
113
|
+
]);
|
|
114
|
+
if (remaining !== null && remaining <= 0) {
|
|
115
|
+
const resetMs = parseResetToWaitMs(h);
|
|
116
|
+
return cap(resetMs !== null && resetMs > 0 ? resetMs : defaultWaitMs);
|
|
117
|
+
}
|
|
118
|
+
// 3) Reset-Timestamp
|
|
119
|
+
const resetMs = parseResetToWaitMs(h);
|
|
120
|
+
if (resetMs !== null && resetMs > 0)
|
|
121
|
+
return cap(resetMs);
|
|
122
|
+
// 4) Fallback
|
|
123
|
+
return cap(defaultWaitMs);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Wendet einen gleichverteilten Jitter auf die Wartezeit an.
|
|
127
|
+
*
|
|
128
|
+
* Zweck: Thundering-Herd verhindern, wenn viele parallele Executions
|
|
129
|
+
* gleichzeitig nach einem 429 wiederanlaufen.
|
|
130
|
+
*
|
|
131
|
+
* @param baseMs Basiswert in Millisekunden
|
|
132
|
+
* @param jitterPct Maximale Abweichung in Prozent (0–100)
|
|
133
|
+
* @returns Jitter-behafteter Wert, niemals negativ
|
|
134
|
+
*/
|
|
135
|
+
function applyJitter(baseMs, jitterPct) {
|
|
136
|
+
const pct = Math.max(0, Math.min(100, jitterPct));
|
|
137
|
+
const variance = baseMs * (pct / 100);
|
|
138
|
+
const jitter = (Math.random() * 2 - 1) * variance;
|
|
139
|
+
return Math.max(0, baseMs + jitter);
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=throttling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttling.js","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/throttling.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bauer-group/n8n-nodes-http-throttled-request",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "n8n community node with built-in HTTP request throttling (rate limiting)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/bauer-group/EXT-n8n-httpThrottled.git"
|
|
9
|
+
},
|
|
10
|
+
"main": "dist/nodes/HttpRequest/HttpRequestThrottled.node.js",
|
|
11
|
+
"n8n": {
|
|
12
|
+
"nodes": [
|
|
13
|
+
{
|
|
14
|
+
"file": "dist/nodes/HttpRequest/HttpRequestThrottled.node.js"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist/",
|
|
20
|
+
"package.json",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public",
|
|
26
|
+
"registry": "https://registry.npmjs.org/"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"clean": "rimraf dist",
|
|
30
|
+
"build": "tsc -p tsconfig.json",
|
|
31
|
+
"rebuild": "npm run clean && npm run build",
|
|
32
|
+
"test": "jest",
|
|
33
|
+
"prepublishOnly": "npm run build"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"n8n-workflow": ">=2.9.0 <3",
|
|
38
|
+
"n8n-core": ">=2.9.0 <3"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"@types/node": "^25.3.0",
|
|
43
|
+
"jest": "^30.2.0",
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
45
|
+
"ts-jest": "^29.4.6",
|
|
46
|
+
"n8n-workflow": "^2.9.0",
|
|
47
|
+
"n8n-core": "^2.9.0",
|
|
48
|
+
"rimraf": "^6.1.3"
|
|
49
|
+
},
|
|
50
|
+
"jest": {
|
|
51
|
+
"testEnvironment": "node",
|
|
52
|
+
"testMatch": [
|
|
53
|
+
"**/test/**/*.test.ts"
|
|
54
|
+
],
|
|
55
|
+
"transform": {
|
|
56
|
+
"^.+\\.ts$": [
|
|
57
|
+
"ts-jest",
|
|
58
|
+
{
|
|
59
|
+
"tsconfig": "tsconfig.test.json"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|