diff --git a/docs/js.md b/docs/js.md index d43df97..a3ded33 100644 --- a/docs/js.md +++ b/docs/js.md @@ -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 @@ -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. diff --git a/macros/src/wasm.rs b/macros/src/wasm.rs index 8b8cc3e..76fd324 100644 --- a/macros/src/wasm.rs +++ b/macros/src/wasm.rs @@ -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"))] @@ -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 { diff --git a/parser/wasm/src/template.js b/parser/wasm/src/template.js index 8c3d066..55a117f 100644 --- a/parser/wasm/src/template.js +++ b/parser/wasm/src/template.js @@ -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 */ @@ -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, { diff --git a/parser/wasm/test/issues.test.js b/parser/wasm/test/issues.test.js index 3ee83c1..e0119b3 100644 --- a/parser/wasm/test/issues.test.js +++ b/parser/wasm/test/issues.test.js @@ -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 => ({