Skip to content

Commit 3200010

Browse files
committed
Fix setFileHandler
1 parent 3fac06f commit 3200010

File tree

2 files changed

+63
-49
lines changed

2 files changed

+63
-49
lines changed

src/lib.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export function loadLib() {
265265
},
266266
webui_interface_set_response_file_handler: {
267267
// void webui_interface_set_response_file_handler(size_t window, const void* response, int length)
268-
parameters: ["usize", "buffer", "usize"],
268+
parameters: ["usize", "pointer", "usize"],
269269
result: "void",
270270
}
271271
} as const,

src/webui.ts

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ const windows: Map<Usize, WebUI> = new Map();
2525
let _lib: WebUILib;
2626

2727
export class WebUI {
28-
#window: Usize;
28+
#window: Usize = 0;
2929
#lib: WebUILib;
30+
#isFileHandler: Boolean = false;
3031

3132
/**
3233
* Instanciate a new WebUI window.
@@ -96,21 +97,24 @@ export class WebUI {
9697
BigInt(this.#window),
9798
toCString(content),
9899
);
99-
if (!status) {
100-
throw new WebUIError(`unable to start the browser`);
101-
}
102-
103-
for (let i = 0; i < 120; i++) { // 30 seconds timeout
100+
if (!this.#isFileHandler) {
101+
// Check if window is lanched
102+
if (!status) {
103+
throw new WebUIError(`unable to start the browser`);
104+
}
105+
// Wait for window connection
106+
for (let i = 0; i < 120; i++) { // 30 seconds timeout
107+
if (!this.isShown) {
108+
await new Promise((resolve) => setTimeout(resolve, 250));
109+
} else {
110+
break;
111+
}
112+
}
113+
// Check if window is connected
104114
if (!this.isShown) {
105-
await new Promise((resolve) => setTimeout(resolve, 250));
106-
} else {
107-
break;
115+
throw new WebUIError(`unable to connect to the browser`);
108116
}
109117
}
110-
111-
if (!this.isShown) {
112-
throw new WebUIError(`unable to connect to the browser`);
113-
}
114118
}
115119

116120
/**
@@ -422,62 +426,73 @@ export class WebUI {
422426
}
423427

424428
/**
425-
* Sets a custom handler to respond to file requests from the web browser.
429+
* Sets a custom files handler to respond to HTTP requests.
426430
*
427-
* @param handler - Callback that takes an URL, and return a HTTP header + body `string` or `Uint8Array`.
431+
* @param handler - Callback that takes an URL, and return a full HTTP header
432+
* + body. (`string` or `Uint8Array`).
428433
*
429434
* @example
430-
* const myWindow = new WebUI()
431435
*
432-
* myWindow.setFileHandler((myUrl: URL) => {
433-
* if (myUrl.pathname === '/app.js') return "console.log('hello from client')"
434-
* if (myUrl.pathname === '/img.png') return imgBytes
435-
* throw new Error(`Unknown request "${myUrl.pathname}""`)
436-
* })
436+
* async function myFileHandler(myUrl: URL) {
437+
* if (myUrl.pathname === '/test') {
438+
* return "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello";
439+
* }
440+
* };
437441
*
438-
* myWindow.show(
439-
* `<html>
440-
* <script src="webui.js">/script>
441-
* <script src="app.js"></script>
442-
* <img src="img.png" />
443-
* </html>`
444-
* )
442+
* myWindow.setFileHandler(myFileHandler);
445443
*/
446-
setFileHandler(handler: (url: URL) => string | Uint8Array) {
447-
// const void* (*handler)(const char *filename, int *length)
448-
const cb = new Deno.UnsafeCallback(
444+
setFileHandler(callback: (url: URL) => Promise<string | Uint8Array>) {
445+
446+
// C: .show_wait_connection = false; // 0
447+
// Disable `.show()` auto waiting for window connection,
448+
// otherwise `.setFileHandler()` will be blocked.
449+
_lib.symbols.webui_set_config(BigInt(0), false);
450+
451+
// C: .use_cookies = false; // 4
452+
// We need to disable WebUI Cookies because
453+
// user will use his own custom HTTP header
454+
// in `.setFileHandler()`.
455+
_lib.symbols.webui_set_config(BigInt(4), false);
456+
457+
// Let `.show()` knows that the user is using `.setFileHandler()`
458+
// so no need to wait for window connection in `.show()`.
459+
this.#isFileHandler = true;
460+
461+
// Create the callback
462+
const callbackResource = new Deno.UnsafeCallback(
449463
{
464+
// const void* (*handler)(const char *filename, int *length)
450465
parameters: ["buffer", "pointer"],
451466
result: "pointer",
452-
},
453-
(
454-
pointerUrl: Deno.PointerValue,
455-
pointerLength: Deno.PointerValue,
467+
} as const,
468+
async (
469+
param_url: Deno.PointerValue,
470+
param_length: Deno.PointerValue,
456471
) => {
457472
// Get URL as string
458-
const url_str :string = pointerUrl !== null ?
459-
new Deno.UnsafePointerView(pointerUrl).getCString()
473+
const url_str :string = param_url !== null ?
474+
new Deno.UnsafePointerView(param_url).getCString()
460475
: "";
461476

462477
// Create URL Obj
463478
const url_obj :URL = new URL(url_str, "http://localhost");
464479

465-
// Call user callback
466-
const user_response :string|Uint8Array = handler(url_obj);
480+
// Call the user callback
481+
const user_response: string|Uint8Array = await callback(url_obj);
467482

468483
// We can pass a local buffer to WebUI like this:
469484
// `return Deno.UnsafePointer.of(user_response);` However,
470485
// this may create a memory leak because WebUI cannot free
471486
// it, or cause memory corruption as Deno may free the buffer
472487
// before WebUI uses it. Therefore, the solution is to create
473-
// a safe WebUI buffer through WebUI. This WebUI buffer will
488+
// a safe WebUI buffer through WebUI API. This WebUI buffer will
474489
// be automatically freed by WebUI later.
475490
const webui_buffer :Deno.PointerValue = _lib.symbols.webui_malloc(BigInt(user_response.length));
476491

477492
// Copy data to C safe buffer
478493
if (typeof user_response === "string") {
479494
// copy `user_response` to `webui_buffer` as String data
480-
const cString = Deno.UnsafePointerView.toCString(user_response);
495+
const cString = toCString(user_response);
481496
const webui_buffer_ref = new Uint8Array(Deno.UnsafePointerView.getArrayBuffer(webui_buffer, cString.byteLength));
482497
webui_buffer_ref.set(new Uint8Array(cString));
483498
} else {
@@ -492,18 +507,15 @@ export class WebUI {
492507
webui_buffer,
493508
BigInt(user_response.length),
494509
);
495-
496-
// Return webui-buffer to webui
497-
return 0;
498510
},
499511
);
500-
512+
// Pass the callback pointer to WebUI
501513
this.#lib.symbols.webui_set_file_handler(
502514
BigInt(this.#window),
503-
cb.pointer,
515+
callbackResource.pointer,
504516
);
505517
}
506-
518+
507519
/**
508520
* Sets the profile name and path for the current window.
509521
* @param name - Profile name.
@@ -678,7 +690,9 @@ export class WebUI {
678690
private static init() {
679691
if (typeof _lib === 'undefined') {
680692
_lib = loadLib();
681-
_lib.symbols.webui_set_config(BigInt(5), true); // Enable async calls
693+
// C: .asynchronous_response = true; // 5
694+
// Enable async calls, this is needed for `.bind()`
695+
_lib.symbols.webui_set_config(BigInt(5), true);
682696
}
683697
}
684698

0 commit comments

Comments
 (0)