Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/js.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Callbacks handling

All callbacks in Milo have the following signature (TypeScript syntax):
Range callbacks in Milo have the following signature (TypeScript syntax):

```typescript
(parser: number, offset: number, length: number) => void
Expand All @@ -18,6 +18,23 @@ where the parameters have the following meaning:

If length is `0`, it means the callback has no payload associated.

`on_headers` instead receives parsed message metadata:

```typescript
(
parser: number,
offset: number,
methodOrStatus: number,
shouldKeepAlive: boolean,
shouldUpgrade: boolean,
hasTrailers: boolean,
bodyKind: number,
contentLength: bigint
) => void
```

`contentLength` is a BigInt so every accepted `Content-Length` value remains exact.

Callbacks are dispatched only when enabled with `setActiveCallbacks`.

Callbacks are disabled by default.
Expand Down
4 changes: 2 additions & 2 deletions macros/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn generate_callbacks(callbacks: &[String]) -> TokenStream {
should_upgrade: bool,
has_trailers: bool,
body_kind: u8,
content_length: f64,
content_length: u64,
);

#[cfg(any(debug_assertions, feature = "debug"))]
Expand Down Expand Up @@ -87,7 +87,7 @@ pub fn generate_callbacks(callbacks: &[String]) -> TokenStream {
let should_upgrade = unsafe { *self.events.add(cursor + 8) } != 0;
let has_trailers = unsafe { *self.events.add(cursor + 9) } != 0;
let body_kind = unsafe { *self.events.add(cursor + 10) };
let content_length = unsafe { core::ptr::read_unaligned(self.events.add(cursor + 11) as *const u64) }.to_le() as f64;
let content_length = unsafe { core::ptr::read_unaligned(self.events.add(cursor + 11) as *const u64) }.to_le();

if self.active_callbacks & CALLBACK_ACTIVE_ON_HEADERS != 0 {
unsafe {
Expand Down
19 changes: 19 additions & 0 deletions parser/wasm/src/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ function hasDebug () {
return this.milo_has_debug() !== 0
}

function wrapOnHeaders (callback) {
return (parser, at, methodOrStatus, keepAlive, shouldUpgrade, hasTrailers, bodyKind, contentLength) => {
callback(
parser,
at,
methodOrStatus,
keepAlive !== 0,
shouldUpgrade !== 0,
hasTrailers !== 0,
bodyKind,
BigInt.asUintN(64, contentLength)
)
}
}

/* REPLACE: enums */

/* REPLACE: getters */
Expand Down Expand Up @@ -77,6 +92,10 @@ export function setup (env = {}) {
logger = log.bind(context, logOption)
}

if (instanceEnvironment.on_headers) {
instanceEnvironment.on_headers = wrapOnHeaders(instanceEnvironment.on_headers)
}

// Create the WASM instance
/* eslint-disable-next-line no-undef */
const instance = new WebAssembly.Instance(wasmModule, {
Expand Down
33 changes: 33 additions & 0 deletions parser/wasm/test/issues.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,39 @@ it('issue-24 - memory_deallocation', async () => {
}
})

for (const contentLength of [9007199254740993n, 9999999999999999999n]) {
it(`headers_content_length_metadata_preserves_${contentLength}`, () => {
let received
const milo = setup({
on_headers (parser, at, methodOrStatus, keepAlive, shouldUpgrade, hasTrailers, bodyKind, value) {
received = { keepAlive, shouldUpgrade, hasTrailers, bodyKind, contentLength: value }
}
})
const parser = milo.create()
const message = Buffer.from(`HTTP/1.1 200 OK\r\nContent-Length: ${contentLength}\r\n\r\n`)
const ptr = milo.alloc(message.length)

try {
milo.setShouldAutodetect(parser, false)
milo.setShouldSuspendAfterHeaders(parser, true)
milo.setActiveCallbacks(parser, milo.CALLBACK_ACTIVE_ON_HEADERS)
new Uint8Array(milo.memory.buffer, ptr, message.length).set(message)

assert.equal(milo.parse(parser, ptr, message.length), message.length)
assert.deepEqual(received, {
keepAlive: true,
shouldUpgrade: false,
hasTrailers: false,
bodyKind: 0,
contentLength
})
} finally {
milo.destroy(parser)
milo.dealloc(ptr, message.length)
}
})
}

it('issue-25 - headers_upgrade_metadata', () => {
const cases = [
...[100, 101, 103, 200, 204, 301, 304, 400, 426, 500].map(status => ({
Expand Down
Loading