diff options
author | 2022-03-12 05:54:34 -0800 | |
---|---|---|
committer | 2022-03-12 05:54:34 -0800 | |
commit | 6f39b8f57627b62c4db1a8ee69219b41389b2f96 (patch) | |
tree | e9e3f03215d6b7e23f114adb8723e11cd12ad221 | |
parent | 7b9312313363e1d63caaef5ec1f98635f2eedd2d (diff) | |
download | bun-6f39b8f57627b62c4db1a8ee69219b41389b2f96.tar.gz bun-6f39b8f57627b62c4db1a8ee69219b41389b2f96.tar.zst bun-6f39b8f57627b62c4db1a8ee69219b41389b2f96.zip |
Response.clone()
-rw-r--r-- | integration/bunjs-only-snippets/fetch.test.js | 17 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 102 |
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); |