mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 19:08:50 +00:00
[bun.js] 2/? Implement Response.file, sendfile edition
This commit is contained in:
@@ -304,8 +304,8 @@ pub const HTMLRewriter = struct {
|
||||
defer if (!is_pending) input.detach();
|
||||
|
||||
if (input.needsToReadFile()) {
|
||||
input.doReadFileInternal(*BufferOutputSink, sink, onFinishedLoading, global);
|
||||
} else if (sink.runOutputSink(input.sharedView())) |error_value| {
|
||||
input.doReadFileInternal(*BufferOutputSink, sink, onFinishedLoadingWrap, global);
|
||||
} else if (sink.runOutputSink(input.sharedView(), false)) |error_value| {
|
||||
return error_value;
|
||||
}
|
||||
|
||||
@@ -316,11 +316,15 @@ pub const HTMLRewriter = struct {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn onFinishedLoadingWrap(sink: *anyopaque, bytes: anyerror![]u8) void {
|
||||
onFinishedLoading(bun.cast(*BufferOutputSink, sink), bytes);
|
||||
}
|
||||
|
||||
pub fn onFinishedLoading(sink: *BufferOutputSink, bytes: anyerror![]u8) void {
|
||||
var input = sink.input;
|
||||
defer input.detach();
|
||||
const data = bytes catch |err| {
|
||||
if (sink.response.body.value == .Locked and sink.response.body.value.Locked.task == sink) {
|
||||
if (sink.response.body.value == .Locked and @ptrToInt(sink.response.body.value.Locked.task) == @ptrToInt(sink)) {
|
||||
sink.response.body.value = .{ .Empty = .{} };
|
||||
}
|
||||
|
||||
@@ -334,7 +338,7 @@ pub const HTMLRewriter = struct {
|
||||
}
|
||||
|
||||
pub fn runOutputSink(sink: *BufferOutputSink, bytes: []const u8, is_async: bool) ?JSValue {
|
||||
sink.bytes.growBy(bytes) catch unreachable;
|
||||
sink.bytes.growBy(bytes.len) catch unreachable;
|
||||
var global = sink.global;
|
||||
var response = sink.response;
|
||||
sink.rewriter.write(bytes) catch {
|
||||
|
||||
@@ -79,6 +79,12 @@ const IOTask = JSC.IOTask;
|
||||
const is_bindgen = JSC.is_bindgen;
|
||||
const uws = @import("uws");
|
||||
|
||||
const SendfileContext = struct {
|
||||
fd: i32,
|
||||
remain: u32 = 0,
|
||||
offset: i64 = 0,
|
||||
};
|
||||
|
||||
pub fn NewServer(comptime ssl_enabled: bool) type {
|
||||
return struct {
|
||||
const ThisServer = @This();
|
||||
@@ -133,9 +139,17 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
|
||||
blob: JSC.WebCore.Blob = JSC.WebCore.Blob{},
|
||||
promise: ?*JSC.JSValue = null,
|
||||
response_headers: ?*JSC.WebCore.Headers.RefCountedHeaders = null,
|
||||
|
||||
has_abort_handler: bool = false,
|
||||
has_sendfile_ctx: bool = false,
|
||||
sendfile: SendfileContext = undefined,
|
||||
pub threadlocal var pool: *RequestContextStackAllocator = undefined;
|
||||
|
||||
pub fn setAbortHandler(this: *RequestContext) void {
|
||||
if (this.has_abort_handler) return;
|
||||
this.has_abort_handler = true;
|
||||
this.resp.onAborted(*RequestContext, RequestContext.onAbort, this);
|
||||
}
|
||||
|
||||
pub fn onResolve(
|
||||
ctx: *RequestContext,
|
||||
_: *JSC.JSGlobalObject,
|
||||
@@ -215,30 +229,206 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
|
||||
this.server.request_pool_allocator.destroy(this);
|
||||
}
|
||||
|
||||
fn writeHeaders(
|
||||
this: *RequestContext,
|
||||
headers_: *Headers.RefCountedHeaders,
|
||||
) void {
|
||||
var headers: *JSC.WebCore.Headers = headers_.get();
|
||||
if (headers.getHeaderIndex("content-length")) |index| {
|
||||
headers.entries.orderedRemove(index);
|
||||
}
|
||||
defer headers_.deref();
|
||||
var entries = headers.entries.slice();
|
||||
const names = entries.items(.name);
|
||||
const values = entries.items(.value);
|
||||
|
||||
this.resp.writeHeaderInt("content-length", this.blob.size);
|
||||
this.resp.writeHeaders(names, values, headers.buf.items);
|
||||
}
|
||||
|
||||
pub fn writeStatus(this: *RequestContext, status: u16) void {
|
||||
var status_text_buf: [48]u8 = undefined;
|
||||
|
||||
if (status == 302) {
|
||||
this.resp.writeStatus("302 Found");
|
||||
} else {
|
||||
this.resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{status}) catch unreachable);
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanupAfterSendfile(this: *RequestContext) void {
|
||||
this.resp.endWithoutBody();
|
||||
std.os.close(this.sendfile.fd);
|
||||
this.sendfile = undefined;
|
||||
this.finalize();
|
||||
}
|
||||
|
||||
pub fn onSendfile(this: *RequestContext, amount_: c_ulong, response: *App.Response) callconv(.C) bool {
|
||||
const amount = @minimum(@truncate(u32, amount_), this.sendfile.remain);
|
||||
|
||||
if (amount == 0 or this.aborted) {
|
||||
this.cleanupAfterSendfile();
|
||||
return true;
|
||||
}
|
||||
|
||||
const adjusted_count_temporary = @minimum(amount, @as(u63, std.math.maxInt(i32)));
|
||||
// TODO we should not need this int cast; improve the return type of `@minimum`
|
||||
const adjusted_count = @intCast(u63, adjusted_count_temporary);
|
||||
var sbytes: std.os.off_t = adjusted_count;
|
||||
const signed_offset = @bitCast(i64, this.sendfile.offset);
|
||||
|
||||
if (Environment.isLinux) {
|
||||
const sent = @truncate(
|
||||
u32,
|
||||
std.os.linux.sendfile(response.getNativeHandle(), this.sendfile.fd, &this.sendfile.offset, amount),
|
||||
);
|
||||
|
||||
this.sendfile.offset += sent;
|
||||
this.sendfile.remain -= sent;
|
||||
|
||||
if (sent == 0 or this.aborted or this.sendfile.remain == 0) {
|
||||
this.cleanupAfterSendfile();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const errcode = std.c.getErrno(std.c.sendfile(
|
||||
this.sendfile.fd,
|
||||
response.getNativeHandle(),
|
||||
|
||||
signed_offset,
|
||||
&sbytes,
|
||||
null,
|
||||
0,
|
||||
));
|
||||
this.sendfile.offset += sbytes;
|
||||
this.sendfile.remain -= if (errcode != .SUCCESS) @intCast(u32, sbytes) else 0;
|
||||
if ((errcode != .AGAIN and errcode != .SUCCESS) or this.aborted or this.sendfile.remain == 0) {
|
||||
if (errcode != .AGAIN and errcode != .SUCCESS) {
|
||||
Output.prettyErrorln("Error: {s}", .{@tagName(errcode)});
|
||||
Output.flush();
|
||||
}
|
||||
this.cleanupAfterSendfile();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.resp.onWritable(*RequestContext, onSendfile, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn onWritablePrepareSendfile(this: *RequestContext, _: c_ulong, _: *App.Response) callconv(.C) bool {
|
||||
this.renderSendFile(this.blob);
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn onPrepareSendfileWrap(this: *anyopaque, fd: i32, size: anyerror!u32, _: *JSGlobalObject) void {
|
||||
onPrepareSendfile(bun.cast(*RequestContext, this), fd, size);
|
||||
}
|
||||
|
||||
fn onPrepareSendfile(this: *RequestContext, fd: i32, size: anyerror!u32) void {
|
||||
this.setAbortHandler();
|
||||
if (this.aborted) return;
|
||||
const size_ = size catch {
|
||||
this.req.setYield(true);
|
||||
this.finalize();
|
||||
return;
|
||||
};
|
||||
this.blob.size = size_;
|
||||
const code = this.response_ptr.?.statusCode();
|
||||
if (size_ == 0 and code >= 200 and code < 300) {
|
||||
this.writeStatus(204);
|
||||
} else {
|
||||
this.writeStatus(code);
|
||||
}
|
||||
|
||||
if (this.response_ptr.?.body.init.headers) |headers_| {
|
||||
this.writeHeaders(headers_);
|
||||
} else {
|
||||
this.resp.writeHeaderInt("content-length", size_);
|
||||
}
|
||||
|
||||
this.sendfile = .{
|
||||
.fd = fd,
|
||||
.remain = size_,
|
||||
};
|
||||
|
||||
if (size_ == 0) {
|
||||
this.cleanupAfterSendfile();
|
||||
this.finalize();
|
||||
|
||||
return;
|
||||
}
|
||||
{
|
||||
const wrote = std.os.write(
|
||||
this.resp.getNativeHandle(),
|
||||
"\r\n",
|
||||
) catch {
|
||||
this.cleanupAfterSendfile();
|
||||
return;
|
||||
};
|
||||
if (wrote == 0) {
|
||||
this.cleanupAfterSendfile();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're not immediately writable, go ahead and try
|
||||
if (this.sendfile.remain == size_) {
|
||||
_ = this.onSendfile(size_, this.resp);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn renderSendFile(this: *RequestContext, blob: JSC.WebCore.Blob) void {
|
||||
if (this.has_sendfile_ctx) return;
|
||||
this.has_sendfile_ctx = true;
|
||||
|
||||
JSC.WebCore.Blob.doOpenAndStatFile(
|
||||
&this.blob,
|
||||
*RequestContext,
|
||||
this,
|
||||
onPrepareSendfileWrap,
|
||||
blob.globalThis,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn doRender(this: *RequestContext) void {
|
||||
if (this.aborted) {
|
||||
return;
|
||||
}
|
||||
var response = this.response_ptr.?;
|
||||
this.blob = response.body.use();
|
||||
var body = &response.body;
|
||||
|
||||
if (body.value == .Error) {
|
||||
this.resp.writeStatus("500 Internal Server Error");
|
||||
this.resp.writeHeader("content-type", "text/plain");
|
||||
this.resp.endWithoutBody();
|
||||
JSC.VirtualMachine.vm.defaultErrorHandler(body.value.Error, null);
|
||||
body.value = JSC.WebCore.Body.Value.empty;
|
||||
this.finalize();
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.value == .Blob) {
|
||||
if (body.value.Blob.needsToReadFile()) {
|
||||
this.blob = response.body.use();
|
||||
this.req.setYield(false);
|
||||
this.setAbortHandler();
|
||||
this.resp.onWritable(*RequestContext, onWritablePrepareSendfile, this);
|
||||
if (!this.has_sendfile_ctx) this.renderSendFile(this.blob);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.renderBytes(response);
|
||||
}
|
||||
|
||||
pub fn renderBytes(this: *RequestContext, response: *JSC.WebCore.Response) void {
|
||||
const status = response.statusCode();
|
||||
|
||||
this.writeStatus(status);
|
||||
|
||||
if (response.body.init.headers) |headers_| {
|
||||
var headers: *JSC.WebCore.Headers = headers_.get();
|
||||
defer headers_.deref();
|
||||
var entries = headers.entries.slice();
|
||||
const names = entries.items(.name);
|
||||
const values = entries.items(.value);
|
||||
|
||||
var status_text_buf: [48]u8 = undefined;
|
||||
|
||||
if (status == 302) {
|
||||
this.resp.writeStatus("302 Found");
|
||||
} else {
|
||||
this.resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{response.body.init.status_code}) catch unreachable);
|
||||
}
|
||||
|
||||
this.resp.writeHeaders(names, values, headers.buf.items);
|
||||
this.writeHeaders(headers_);
|
||||
}
|
||||
|
||||
if (status == 302 or status == 202 or this.blob.size == 0) {
|
||||
@@ -253,8 +443,8 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
|
||||
|
||||
pub fn render(this: *RequestContext, response: *JSC.WebCore.Response) void {
|
||||
this.response_ptr = response;
|
||||
this.resp.runCorked(*RequestContext, doRender, this);
|
||||
this.response_ptr = null;
|
||||
// this.resp.runCorked(*RequestContext, doRender, this);
|
||||
this.doRender();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -292,7 +482,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
|
||||
}
|
||||
|
||||
if (ctx.response_jsvalue.jsTypeLoose() == .JSPromise) {
|
||||
resp.onAborted(*RequestContext, RequestContext.onAbort, ctx);
|
||||
ctx.setAbortHandler();
|
||||
JSC.VirtualMachine.vm.tick();
|
||||
|
||||
ctx.response_jsvalue.then(
|
||||
|
||||
@@ -298,6 +298,7 @@ pub fn IOTask(comptime Context: type) type {
|
||||
const AsyncTransformTask = @import("./api/transpiler.zig").TransformTask.AsyncTransformTask;
|
||||
const BunTimerTimeoutTask = Bun.Timer.Timeout.TimeoutTask;
|
||||
const ReadFileTask = WebCore.Blob.Store.ReadFile.ReadFileTask;
|
||||
const OpenAndStatFileTask = WebCore.Blob.Store.OpenAndStatFile.OpenAndStatFileTask;
|
||||
// const PromiseTask = JSInternalPromise.Completion.PromiseTask;
|
||||
pub const Task = TaggedPointerUnion(.{
|
||||
FetchTasklet,
|
||||
@@ -305,6 +306,7 @@ pub const Task = TaggedPointerUnion(.{
|
||||
AsyncTransformTask,
|
||||
BunTimerTimeoutTask,
|
||||
ReadFileTask,
|
||||
OpenAndStatFileTask,
|
||||
// PromiseTask,
|
||||
// TimeoutTasklet,
|
||||
});
|
||||
@@ -533,6 +535,11 @@ pub const VirtualMachine = struct {
|
||||
transform_task.*.runFromJS();
|
||||
finished += 1;
|
||||
},
|
||||
@field(Task.Tag, @typeName(OpenAndStatFileTask)) => {
|
||||
var transform_task: *OpenAndStatFileTask = task.get(OpenAndStatFileTask).?;
|
||||
transform_task.*.runFromJS();
|
||||
finished += 1;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ pub const Response = struct {
|
||||
|
||||
return default.value;
|
||||
},
|
||||
.Used, .Locked, .Empty => return default.value,
|
||||
.Used, .Locked, .Empty, .Error => return default.value,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1806,6 +1806,211 @@ pub const Blob = struct {
|
||||
return try Blob.Store.init(list.items, allocator);
|
||||
}
|
||||
|
||||
const AsyncIO = HTTPClient.NetworkThread.AsyncIO;
|
||||
|
||||
pub fn FileOpenerMixin(comptime This: type) type {
|
||||
return struct {
|
||||
const open_flags = std.os.O.RDONLY | std.os.O.NONBLOCK | std.os.O.CLOEXEC;
|
||||
|
||||
pub fn getFdMac(this: *This) AsyncIO.OpenError!JSC.Node.FileDescriptor {
|
||||
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
this.opened_fd = AsyncIO.openSync(
|
||||
this.file_store.pathlike.path.sliceZ(&buf),
|
||||
open_flags,
|
||||
) catch |err| {
|
||||
this.errno = err;
|
||||
return err;
|
||||
};
|
||||
return this.opened_fd;
|
||||
}
|
||||
|
||||
pub fn getFd(this: *This) AsyncIO.OpenError!JSC.Node.FileDescriptor {
|
||||
if (this.opened_fd != 0) {
|
||||
return this.opened_fd;
|
||||
}
|
||||
|
||||
if (comptime Environment.isMac) {
|
||||
return try this.getFdMac();
|
||||
} else {
|
||||
return try this.getFdLinux();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getFdLinux(this: *This) AsyncIO.OpenError!JSC.Node.FileDescriptor {
|
||||
var aio = &AsyncIO.global;
|
||||
|
||||
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
aio.open(
|
||||
*This,
|
||||
this,
|
||||
onOpen,
|
||||
&this.open_completion,
|
||||
this.file_store.pathlike.path.sliceZ(&buf),
|
||||
open_flags,
|
||||
0,
|
||||
);
|
||||
|
||||
suspend {
|
||||
this.open_frame = @frame().*;
|
||||
}
|
||||
|
||||
if (this.errno) |errno| {
|
||||
return @errSetCast(AsyncIO.OpenError, errno);
|
||||
}
|
||||
|
||||
return this.opened_fd;
|
||||
}
|
||||
|
||||
pub fn onOpen(this: *This, _: *HTTPClient.NetworkThread.Completion, result: AsyncIO.OpenError!JSC.Node.FileDescriptor) void {
|
||||
this.opened_fd = result catch |err| {
|
||||
this.errno = err;
|
||||
if (comptime Environment.isLinux) resume this.open_frame;
|
||||
return;
|
||||
};
|
||||
|
||||
if (comptime Environment.isLinux) resume this.open_frame;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn FileCloserMixin(comptime This: type) type {
|
||||
return struct {
|
||||
pub fn doClose(this: *This) AsyncIO.CloseError!void {
|
||||
var aio = &AsyncIO.global;
|
||||
|
||||
aio.close(
|
||||
*This,
|
||||
this,
|
||||
onClose,
|
||||
&this.close_completion,
|
||||
this.opened_fd,
|
||||
);
|
||||
this.opened_fd = 0;
|
||||
|
||||
suspend {
|
||||
this.close_frame = @frame().*;
|
||||
}
|
||||
if (@hasField(This, "errno")) {
|
||||
if (this.errno) |errno| {
|
||||
return @errSetCast(AsyncIO.CloseError, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onClose(this: *This, _: *HTTPClient.NetworkThread.Completion, result: AsyncIO.CloseError!void) void {
|
||||
result catch |err| {
|
||||
if (@hasField(This, "errno")) {
|
||||
this.errno = err;
|
||||
}
|
||||
resume this.close_frame;
|
||||
return;
|
||||
};
|
||||
|
||||
resume this.close_frame;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const OpenAndStatFile = struct {
|
||||
const OpenFrameType = if (Environment.isMac)
|
||||
void
|
||||
else
|
||||
@Frame(OpenAndStatFile.getFdLinux);
|
||||
|
||||
open_frame: OpenFrameType = undefined,
|
||||
errno: ?anyerror = null,
|
||||
open_completion: HTTPClient.NetworkThread.Completion = undefined,
|
||||
opened_fd: JSC.Node.FileDescriptor = undefined,
|
||||
size: u32 = 0,
|
||||
|
||||
store: *Store = undefined,
|
||||
file_store: FileStore,
|
||||
|
||||
onCompleteCtx: *anyopaque = undefined,
|
||||
onCompleteCallback: OnCompleteCallback = undefined,
|
||||
runAsyncFrame: @Frame(OpenAndStatFile.runAsync) = undefined,
|
||||
|
||||
task: HTTPClient.NetworkThread.Task = undefined,
|
||||
|
||||
pub const OnCompleteCallback = fn (
|
||||
ctx: *anyopaque,
|
||||
fd: JSC.Node.FileDescriptor,
|
||||
size: anyerror!u32,
|
||||
global: *JSGlobalObject,
|
||||
) void;
|
||||
|
||||
pub usingnamespace FileOpenerMixin(OpenAndStatFile);
|
||||
pub usingnamespace FileCloserMixin(OpenAndStatFile);
|
||||
|
||||
pub fn createWithCtx(
|
||||
allocator: std.mem.Allocator,
|
||||
store: *Store,
|
||||
ctx: *anyopaque,
|
||||
onCompleteCallback: OnCompleteCallback,
|
||||
) !*OpenAndStatFile {
|
||||
var read_file = try allocator.create(OpenAndStatFile);
|
||||
read_file.* = OpenAndStatFile{
|
||||
.file_store = store.data.file,
|
||||
|
||||
.store = store,
|
||||
.onCompleteCtx = ctx,
|
||||
.onCompleteCallback = onCompleteCallback,
|
||||
};
|
||||
store.ref();
|
||||
return read_file;
|
||||
}
|
||||
|
||||
pub const OpenAndStatFileTask = JSC.IOTask(@This());
|
||||
|
||||
pub fn run(this: *OpenAndStatFile, task: *OpenAndStatFileTask) void {
|
||||
this.runAsyncFrame = async this.runAsync(task);
|
||||
}
|
||||
|
||||
pub fn then(this: *OpenAndStatFile, globalThis: *JSC.JSGlobalObject) void {
|
||||
var cb = this.onCompleteCallback;
|
||||
var cb_ctx = this.onCompleteCtx;
|
||||
const fd = this.opened_fd;
|
||||
const _size = this.size;
|
||||
const errno = this.errno;
|
||||
this.store.deref();
|
||||
|
||||
bun.default_allocator.destroy(this);
|
||||
if (errno) |err| {
|
||||
cb(cb_ctx, fd, err, globalThis);
|
||||
} else {
|
||||
cb(cb_ctx, fd, _size, globalThis);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runAsync(this: *OpenAndStatFile, task: *OpenAndStatFileTask) void {
|
||||
defer task.onFinish();
|
||||
this.opened_fd = 0;
|
||||
if (this.file_store.pathlike == .fd) {
|
||||
this.opened_fd = this.file_store.pathlike.fd;
|
||||
}
|
||||
const fd =
|
||||
if (this.opened_fd == 0)
|
||||
this.getFd() catch return
|
||||
else
|
||||
this.opened_fd;
|
||||
|
||||
const stat: std.os.Stat = switch (JSC.Node.Syscall.fstat(fd)) {
|
||||
.result => |result| result,
|
||||
.err => |err| {
|
||||
this.errno = AsyncIO.asError(err.errno);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
if (!std.os.S.ISREG(stat.mode)) {
|
||||
this.errno = error.ENOTSUP;
|
||||
return;
|
||||
}
|
||||
|
||||
this.size = @truncate(u32, @intCast(u64, @maximum(@intCast(i64, stat.size), 0)));
|
||||
}
|
||||
};
|
||||
|
||||
pub const ReadFile = struct {
|
||||
const OpenFrameType = if (Environment.isMac)
|
||||
void
|
||||
@@ -1831,18 +2036,19 @@ pub const Blob = struct {
|
||||
close_completion: HTTPClient.NetworkThread.Completion = undefined,
|
||||
task: HTTPClient.NetworkThread.Task = undefined,
|
||||
|
||||
onReadFileCompleteCtx: *anyopaque = undefined,
|
||||
onReadFileComplete: OnReadFileCallback = undefined,
|
||||
onCompleteCtx: *anyopaque = undefined,
|
||||
onCompleteCallback: OnReadFileCallback = undefined,
|
||||
|
||||
pub const OnReadFileCallback = fn (ctx: *anyopaque, bytes: anyerror![]u8) void;
|
||||
|
||||
const AsyncIO = HTTPClient.NetworkThread.AsyncIO;
|
||||
pub usingnamespace FileOpenerMixin(ReadFile);
|
||||
pub usingnamespace FileCloserMixin(ReadFile);
|
||||
|
||||
pub fn createWithCtx(
|
||||
allocator: std.mem.Allocator,
|
||||
store: *Store,
|
||||
onReadFileContext: *anyopaque,
|
||||
onReadFileComplete: OnReadFileCallback,
|
||||
onCompleteCallback: OnReadFileCallback,
|
||||
off: u32,
|
||||
max_len: u32,
|
||||
) !*ReadFile {
|
||||
@@ -1852,8 +2058,8 @@ pub const Blob = struct {
|
||||
.offset = off,
|
||||
.max_length = max_len,
|
||||
.store = store,
|
||||
.onReadFileCompleteCtx = onReadFileContext,
|
||||
.onReadFileComplete = onReadFileComplete,
|
||||
.onCompleteCtx = onReadFileContext,
|
||||
.onCompleteCallback = onCompleteCallback,
|
||||
};
|
||||
store.ref();
|
||||
return read_file;
|
||||
@@ -1877,54 +2083,6 @@ pub const Blob = struct {
|
||||
return try ReadFile.createWithCtx(allocator, store, @ptrCast(*anyopaque, context), Handler.run, off, max_len);
|
||||
}
|
||||
|
||||
pub fn getFdMac(this: *ReadFile) AsyncIO.OpenError!JSC.Node.FileDescriptor {
|
||||
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
this.opened_fd = AsyncIO.openSync(
|
||||
this.file_store.pathlike.path.sliceZ(&buf),
|
||||
std.os.O.RDONLY,
|
||||
) catch |err| {
|
||||
this.errno = err;
|
||||
return err;
|
||||
};
|
||||
return this.opened_fd;
|
||||
}
|
||||
|
||||
pub fn getFd(this: *ReadFile) AsyncIO.OpenError!JSC.Node.FileDescriptor {
|
||||
if (this.file_store.pathlike == .fd) {
|
||||
return this.file_store.pathlike.fd;
|
||||
}
|
||||
|
||||
if (comptime Environment.isMac) {
|
||||
return try this.getFdMac();
|
||||
} else {
|
||||
return try this.getFdLinux();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getFdLinux(this: *ReadFile) AsyncIO.OpenError!JSC.Node.FileDescriptor {
|
||||
var aio = &AsyncIO.global;
|
||||
|
||||
aio.open(
|
||||
*ReadFile,
|
||||
this,
|
||||
onOpen,
|
||||
&this.open_completion,
|
||||
this.file_store.pathlike.path.sliceZ(),
|
||||
std.os.O.RDONLY,
|
||||
0,
|
||||
);
|
||||
|
||||
suspend {
|
||||
this.open_frame = @frame().*;
|
||||
}
|
||||
|
||||
if (this.errno) |errno| {
|
||||
return @errSetCast(AsyncIO.OpenError, errno);
|
||||
}
|
||||
|
||||
return this.opened_fd;
|
||||
}
|
||||
|
||||
pub fn doRead(this: *ReadFile) AsyncIO.ReadError!u32 {
|
||||
var aio = &AsyncIO.global;
|
||||
|
||||
@@ -1951,32 +2109,11 @@ pub const Blob = struct {
|
||||
return this.read_len;
|
||||
}
|
||||
|
||||
pub fn doClose(this: *ReadFile) AsyncIO.CloseError!void {
|
||||
var aio = &AsyncIO.global;
|
||||
|
||||
aio.close(
|
||||
*ReadFile,
|
||||
this,
|
||||
onClose,
|
||||
&this.close_completion,
|
||||
this.opened_fd,
|
||||
);
|
||||
this.opened_fd = 0;
|
||||
|
||||
suspend {
|
||||
this.close_frame = @frame().*;
|
||||
}
|
||||
|
||||
if (this.errno) |errno| {
|
||||
return @errSetCast(AsyncIO.CloseError, errno);
|
||||
}
|
||||
}
|
||||
|
||||
pub const ReadFileTask = JSC.IOTask(@This());
|
||||
|
||||
pub fn then(this: *ReadFile, _: *JSC.JSGlobalObject) void {
|
||||
var cb = this.onReadFileComplete;
|
||||
var cb_ctx = this.onReadFileCompleteCtx;
|
||||
var cb = this.onCompleteCallback;
|
||||
var cb_ctx = this.onCompleteCtx;
|
||||
|
||||
var store = this.store orelse {
|
||||
var _err = this.errno orelse error.MissingData;
|
||||
@@ -2017,16 +2154,6 @@ pub const Blob = struct {
|
||||
this.runAsyncFrame = async this.runAsync(task);
|
||||
}
|
||||
|
||||
pub fn onOpen(this: *ReadFile, _: *HTTPClient.NetworkThread.Completion, result: AsyncIO.OpenError!JSC.Node.FileDescriptor) void {
|
||||
this.opened_fd = result catch |err| {
|
||||
this.errno = err;
|
||||
if (comptime Environment.isLinux) resume this.open_frame;
|
||||
return;
|
||||
};
|
||||
|
||||
if (comptime Environment.isLinux) resume this.open_frame;
|
||||
}
|
||||
|
||||
pub fn onRead(this: *ReadFile, _: *HTTPClient.NetworkThread.Completion, result: AsyncIO.ReadError!usize) void {
|
||||
this.read_len = @truncate(u32, result catch |err| {
|
||||
this.errno = err;
|
||||
@@ -2038,18 +2165,11 @@ pub const Blob = struct {
|
||||
resume this.read_frame;
|
||||
}
|
||||
|
||||
pub fn onClose(this: *ReadFile, _: *HTTPClient.NetworkThread.Completion, result: AsyncIO.CloseError!void) void {
|
||||
result catch |err| {
|
||||
this.errno = err;
|
||||
resume this.close_frame;
|
||||
return;
|
||||
};
|
||||
|
||||
resume this.close_frame;
|
||||
}
|
||||
|
||||
pub fn runAsync(this: *ReadFile, task: *ReadFileTask) void {
|
||||
defer task.onFinish();
|
||||
if (this.file_store.pathlike == .fd) {
|
||||
this.opened_fd = this.file_store.pathlike.fd;
|
||||
}
|
||||
|
||||
const fd = this.getFd() catch return;
|
||||
const needs_close = this.file_store.pathlike == .path;
|
||||
@@ -2591,10 +2711,8 @@ pub const Blob = struct {
|
||||
|
||||
pub fn NewInternalReadFileHandler(comptime Context: type, comptime Function: anytype) type {
|
||||
return struct {
|
||||
context: Context,
|
||||
|
||||
pub fn run(handler: *anyopaque, bytes_: anyerror![]u8) void {
|
||||
Function(bun.cast(Context, handler.context), bytes_);
|
||||
Function(bun.cast(Context, handler), bytes_);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2603,13 +2721,23 @@ pub const Blob = struct {
|
||||
var file_read = Store.ReadFile.createWithCtx(
|
||||
bun.default_allocator,
|
||||
this.store.?,
|
||||
ctx,
|
||||
Function,
|
||||
this.offset,
|
||||
this.size,
|
||||
Handler,
|
||||
) catch unreachable;
|
||||
var read_file_task = Store.ReadFile.ReadFileTask.createOnJSThread(bun.default_allocator, global, file_read) catch unreachable;
|
||||
read_file_task.schedule();
|
||||
}
|
||||
|
||||
pub fn doOpenAndStatFile(this: *Blob, comptime Handler: type, ctx: Handler, comptime Function: anytype, global: *JSGlobalObject) void {
|
||||
var file_read = Store.OpenAndStatFile.createWithCtx(
|
||||
bun.default_allocator,
|
||||
this.store.?,
|
||||
ctx,
|
||||
Function,
|
||||
) catch unreachable;
|
||||
var read_file_task = Store.ReadFile.ReadFileTask.createOnJSThread(bun.default_allocator, global, file_read) catch unreachable;
|
||||
var read_file_task = Store.OpenAndStatFile.OpenAndStatFileTask.createOnJSThread(bun.default_allocator, global, file_read) catch unreachable;
|
||||
read_file_task.schedule();
|
||||
}
|
||||
|
||||
@@ -3208,7 +3336,7 @@ pub const Body = struct {
|
||||
}
|
||||
|
||||
pub fn toErrorInstance(this: *Value, error_instance: JSC.JSValue, global: *JSGlobalObject) void {
|
||||
if (this.value == .Locked) {
|
||||
if (this.* == .Locked) {
|
||||
var locked = this.Locked;
|
||||
locked.deinit = true;
|
||||
if (locked.promise) |promise| {
|
||||
@@ -3236,7 +3364,7 @@ pub const Body = struct {
|
||||
bun.default_allocator,
|
||||
"Error reading file {s}",
|
||||
.{@errorName(err)},
|
||||
));
|
||||
) catch unreachable);
|
||||
error_str.mark();
|
||||
var error_instance = error_str.toErrorInstance(global);
|
||||
return this.toErrorInstance(error_instance, global);
|
||||
@@ -3255,7 +3383,7 @@ pub const Body = struct {
|
||||
}
|
||||
|
||||
if (tag == .Error) {
|
||||
JSC.C.JSValueUnprotect(VirtualMachine.vm.global.vm(), this.Error.asObjectRef());
|
||||
JSC.C.JSValueUnprotect(VirtualMachine.vm.global.ref(), this.Error.asObjectRef());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3383,7 +3511,7 @@ pub const Request = struct {
|
||||
|
||||
return MimeType.other.value;
|
||||
},
|
||||
.Used, .Locked, .Empty => return MimeType.other.value,
|
||||
.Error, .Used, .Locked, .Empty => return MimeType.other.value,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user