-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fetch(stream) add stream support for compressed and uncompressed data #4127
Conversation
✅ #c07180a368f1ac203a73157cf3dfbdef341a631a |
d7f6800
to
bad4db1
Compare
6a4e1cb
to
6189205
Compare
❌ @cirospaciari 1 files with test failures on linux-x64-baseline:
|
❌ @cirospaciari 1 files with test failures on linux-x64:
|
❌ @cirospaciari 8 files with test failures on bun-darwin-aarch64:
|
❌ @cirospaciari 5 files with test failures on bun-darwin-x64-baseline:
|
GREAT WORK! Looking forward to this one. |
0757809
to
86f0dd1
Compare
src/bun.js/unbounded_queue.zig
Outdated
@@ -73,6 +73,8 @@ pub fn UnboundedQueue(comptime T: type, comptime next_field: meta.FieldEnum(T)) | |||
} | |||
|
|||
pub fn pop(self: *Self) ?*T { | |||
if (self.isEmpty()) return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if count
is incremented after isEmpty()
returns true?
Why isn't it sufficient for front
to be null and then return?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should use popBatch
src/bun.js/webcore/body.zig
Outdated
pub fn size(this: *const Value) Blob.SizeType { | ||
return switch (this.*) { | ||
.Blob => this.Blob.size, | ||
.InternalBlob => @as(Blob.SizeType, @truncate(this.InternalBlob.sliceConst().len)), | ||
.WTFStringImpl => @as(Blob.SizeType, @truncate(this.WTFStringImpl.utf8ByteLength())), | ||
.Locked => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.Locked => { | |
.Locked => this._lcokedSize() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 4676cfe
src/bun.js/webcore/response.zig
Outdated
@@ -619,23 +621,32 @@ pub const Fetch = struct { | |||
javascript_vm: *VirtualMachine = undefined, | |||
global_this: *JSGlobalObject = undefined, | |||
request_body: HTTPRequestBody = undefined, | |||
// buffer being used by AsyncHTTP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use ///
because //
doesn't show up in autocomplete
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed 4676cfe
src/bun.js/webcore/response.zig
Outdated
return; | ||
} | ||
} else { | ||
this.response.?.body.value.Locked.size_hint = @as(u52, @intCast(this.body_size)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the pointer aliasing here intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed 4676cfe
src/bun.js/webcore/response.zig
Outdated
return; | ||
} | ||
|
||
if (this.response) |response| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is keeping this response alive? How can we be sure that the garbage collector doesn't free the Response object in the middle of streaming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed 4676cfe
I think this needs about two dozen more tests |
aa5e79b
to
f42efc7
Compare
looks like macOS build fails |
1e474e8
to
7cb1869
Compare
@@ -1181,7 +1181,7 @@ WebCore::FetchHeaders* WebCore__FetchHeaders__createFromPicoHeaders_(const void* | |||
|
|||
for (size_t j = 0; j < end; j++) { | |||
PicoHTTPHeader header = pico_headers.ptr[j]; | |||
if (header.value.len == 0) | |||
if (header.value.len == 0 || header.name.len == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting
@@ -3794,6 +3794,10 @@ pub const Blob = struct { | |||
} else { | |||
return build.blob.dupe(); | |||
} | |||
} else if (current.toSliceOrNull(global)) |sliced| { | |||
if (sliced.allocator.get()) |allocator| { | |||
return Blob.initWithAllASCII(bun.constStrToU8(sliced.slice()), allocator, global, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be cloned. There are too many cases where we assume Blob is always owned by mimalloc, but in this case it might not be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also if you do .arrayBuffer() on a Response it will transfer the buffer which would mean that the string is now mutable which will break an unknown number of things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird toSliceOrNull
dont clone it?
https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/bindings.zig#L4193-L4198
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toSlice does not if it's an ASCII latin1 string. toOwnedSlice clones it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed on ef6989f
src/bun.js/webcore/body.zig
Outdated
// .InlineBlob => @truncate(Blob.SizeType, this.InlineBlob.sliceConst().len), | ||
else => 0, | ||
}; | ||
} | ||
|
||
pub fn estimatedSize(this: *const Value) usize { | ||
return switch (this.*) { | ||
.Blob => this.Blob.size, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct. Blob.size
defaults to a max value which is an absurdly large number to indicate that it is not a valid number. This would need to call resolveSize
which would be too expensive to call here because it potentially does stat()
on a file in the filesystem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed on ef6989f
src/bun.js/webcore/response.zig
Outdated
} | ||
|
||
fn getSizeHint(this: *FetchTasklet) u52 { | ||
if (this.body_size == .content_length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be more idiomatic Zig for this to be a switch statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed on ef6989f
src/bun.js/webcore/response.zig
Outdated
}; | ||
} | ||
|
||
fn getSizeHint(this: *FetchTasklet) u52 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use Blob.SizeType
instead of directly using u52
? I think that makes it more clear that it is consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed on ef6989f
src/bun.js/webcore/response.zig
Outdated
|
||
fn getSizeHint(this: *FetchTasklet) u52 { | ||
if (this.body_size == .content_length) { | ||
return @as(u52, @intCast(this.body_size.content_length)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be truncate and the @as
is unnecessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed on ef6989f
src/bun.js/webcore/response.zig
Outdated
@@ -830,7 +1022,10 @@ pub const Fetch = struct { | |||
const allocator = bun.default_allocator; | |||
var response = allocator.create(Response) catch unreachable; | |||
response.* = this.toResponse(allocator); | |||
return Response.makeMaybePooled(@as(js.JSContextRef, @ptrCast(this.global_this)), response); | |||
const response_js = Response.makeMaybePooled(@as(js.JSContextRef, @ptrCast(this.global_this)), response); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the @ptrCast
here does nothing btw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed on ef6989f
.err = Syscall.Error{ | ||
.errno = @as(Syscall.Error.Int, @intCast(-completion.result)), | ||
.syscall = .read, | ||
.err = .{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err.err is kinda weird
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah will rename to sys_err
} | ||
} | ||
gcTick(false); | ||
expect(buffer.byteLength).toBe(content.byteLength); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only checks the length. It needs to check the value too. expect(buffer).toEqual(content)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will add it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed on 70d8e69
Thank you for writing the tests. I think we can ship this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any idea why the fsevents test is failing?
@@ -545,8 +545,7 @@ pub const EventLoop = struct { | |||
}, | |||
.FetchTasklet => { | |||
var fetch_task: *Fetch.FetchTasklet = task.get(Fetch.FetchTasklet).?; | |||
fetch_task.onDone(); | |||
fetch_task.deinit(); | |||
fetch_task.onProgressUpdate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when does the task itself get freed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it self deinit
after receiving the has_more == false
after clearData
calls
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but the Tasklet part. The thing which is queued to the tasks queue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but the Tasklet part. The thing which is queued to the tasks queue.
Oh I see dummy mistake, let me fix it ASAP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually only one task is allocated, and on the last callback it is freed. We use task.javascript_vm.eventLoop().enqueueTaskConcurrent(task.concurrent_task.from(task, .manual_deinit));
so we use the same pointer to the task, not need to be freed, on each callback only in the last one the deinits are:
bun/src/bun.js/webcore/response.zig
Line 735 in ef6989f
this.deinit(); |
bun/src/bun.js/webcore/response.zig
Line 855 in ef6989f
this.deinit(); |
bun/src/bun.js/webcore/response.zig
Line 863 in ef6989f
this.deinit(); |
no idea, this poped up after the last rebase |
src/bun.js/webcore/streams.zig
Outdated
@@ -525,9 +525,19 @@ pub const StreamResult = union(Tag) { | |||
into_array: IntoArray, | |||
into_array_and_done: IntoArray, | |||
pending: *Pending, | |||
err: Syscall.Error, | |||
|
|||
err: union(Err) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe do
Error: Syscall.Error
JSValue: JSC.JSValue
That JSvalue version also needs to be kept alive somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed on 4d07c98
🥳 🥳 🥳 |
What does this PR do?
How did you verify your code works?
I wrote automated tests + existing tests