diff --git a/docs/configuration.md b/docs/configuration.md index 9adbb72..6b3e499 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -609,9 +609,12 @@ Unknown options and values of the wrong type raise an error. | `header(name)` | First value of a header, case-insensitive; `nil`/`null` if missing | | `cookie(name)` | Value of a cookie from `Set-Cookie`; `nil`/`null` if missing | | `json()` | JavaScript only: parses the body as JSON | +| `bytes()` | JavaScript only: the body as a `Uint8Array` | In Lua, call the helpers as `res:header("name")` or `res.header("name")`. +> **Note:** JavaScript strings hold UTF-16, so `body` replaces bytes that are not valid UTF-8 and `body.length` counts code units rather than bytes. Use `bytes()` for binary responses and for the real byte count. Lua strings keep the bytes as they arrived. + **Behavior:** - **Runs on every request:** the requests are sent each time `transform` runs, so every main request triggers them. Script globals persist across requests within a worker, so a script can cache a value itself (e.g. `if not token then token = login() end`). diff --git a/internal/script/js_http.go b/internal/script/js_http.go index 7cdce59..ee58c1c 100644 --- a/internal/script/js_http.go +++ b/internal/script/js_http.go @@ -159,6 +159,16 @@ func (e *JsEngine) httpResponseToObject(resp *HTTPResponse) *goja.Object { } return value }) + // body is a JavaScript string, so bytes that are not valid UTF-8 reach the script + // only through this. + _ = obj.Set("bytes", func(goja.FunctionCall) goja.Value { + buffer := rt.NewArrayBuffer([]byte(resp.Body)) + value, err := rt.New(rt.Get("Uint8Array").ToObject(rt), rt.ToValue(buffer)) + if err != nil { + panic(err) + } + return value + }) return obj }