Skip to content

Commit

Permalink
Make Blob.prototype. type more spec compliant
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarred-Sumner committed Mar 8, 2023
1 parent 9bf4d9b commit e838f8b
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 33 deletions.
4 changes: 3 additions & 1 deletion src/bun.js/api/server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,9 @@ pub const ServerConfig = struct {
}

if (arg.getTruthy(global, "maxRequestBodySize")) |max_request_body_size| {
args.max_request_body_size = @intCast(u64, @max(0, max_request_body_size.toInt64()));
if (max_request_body_size.isNumber()) {
args.max_request_body_size = @intCast(u64, @max(0, max_request_body_size.toInt64()));
}
}

if (arg.getTruthy(global, "error")) |onError| {
Expand Down
4 changes: 4 additions & 0 deletions src/bun.js/javascript.zig
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,10 @@ pub const VirtualMachine = struct {
return VMHolder.vm.?;
}

pub fn mimeType(this: *VirtualMachine, str: []const u8) ?bun.HTTP.MimeType {
return this.rareData().mimeTypeFromString(this.allocator, str);
}

pub const GCLevel = enum(u3) {
none = 0,
mild = 1,
Expand Down
12 changes: 12 additions & 0 deletions src/bun.js/rare_data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ file_polls_: ?*JSC.FilePoll.HiveArray = null,

global_dns_data: ?*JSC.DNS.GlobalData = null,

mime_types: ?bun.HTTP.MimeType.Map = null,

pub fn mimeTypeFromString(this: *RareData, allocator: std.mem.Allocator, str: []const u8) ?bun.HTTP.MimeType {
if (this.mime_types == null) {
this.mime_types = bun.HTTP.MimeType.createHashTable(
allocator,
) catch @panic("Out of memory");
}

return this.mime_types.?.get(str);
}

pub fn filePolls(this: *RareData, vm: *JSC.VirtualMachine) *JSC.FilePoll.HiveArray {
return this.file_polls_ orelse {
this.file_polls_ = vm.allocator.create(JSC.FilePoll.HiveArray) catch unreachable;
Expand Down
106 changes: 74 additions & 32 deletions src/bun.js/webcore/blob.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2391,7 +2391,7 @@ pub const Blob = struct {
callframe: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
var allocator = globalThis.allocator();
var arguments_ = callframe.arguments(2);
var arguments_ = callframe.arguments(3);
var args = arguments_.ptr[0..arguments_.len];

if (this.size == 0) {
Expand All @@ -2410,51 +2410,84 @@ pub const Blob = struct {
// If the optional end parameter is not used as a parameter when making this call, let relativeEnd be size.
var relativeEnd: i64 = @intCast(i64, this.size);

if (args.ptr[0].isString()) {
args.ptr[2] = args.ptr[0];
args.ptr[1] = .zero;
args.ptr[0] = .zero;
args.len = 3;
} else if (args.ptr[1].isString()) {
args.ptr[2] = args.ptr[1];
args.ptr[1] = .zero;
args.len = 3;
}

var args_iter = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), args);
if (args_iter.nextEat()) |start_| {
const start = start_.toInt64();
if (start < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeStart = @intCast(i64, @max(start + @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeStart = @min(@intCast(i64, start), @intCast(i64, this.size));
if (start_.isNumber()) {
const start = start_.toInt64();
if (start < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeStart = @intCast(i64, @max(start +% @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeStart = @min(@intCast(i64, start), @intCast(i64, this.size));
}
}
}

if (args_iter.nextEat()) |end_| {
const end = end_.toInt64();
// If end is negative, let relativeEnd be max((size + end), 0).
if (end < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeEnd = @intCast(i64, @max(end + @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeEnd = @min(@intCast(i64, end), @intCast(i64, this.size));
if (end_.isNumber()) {
const end = end_.toInt64();
// If end is negative, let relativeEnd be max((size + end), 0).
if (end < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeEnd = @intCast(i64, @max(end +% @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeEnd = @min(@intCast(i64, end), @intCast(i64, this.size));
}
}
}

var content_type: string = "";
var content_type_was_allocated = false;
if (args_iter.nextEat()) |content_type_| {
if (content_type_.isString()) {
var zig_str = content_type_.getZigString(globalThis);
var slicer = zig_str.toSlice(bun.default_allocator);
defer slicer.deinit();
var slice = slicer.slice();
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
content_type = strings.copyLowercase(slice, content_type_buf);
inner: {
if (content_type_.isString()) {
var zig_str = content_type_.getZigString(globalThis);
var slicer = zig_str.toSlice(bun.default_allocator);
defer slicer.deinit();
var slice = slicer.slice();
if (!strings.isAllASCII(slice)) {
break :inner;
}

if (globalThis.bunVM().mimeType(slice)) |mime| {
content_type = mime.value;
break :inner;
}

content_type_was_allocated = slice.len > 0;
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
content_type = strings.copyLowercase(slice, content_type_buf);
}
}
}

const len = @intCast(SizeType, @max(relativeEnd - relativeStart, 0));
const len = @intCast(SizeType, @max(relativeEnd -| relativeStart, 0));

// This copies over the is_all_ascii flag
// which is okay because this will only be a <= slice
var blob = this.dupe();
blob.offset = @intCast(SizeType, relativeStart);
blob.size = len;

// infer the content type if it was not specified
if (content_type.len == 0 and this.content_type.len > 0 and !this.content_type_allocated)
content_type = this.content_type;

blob.content_type = content_type;
blob.content_type_allocated = content_type.len > 0;
blob.content_type_allocated = content_type_was_allocated;

var blob_ = allocator.create(Blob) catch unreachable;
blob_.* = blob;
Expand Down Expand Up @@ -2602,13 +2635,22 @@ pub const Blob = struct {
// Normative conditions for this member are provided
// in the § 3.1 Constructors.
if (options.get(globalThis, "type")) |content_type| {
if (content_type.isString()) {
var content_type_str = content_type.toSlice(globalThis, bun.default_allocator);
defer content_type_str.deinit();
var slice = content_type_str.slice();
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
blob.content_type = strings.copyLowercase(slice, content_type_buf);
blob.content_type_allocated = true;
inner: {
if (content_type.isString()) {
var content_type_str = content_type.toSlice(globalThis, bun.default_allocator);
defer content_type_str.deinit();
var slice = content_type_str.slice();
if (!strings.isAllASCII(slice)) {
break :inner;
}
if (globalThis.bunVM().mimeType(slice)) |mime| {
blob.content_type = mime.value;
break :inner;
}
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
blob.content_type = strings.copyLowercase(slice, content_type_buf);
blob.content_type_allocated = true;
}
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/http/mime_type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@ const MimeType = @This();
value: string,
category: Category,

pub const Map = bun.StringHashMap(MimeType);

pub fn createHashTable(allocator: std.mem.Allocator) !Map {
@setCold(true);

const decls = comptime std.meta.declarations(all);

var map = Map.init(allocator);
try map.ensureTotalCapacity(@truncate(u32, decls.len));
@setEvalBranchQuota(4000);
inline for (decls) |decl| {
map.putAssumeCapacityNoClobber(decl.name, @field(all, decl.name));
}

return map;
}

pub fn canOpenInEditor(this: MimeType) bool {
if (this.category == .text or this.category.isCode())
return true;
Expand Down
25 changes: 25 additions & 0 deletions test/js/web/fetch/blob.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { test, expect } from "bun:test";

test("Blob.slice", () => {
const blob = new Blob(["Bun", "Foo"]);
const b1 = blob.slice(0, 3, "Text/HTML");
expect(b1 instanceof Blob).toBeTruthy();
expect(b1.size).toBe(3);
expect(b1.type).toBe("text/html");
const b2 = blob.slice(-1, 3);
expect(b2.size).toBe(0);
const b3 = blob.slice(100, 3);
expect(b3.size).toBe(0);
const b4 = blob.slice(0, 10);
expect(b4.size).toBe(blob.size);
});

test("new Blob", () => {
var blob = new Blob(["Bun", "Foo"], { type: "text/foo" });
expect(blob.size).toBe(6);
expect(blob.type).toBe("text/foo");

blob = new Blob(["Bun", "Foo"], { type: "\u1234" });
expect(blob.size).toBe(6);
expect(blob.type).toBe("");
});

0 comments on commit e838f8b

Please sign in to comment.