You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Scripts can also return `body` as a plain string, object, or array - the runtime auto-converts them. See [Body auto-conversion](#body-auto-conversion) below.
54
+
:::
55
+
55
56
### `Deno.core.decode(buffer)`
56
57
57
58
Decodes a `Uint8Array` back into a JavaScript string using **UTF-8**:
// resp.statusCode, resp.headers, resp.body are available
290
292
return resp;
291
293
})()
292
294
```
293
295
296
+
#### Transparent decompression
297
+
298
+
When the upstream server returns a compressed response (e.g. `Content-Encoding: gzip`), `op_proxy_request`**automatically decompresses** the body before returning it to the script. Supported encodings: `gzip`, `x-gzip`, `deflate`, `br` (Brotli), and `zstd` (Zstandard).
299
+
300
+
After decompression, the original transport headers are preserved under renamed keys so you can still see what the upstream sent:
This means scripts can always work with the decompressed body directly (e.g. `JSON.parse(Deno.core.decode(new Uint8Array(resp.body)))`) regardless of whether the upstream compresses its responses.
308
+
309
+
To **opt out** of automatic decompression (e.g. to forward compressed bytes as-is), pass `decompress: false`:
310
+
311
+
```javascript
312
+
constresp=awaitDeno.core.ops.op_proxy_request({
313
+
url:'https://upstream/api',
314
+
decompress:false, // body stays compressed, content-encoding header is preserved
315
+
});
316
+
```
317
+
294
318
**Error handling.** When the upstream request fails, the op throws a JavaScript error with a descriptive message. Scripts can catch these errors and return a custom response:
|`Invalid URL '…': …`| The `url` field could not be parsed. |
339
+
|`URL not allowed (non-public address): …`| SSRF protection blocked the URL because it resolves to a private/internal IP address. |
340
+
|`Invalid HTTP method: '…'`| The `method` value is not a valid HTTP method token. |
341
+
|`Invalid header name: '…'`| A key in `headers` is not a valid HTTP header name. |
342
+
|`Invalid header value for '…'`| A value in `headers` contains invalid characters. |
343
+
|`Upstream request timed out: …`| The upstream server did not respond within the configured timeout. |
344
+
|`Failed to connect to upstream: …`| Could not establish a TCP connection to the upstream server. |
345
+
|`Upstream request failed: …`| A general upstream request failure (DNS, TLS, etc.). |
346
+
|`Upstream response body too large: …`| The response body exceeded the configured size limit. |
347
+
|`Failed to decompress gzip/deflate/brotli/zstd …`| The response body claimed a content-encoding but the data was invalid. |
323
348
324
349
:::caution Security
325
350
By default, `op_proxy_request` only allows requests to **publicly routable** IP addresses. URLs that resolve to private or loopback addresses (e.g. `127.0.0.1`, `10.x.x.x`, `192.168.x.x`) are rejected to prevent [Server-Side Request Forgery (SSRF)](https://owasp.org/www-community/attacks/Server-Side_Request_Forgery). This restriction can be relaxed per subscription tier for local development scenarios.
@@ -332,3 +357,44 @@ Each responder has a maximum number of concurrent proxy requests it can handle s
332
357
:::tip Response tracking
333
358
When using `op_proxy_request`, you can store the upstream response alongside the tracked request by returning `trackResponse: true` in your script result. This is especially useful for debugging proxy issues. See [Track responses](/docs/guides/webhooks#track-responses) for details.
334
359
:::
360
+
361
+
## Body auto-conversion {#body-auto-conversion}
362
+
363
+
Responder scripts can return `body` as several types - not just `Uint8Array`. The runtime automatically converts the value before sending the response:
context.query.arg ?? 'Query string does not include \`arg\` parameter'
180
-
)
177
+
body: context.query.arg ?? 'Query string does not include \`arg\` parameter'
181
178
};
182
179
})();`}</CodeBlock></td></tr>
183
180
</tbody>
@@ -240,28 +237,28 @@ interface ScriptResult {
240
237
// Optional HTTP headers of the response. If not specified, the default headers of responder are used.
241
238
headers?:Record<string, string>;
242
239
// Optional HTTP body of the response. If not specified, the default body of responder is used.
243
-
body?:Uint8Array;
240
+
// Accepts Uint8Array, string, object, or array - see Body auto-conversion.
241
+
body?:Uint8Array|string|object;
244
242
// When true, the request is not recorded in the responder's tracked request history.
245
243
skipRequest?:boolean;
246
244
// When true, the response sent to the client is also stored alongside the tracked request.
247
245
trackResponse?:boolean;
248
246
}
249
247
```
250
248
249
+
:::tip Body auto-conversion
250
+
The `body` field accepts multiple types: `Uint8Array` for raw bytes, a **string** (auto-encoded to UTF-8), a **plain object or array** (auto-serialized to JSON), or a **number/boolean** (auto-stringified). See [Body auto-conversion](/docs/guides/platform/deno_runtime#body-auto-conversion) for the full conversion table.
251
+
:::
252
+
251
253
### Override response properties
252
254
The script overrides the responder's response with a custom status code, headers, and body:
253
255
254
256
```javascript
255
257
(async () => {
256
258
return {
257
259
statusCode:201,
258
-
headers: {
259
-
"Content-Type":"application/json"
260
-
},
261
-
// Encode body as binary data.
262
-
body:Deno.core.encode(
263
-
JSON.stringify({ a:1, b:2 })
264
-
)
260
+
headers: { "Content-Type":"application/json" },
261
+
body: { a:1, b:2 },
265
262
};
266
263
})();
267
264
```
@@ -278,7 +275,7 @@ This script inspects the incoming request properties and returns them as a JSON
278
275
279
276
// Override response with a custom HTML body.
280
277
return {
281
-
body:Deno.core.encode(`
278
+
body:`
282
279
<h2>Request headers</h2>
283
280
<table>
284
281
<tr><th>Header</th><th>Value</th></tr>
@@ -303,7 +300,7 @@ This script inspects the incoming request properties and returns them as a JSON
@@ -340,7 +337,7 @@ Responder scripts can forward incoming requests to a real backend using `Deno.co
340
337
})()
341
338
```
342
339
343
-
**Transform the response**-e.g. inject a field into JSON responses:
340
+
**Transform the response**-e.g., inject a field into JSONresponses. Compressedresponses (gzip, deflate, Brotli) are automatically decompressed, so `JSON.parse` works regardless of the upstream's `Content-Encoding`:
344
341
345
342
```javascript
346
343
(async () => {
@@ -354,11 +351,7 @@ Responder scripts can forward incoming requests to a real backend using `Deno.co
354
351
if (resp.headers['content-type']?.includes('application/json')) {
355
352
const body = JSON.parse(Deno.core.decode(new Uint8Array(resp.body)));
0 commit comments