aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/api/html_rewriter.zig97
-rw-r--r--test/js/workerd/html-rewriter.test.js21
2 files changed, 84 insertions, 34 deletions
diff --git a/src/bun.js/api/html_rewriter.zig b/src/bun.js/api/html_rewriter.zig
index d09c14e42..5e14d5503 100644
--- a/src/bun.js/api/html_rewriter.zig
+++ b/src/bun.js/api/html_rewriter.zig
@@ -106,7 +106,7 @@ pub const HTMLRewriter = struct {
var selector = LOLHTML.HTMLSelector.parse(selector_slice) catch
return throwLOLHTMLError(global);
- var handler_ = ElementHandler.init(global, listener, exception);
+ var handler_ = ElementHandler.init(global, listener, exception) catch return .zero;
if (exception.* != null) {
selector.deinit();
return JSValue.fromRef(exception.*);
@@ -154,7 +154,7 @@ pub const HTMLRewriter = struct {
thisObject: JSC.C.JSObjectRef,
exception: JSC.C.ExceptionRef,
) JSValue {
- var handler_ = DocumentHandler.init(global, listener, exception);
+ var handler_ = DocumentHandler.init(global, listener, exception) catch return .zero;
if (exception.* != null) {
return JSValue.fromRef(exception.*);
}
@@ -446,10 +446,14 @@ pub const HTMLRewriter = struct {
},
};
- result.body.init.headers = original.body.init.headers;
result.body.init.method = original.body.init.method;
result.body.init.status_code = original.body.init.status_code;
+ // https://github.com/oven-sh/bun/issues/3334
+ if (original.body.init.headers) |headers| {
+ result.body.init.headers = headers.cloneThis(global);
+ }
+
result.url = bun.default_allocator.dupe(u8, original.url) catch unreachable;
result.status_text = bun.default_allocator.dupe(u8, original.status_text) catch unreachable;
@@ -723,29 +727,44 @@ const DocumentHandler = struct {
"onEndCallback",
);
- pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) DocumentHandler {
+ pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) !DocumentHandler {
var handler = DocumentHandler{
.thisObject = thisObject,
.global = global,
};
- switch (thisObject.jsType()) {
- .Object, .ProxyObject, .Cell, .FinalObject => {},
- else => |kind| {
- JSC.throwInvalidArguments(
- "Expected object but received {s}",
- .{@as(string, @tagName(kind))},
- global,
- exception,
- );
- return undefined;
- },
+ if (!thisObject.isObject()) {
+ JSC.throwInvalidArguments(
+ "Expected object",
+ .{},
+ global,
+ exception,
+ );
+ return error.InvalidArguments;
+ }
+
+ errdefer {
+ if (handler.onDocTypeCallback) |cb| {
+ cb.unprotect();
+ }
+
+ if (handler.onCommentCallback) |cb| {
+ cb.unprotect();
+ }
+
+ if (handler.onTextCallback) |cb| {
+ cb.unprotect();
+ }
+
+ if (handler.onEndCallback) |cb| {
+ cb.unprotect();
+ }
}
if (thisObject.get(global, "doctype")) |val| {
if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
JSC.throwInvalidArguments("doctype must be a function", .{}, global, exception);
- return undefined;
+ return error.InvalidArguments;
}
JSC.C.JSValueProtect(global, val.asObjectRef());
handler.onDocTypeCallback = val;
@@ -754,7 +773,7 @@ const DocumentHandler = struct {
if (thisObject.get(global, "comments")) |val| {
if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
JSC.throwInvalidArguments("comments must be a function", .{}, global, exception);
- return undefined;
+ return error.InvalidArguments;
}
JSC.C.JSValueProtect(global, val.asObjectRef());
handler.onCommentCallback = val;
@@ -763,7 +782,7 @@ const DocumentHandler = struct {
if (thisObject.get(global, "text")) |val| {
if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
JSC.throwInvalidArguments("text must be a function", .{}, global, exception);
- return undefined;
+ return error.InvalidArguments;
}
JSC.C.JSValueProtect(global, val.asObjectRef());
handler.onTextCallback = val;
@@ -772,7 +791,7 @@ const DocumentHandler = struct {
if (thisObject.get(global, "end")) |val| {
if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
JSC.throwInvalidArguments("end must be a function", .{}, global, exception);
- return undefined;
+ return error.InvalidArguments;
}
JSC.C.JSValueProtect(global, val.asObjectRef());
handler.onEndCallback = val;
@@ -863,29 +882,39 @@ const ElementHandler = struct {
global: *JSGlobalObject,
ctx: ?*HTMLRewriter.BufferOutputSink = null,
- pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) ElementHandler {
+ pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) !ElementHandler {
var handler = ElementHandler{
.thisObject = thisObject,
.global = global,
};
+ errdefer {
+ if (handler.onCommentCallback) |cb| {
+ cb.unprotect();
+ }
- switch (thisObject.jsType()) {
- .Object, .ProxyObject, .Cell, .FinalObject => {},
- else => |kind| {
- JSC.throwInvalidArguments(
- "Expected object but received {s}",
- .{@as(string, @tagName(kind))},
- global,
- exception,
- );
- return undefined;
- },
+ if (handler.onElementCallback) |cb| {
+ cb.unprotect();
+ }
+
+ if (handler.onTextCallback) |cb| {
+ cb.unprotect();
+ }
+ }
+
+ if (!thisObject.isObject()) {
+ JSC.throwInvalidArguments(
+ "Expected object",
+ .{},
+ global,
+ exception,
+ );
+ return error.InvalidArguments;
}
if (thisObject.get(global, "element")) |val| {
if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
JSC.throwInvalidArguments("element must be a function", .{}, global, exception);
- return undefined;
+ return error.InvalidArguments;
}
JSC.C.JSValueProtect(global, val.asObjectRef());
handler.onElementCallback = val;
@@ -894,7 +923,7 @@ const ElementHandler = struct {
if (thisObject.get(global, "comments")) |val| {
if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
JSC.throwInvalidArguments("comments must be a function", .{}, global, exception);
- return undefined;
+ return error.InvalidArguments;
}
JSC.C.JSValueProtect(global, val.asObjectRef());
handler.onCommentCallback = val;
@@ -903,7 +932,7 @@ const ElementHandler = struct {
if (thisObject.get(global, "text")) |val| {
if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
JSC.throwInvalidArguments("text must be a function", .{}, global, exception);
- return undefined;
+ return error.InvalidArguments;
}
JSC.C.JSValueProtect(global, val.asObjectRef());
handler.onTextCallback = val;
diff --git a/test/js/workerd/html-rewriter.test.js b/test/js/workerd/html-rewriter.test.js
index b6131a09f..3f7b7493d 100644
--- a/test/js/workerd/html-rewriter.test.js
+++ b/test/js/workerd/html-rewriter.test.js
@@ -316,3 +316,24 @@ describe("HTMLRewriter", () => {
expect(lastInTextNode).toBeBoolean();
});
});
+
+// By not segfaulting, this test passes
+it("#3334 regression", async () => {
+ for (let i = 0; i < 10; i++) {
+ const headers = new Headers({
+ "content-type": "text/html",
+ });
+ const response = new Response("<div>content</div>", { headers });
+
+ const result = await new HTMLRewriter()
+ .on("div", {
+ element(elem) {
+ elem.setInnerContent("new");
+ },
+ })
+ .transform(response)
+ .text();
+ expect(result).toEqual("<div>new</div>");
+ }
+ Bun.gc(true);
+});