aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/api
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-06-17 04:26:44 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-06-22 06:56:47 -0700
commit38cc869104584987d0a7f3b21be4da196bb3f390 (patch)
tree6523c2d1d90a80f704e955af335a44d8e89b80f9 /src/javascript/jsc/api
parent5d8a99e1d4009cb4b9c7766b2af76c3df8364670 (diff)
downloadbun-38cc869104584987d0a7f3b21be4da196bb3f390.tar.gz
bun-38cc869104584987d0a7f3b21be4da196bb3f390.tar.zst
bun-38cc869104584987d0a7f3b21be4da196bb3f390.zip
WIP WebSocket
Diffstat (limited to 'src/javascript/jsc/api')
-rw-r--r--src/javascript/jsc/api/html_rewriter.zig159
1 files changed, 158 insertions, 1 deletions
diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig
index 7605cfef7..302af6aac 100644
--- a/src/javascript/jsc/api/html_rewriter.zig
+++ b/src/javascript/jsc/api/html_rewriter.zig
@@ -32,7 +32,7 @@ const Response = WebCore.Response;
const LOLHTML = @import("lolhtml");
const SelectorMap = std.ArrayListUnmanaged(*LOLHTML.HTMLSelector);
-const LOLHTMLContext = struct {
+pub const LOLHTMLContext = struct {
selectors: SelectorMap = .{},
element_handlers: std.ArrayListUnmanaged(*ElementHandler) = .{},
document_handlers: std.ArrayListUnmanaged(*DocumentHandler) = .{},
@@ -232,6 +232,163 @@ pub const HTMLRewriter = struct {
return this.beginTransform(global, response);
}
+ pub const HTMLRewriterLoader = struct {
+ rewriter: *LOLHTML.HTMLRewriter,
+ finalized: bool = false,
+ context: LOLHTMLContext,
+ chunk_size: usize = 0,
+ failed: bool = false,
+ output: JSC.WebCore.Sink,
+ signal: JSC.WebCore.Signal = .{},
+ backpressure: std.fifo.LinearFifo(u8, .Dynamic) = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator),
+
+ pub fn finalize(this: *HTMLRewriterLoader) void {
+ if (this.finalized) return;
+ this.rewriter.deinit();
+ this.backpressure.deinit();
+ this.backpressure = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator);
+ this.finalized = true;
+ }
+
+ pub fn fail(this: *HTMLRewriterLoader, err: JSC.Node.Syscall.Error) void {
+ this.signal.close(err);
+ this.output.end(err);
+ this.failed = true;
+ this.finalize();
+ }
+
+ pub fn connect(this: *HTMLRewriterLoader, signal: JSC.WebCore.Signal) void {
+ this.signal = signal;
+ }
+
+ pub fn writeToDestination(this: *HTMLRewriterLoader, bytes: []const u8) void {
+ if (this.backpressure.count > 0) {
+ this.backpressure.write(bytes) catch {
+ this.fail(JSC.Node.Syscall.Error.oom);
+ this.finalize();
+ };
+ return;
+ }
+
+ const write_result = this.output.write(.{ .temporary = bun.ByteList.init(bytes) });
+
+ switch (write_result) {
+ .err => |err| {
+ this.fail(err);
+ },
+ .owned_and_done, .temporary_and_done, .into_array_and_done => {
+ this.done();
+ },
+ .pending => |pending| {
+ pending.applyBackpressure(bun.default_allocator, &this.output, pending, bytes);
+ },
+ .into_array, .owned, .temporary => {
+ this.signal.ready(if (this.chunk_size > 0) this.chunk_size else null, null);
+ },
+ }
+ }
+
+ pub fn done(
+ this: *HTMLRewriterLoader,
+ ) void {
+ this.output.end(null);
+ this.signal.close(null);
+ this.finalize();
+ }
+
+ pub fn setup(
+ this: *HTMLRewriterLoader,
+ builder: *LOLHTML.HTMLRewriter.Builder,
+ context: LOLHTMLContext,
+ size_hint: ?usize,
+ output: JSC.WebCore.Sink,
+ ) ?[]const u8 {
+ for (context.document_handlers.items) |doc| {
+ doc.ctx = this;
+ }
+ for (context.element_handlers.items) |doc| {
+ doc.ctx = this;
+ }
+
+ const chunk_size = @maximum(size_hint orelse 16384, 1024);
+ this.rewriter = builder.build(
+ .UTF8,
+ .{
+ .preallocated_parsing_buffer_size = chunk_size,
+ .max_allowed_memory_usage = std.math.maxInt(u32),
+ },
+ false,
+ HTMLRewriterLoader,
+ this,
+ HTMLRewriterLoader.writeToDestination,
+ HTMLRewriterLoader.done,
+ ) catch {
+ output.end();
+ return LOLHTML.HTMLString.lastError().slice();
+ };
+
+ this.chunk_size = chunk_size;
+ this.context = context;
+ this.output = output;
+
+ return null;
+ }
+
+ pub fn sink(this: *HTMLRewriterLoader) JSC.WebCore.Sink {
+ return JSC.WebCore.Sink.init(this);
+ }
+
+ fn writeBytes(this: *HTMLRewriterLoader, bytes: bun.ByteList, comptime deinit_: bool) ?JSC.Node.Syscall.Error {
+ this.rewriter.write(bytes.slice()) catch {
+ return JSC.Node.Syscall.Error{
+ .errno = 1,
+ // TODO: make this a union
+ .path = bun.default_allocator.dupe(u8, LOLHTML.HTMLString.lastError().slice()) catch unreachable,
+ };
+ };
+ if (comptime deinit_) bytes.listManaged(bun.default_allocator).deinit();
+ return null;
+ }
+
+ pub fn write(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
+ switch (data) {
+ .owned => |bytes| {
+ if (this.writeBytes(bytes, true)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .owned = bytes.len };
+ },
+ .owned_and_done => |bytes| {
+ if (this.writeBytes(bytes, true)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .owned_and_done = bytes.len };
+ },
+ .temporary_and_done => |bytes| {
+ if (this.writeBytes(bytes, false)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .temporary_and_done = bytes.len };
+ },
+ .temporary => |bytes| {
+ if (this.writeBytes(bytes, false)) |err| {
+ return .{ .err = err };
+ }
+ return .{ .temporary = bytes.len };
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn writeUTF16(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
+ return JSC.WebCore.Sink.UTF8Fallback.writeUTF16(HTMLRewriterLoader, this, data, write);
+ }
+
+ pub fn writeLatin1(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
+ return JSC.WebCore.Sink.UTF8Fallback.writeLatin1(HTMLRewriterLoader, this, data, write);
+ }
+ };
+
pub const BufferOutputSink = struct {
global: *JSGlobalObject,
bytes: bun.MutableString,