aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-12 05:54:34 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-12 05:54:34 -0800
commit6f39b8f57627b62c4db1a8ee69219b41389b2f96 (patch)
treee9e3f03215d6b7e23f114adb8723e11cd12ad221
parent7b9312313363e1d63caaef5ec1f98635f2eedd2d (diff)
downloadbun-6f39b8f57627b62c4db1a8ee69219b41389b2f96.tar.gz
bun-6f39b8f57627b62c4db1a8ee69219b41389b2f96.tar.zst
bun-6f39b8f57627b62c4db1a8ee69219b41389b2f96.zip
Response.clone()
-rw-r--r--integration/bunjs-only-snippets/fetch.test.js17
-rw-r--r--src/javascript/jsc/webcore/response.zig102
2 files changed, 106 insertions, 13 deletions
diff --git a/integration/bunjs-only-snippets/fetch.test.js b/integration/bunjs-only-snippets/fetch.test.js
index 72b80cd35..76e09bcf8 100644
--- a/integration/bunjs-only-snippets/fetch.test.js
+++ b/integration/bunjs-only-snippets/fetch.test.js
@@ -1,4 +1,4 @@
-import { it, describe } from "bun:test";
+import { it, describe, expect } from "bun:test";
import fs from "fs";
describe("fetch", () => {
@@ -20,3 +20,18 @@ describe("fetch", () => {
});
}
});
+
+describe("Response", () => {
+ it("clone", async () => {
+ var body = new Response("<div>hello</div>", {
+ headers: {
+ "content-type": "text/html; charset=utf-8",
+ },
+ });
+ var clone = body.clone();
+ body.headers.set("content-type", "text/plain");
+ expect(clone.headers.get("content-type")).toBe("text/html; charset=utf-8");
+ expect(body.headers.get("content-type")).toBe("text/plain");
+ expect(await clone.text()).toBe("<div>hello</div>");
+ });
+});
diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig
index a091c7cf2..0b4bb7a76 100644
--- a/src/javascript/jsc/webcore/response.zig
+++ b/src/javascript/jsc/webcore/response.zig
@@ -167,6 +167,11 @@ pub const Response = struct {
return js.JSValueMakeBoolean(ctx, this.isOK());
}
+ pub fn clone(this: *const Response, allocator: std.mem.Allocator) *Response {
+ var new_response = allocator.create(Response) catch unreachable;
+ this.cloneInto(new_response, allocator);
+ return new_response;
+ }
pub fn getText(
this: *Response,
ctx: js.JSContextRef,
@@ -406,7 +411,7 @@ pub const Response = struct {
return MimeType.html.value;
},
- .Unconsumed, .ArrayBuffer => {
+ else => {
return "application/octet-stream";
},
}
@@ -421,7 +426,7 @@ pub const Response = struct {
const body: Body = brk: {
switch (arguments.len) {
0 => {
- break :brk Body.@"404"(ctx);
+ break :brk Body.@"200"(ctx);
},
1 => {
break :brk Body.extract(ctx, arguments[0], exception);
@@ -887,6 +892,17 @@ pub const Headers = struct {
headers.entries.deinit(headers.allocator);
}
+ pub fn empty(allocator: std.mem.Allocator) Headers {
+ var headers: Headers = undefined;
+ return Headers{
+ .entries = @TypeOf(headers.entries){},
+ .buf = @TypeOf(headers.buf){},
+ .used = 0,
+ .allocator = allocator,
+ .guard = Guard.none,
+ };
+ }
+
// https://developer.mozilla.org/en-US/docs/Web/API/Headers#methods
pub const JS = struct {
@@ -930,7 +946,7 @@ pub const Headers = struct {
return js.JSValueMakeUndefined(ctx);
}
- this.putHeader(arguments[0], arguments[1], false);
+ this.putHeaderFromJS(arguments[0], arguments[1], false);
return js.JSValueMakeUndefined(ctx);
}
@@ -947,7 +963,7 @@ pub const Headers = struct {
return js.JSValueMakeUndefined(ctx);
}
- this.putHeader(arguments[0], arguments[1], true);
+ this.putHeaderFromJS(arguments[0], arguments[1], true);
return js.JSValueMakeUndefined(ctx);
}
pub fn delete(
@@ -1096,13 +1112,7 @@ pub const Headers = struct {
.guard = Guard.none,
};
} else {
- headers.* = Headers{
- .entries = @TypeOf(headers.entries){},
- .buf = @TypeOf(headers.buf){},
- .used = 0,
- .allocator = getAllocator(ctx),
- .guard = Guard.none,
- };
+ headers.* = Headers.empty(getAllocator(ctx));
}
return Headers.Class.make(ctx, headers);
@@ -1223,7 +1233,19 @@ pub const Headers = struct {
threadlocal var header_kv_buf: [4096]u8 = undefined;
- pub fn putHeader(headers: *Headers, key_: js.JSStringRef, value_: js.JSStringRef, comptime append: bool) void {
+ pub fn putHeader(headers: *Headers, key_: []const u8, value_: []const u8, comptime append: bool) void {
+ const key = strings.copyLowercase(strings.trim(key_, " \n\r"), &header_kv_buf);
+ const value = strings.copyLowercase(strings.trim(value_, " \n\r"), header_kv_buf[key.len..]);
+ return headers.putHeaderNormalized(key, value, append);
+ }
+
+ pub fn putHeaderNumber(headers: *Headers, key_: []const u8, value_: u32, comptime append: bool) void {
+ const key = strings.copyLowercase(strings.trim(key_, " \n\r"), &header_kv_buf);
+ const value = std.fmt.bufPrint(header_kv_buf[key.len..], "{d}", .{value_}) catch unreachable;
+ return headers.putHeaderNormalized(key, value, append);
+ }
+
+ pub fn putHeaderFromJS(headers: *Headers, key_: js.JSStringRef, value_: js.JSStringRef, comptime append: bool) void {
const key_len = js.JSStringGetUTF8CString(key_, &header_kv_buf, header_kv_buf.len) - 1;
// TODO: make this one pass instead of two
var key = strings.trim(header_kv_buf[0..key_len], " \n\r");
@@ -1233,6 +1255,10 @@ pub const Headers = struct {
const value_len = js.JSStringGetUTF8CString(value_, remainder.ptr, remainder.len) - 1;
var value = strings.trim(remainder[0..value_len], " \n\r");
+ headers.putHeaderNormalized(key, value, append);
+ }
+
+ pub fn putHeaderNormalized(headers: *Headers, key: []const u8, value: []const u8, comptime append: bool) void {
if (headers.getHeaderIndex(key)) |header_i| {
const existing_value = headers.entries.items(.value)[header_i];
@@ -1467,6 +1493,45 @@ pub const Body = struct {
len: usize = 0,
ptr_allocator: ?std.mem.Allocator = null,
+ pub fn slice(this: *const Body) []const u8 {
+ return switch (this.value) {
+ .String => this.value.String,
+ .ArrayBuffer => this.value.ArrayBuffer.slice(),
+ else => "",
+ };
+ }
+
+ pub fn clone(this: Body, allocator: std.mem.Allocator) Body {
+ var value: Value = .{ .Empty = 0 };
+ var ptr: ?[*]u8 = null;
+ var len: usize = 0;
+ switch (this.value) {
+ .ArrayBuffer => |buffer| {
+ value = .{
+ .ArrayBuffer = ArrayBuffer.fromBytes(allocator.dupe(u8, buffer.slice()) catch unreachable, buffer.typed_array_type),
+ };
+ len = buffer.len;
+ ptr = value.ArrayBuffer.ptr;
+ },
+ .String => |str| {
+ value = .{
+ .String = allocator.dupe(u8, str) catch unreachable,
+ };
+ len = str.len;
+ ptr = bun.constStrToU8(value.String).ptr;
+ },
+ else => {},
+ }
+
+ return Body{
+ .init = this.init.clone(allocator),
+ .value = value,
+ .ptr_allocator = if (len > 0) allocator else null,
+ .ptr = ptr,
+ .len = len,
+ };
+ }
+
pub fn writeFormat(this: *const Body, formatter: *JSC.Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void {
const Writer = @TypeOf(writer);
@@ -1508,6 +1573,19 @@ pub const Body = struct {
headers: ?Headers,
status_code: u16,
+ pub fn clone(this: Init, allocator: std.mem.Allocator) Init {
+ var that = this;
+ var headers = this.headers;
+ if (headers) |*head| {
+ headers.?.allocator = allocator;
+ var new_headers: Headers = undefined;
+ head.clone(&new_headers) catch unreachable;
+ that.headers = new_headers;
+ }
+
+ return that;
+ }
+
pub fn init(_: std.mem.Allocator, ctx: js.JSContextRef, init_ref: js.JSValueRef) !?Init {
var result = Init{ .headers = null, .status_code = 0 };
var array = js.JSObjectCopyPropertyNames(ctx, init_ref);