diff options
author | 2021-12-18 20:23:10 -0800 | |
---|---|---|
committer | 2021-12-18 20:23:10 -0800 | |
commit | 430fe09d6dcf0f6bf10460c95d5ac47e51189fc3 (patch) | |
tree | fa1c2abedef74b2376158e7fdeb23240d94137fc /src | |
parent | 5455c71f93360a66af59b70ec569050ba7a28442 (diff) | |
download | bun-430fe09d6dcf0f6bf10460c95d5ac47e51189fc3.tar.gz bun-430fe09d6dcf0f6bf10460c95d5ac47e51189fc3.tar.zst bun-430fe09d6dcf0f6bf10460c95d5ac47e51189fc3.zip |
Delete synchronous HTTP client!
Diffstat (limited to 'src')
-rw-r--r-- | src/analytics/analytics_thread.zig | 46 | ||||
-rw-r--r-- | src/cli/create_command.zig | 2 | ||||
-rw-r--r-- | src/cli/install_completions_command.zig | 1 | ||||
-rw-r--r-- | src/http_client.zig | 934 | ||||
-rw-r--r-- | src/install/install.zig | 4 |
5 files changed, 29 insertions, 958 deletions
diff --git a/src/analytics/analytics_thread.zig b/src/analytics/analytics_thread.zig index e45359ddb..3d5b63e4f 100644 --- a/src/analytics/analytics_thread.zig +++ b/src/analytics/analytics_thread.zig @@ -2,7 +2,8 @@ usingnamespace @import("../global.zig"); const sync = @import("../sync.zig"); const std = @import("std"); -const HTTPClient = @import("../http_client.zig"); +const HTTP = @import("http"); +const NetworkThread = @import("network_thread"); const URL = @import("../query_string_map.zig").URL; const Fs = @import("../fs.zig"); const Analytics = @import("./analytics_schema.zig").analytics; @@ -349,10 +350,23 @@ fn readloop() anyerror!void { defer Output.flush(); thread.setName("Analytics") catch {}; - var event_list = EventList.init(); - event_list.client.verbose = FeatureFlags.verbose_analytics; - event_list.client.header_entries.append(default_allocator, header_entry) catch unreachable; - event_list.client.header_buf = headers_buf; + var event_list = try default_allocator.create(EventList); + event_list.* = EventList.init(); + + var headers_entries: Headers.Entries = Headers.Entries{}; + headers_entries.append(default_allocator, header_entry) catch unreachable; + event_list.async_http = HTTP.AsyncHTTP.init( + default_allocator, + .POST, + URL.parse(Environment.analytics_url), + headers_entries, + headers_buf, + &event_list.out_buffer, + &event_list.in_buffer, + std.time.ns_per_ms * 10000, + ) catch return; + + event_list.async_http.client.verbose = FeatureFlags.verbose_analytics; // everybody's random should be random while (true) { @@ -372,24 +386,18 @@ fn readloop() anyerror!void { pub const EventList = struct { header: Analytics.EventListHeader, events: std.ArrayList(Event), - client: HTTPClient, + async_http: HTTP.AsyncHTTP, out_buffer: MutableString, - in_buffer: std.ArrayList(u8), + in_buffer: MutableString, pub fn init() EventList { random = std.rand.DefaultPrng.init(@intCast(u64, std.time.milliTimestamp())); return EventList{ .header = GenerateHeader.generate(), .events = std.ArrayList(Event).init(default_allocator), - .in_buffer = std.ArrayList(u8).init(default_allocator), - .client = HTTPClient.init( - default_allocator, - .POST, - URL.parse(Environment.analytics_url), - Headers.Entries{}, - "", - ), + .in_buffer = MutableString.init(default_allocator, 1024) catch unreachable, + .async_http = undefined, .out_buffer = MutableString.init(default_allocator, 0) catch unreachable, }; } @@ -408,9 +416,9 @@ pub const EventList = struct { pub var is_stuck = false; var stuck_count: u8 = 0; fn _flush(this: *EventList) !void { - this.in_buffer.clearRetainingCapacity(); + this.in_buffer.reset(); - const AnalyticsWriter = Writer(*std.ArrayList(u8).Writer); + const AnalyticsWriter = Writer(*MutableString.Writer); var in_buffer = &this.in_buffer; var buffer_writer = in_buffer.writer(); @@ -456,7 +464,7 @@ pub const EventList = struct { var retry_remaining: usize = 10; retry: while (retry_remaining > 0) { - const response = this.client.send(this.in_buffer.items, &this.out_buffer) catch |err| { + const response = this.async_http.sendSync() catch |err| { if (FeatureFlags.verbose_analytics) { Output.prettyErrorln("[Analytics] failed due to error {s} ({d} retries remain)", .{ @errorName(err), retry_remaining }); } @@ -490,7 +498,7 @@ pub const EventList = struct { stuck_count *= @intCast(u8, @boolToInt(retry_remaining == 0)); disabled = disabled or stuck_count > 4; - this.in_buffer.clearRetainingCapacity(); + this.in_buffer.reset(); this.out_buffer.reset(); if (comptime FeatureFlags.verbose_analytics) { diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 2ac8005db..096fe5ab7 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -245,8 +245,6 @@ const CreateOptions = struct { const BUN_CREATE_DIR = ".bun-create"; var home_dir_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; pub const CreateCommand = struct { - var client: HTTPClient = undefined; - pub fn exec(ctx: Command.Context, positionals_: []const []const u8) !void { Global.configureAllocator(.{ .long_running = false }); try NetworkThread.init(); diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index 8d4de60fa..d4e9fc5b9 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -21,7 +21,6 @@ const bundler = @import("../bundler.zig"); const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle; const fs = @import("../fs.zig"); const URL = @import("../query_string_map.zig").URL; -const HTTPClient = @import("../http_client.zig"); const ParseJSON = @import("../json_parser.zig").ParseJSON; const Archive = @import("../libarchive/libarchive.zig").Archive; const Zlib = @import("../zlib.zig"); diff --git a/src/http_client.zig b/src/http_client.zig deleted file mode 100644 index 9fa3e2e6c..000000000 --- a/src/http_client.zig +++ /dev/null @@ -1,934 +0,0 @@ -// @link "/Users/jarred/Code/bun/src/deps/zlib/libz.a" - -const picohttp = @import("./deps/picohttp.zig"); -usingnamespace @import("./global.zig"); -const std = @import("std"); -const Headers = @import("./javascript/jsc/webcore/response.zig").Headers; -const URL = @import("./query_string_map.zig").URL; -const Method = @import("./http/method.zig").Method; -const Api = @import("./api/schema.zig").Api; -const Lock = @import("./lock.zig").Lock; -const HTTPClient = @This(); -const SOCKET_FLAGS = os.SOCK_CLOEXEC; -// const S2n = @import("./s2n.zig"); -const Zlib = @import("./zlib.zig"); -const StringBuilder = @import("./string_builder.zig"); - -fn writeRequest( - comptime Writer: type, - writer: Writer, - request: picohttp.Request, - body: string, - // header_hashes: []u64, -) !void { - try writer.writeAll(request.method); - try writer.writeAll(" "); - try writer.writeAll(request.path); - try writer.writeAll(" HTTP/1.1\r\n"); - - for (request.headers) |header, i| { - try writer.writeAll(header.name); - try writer.writeAll(": "); - try writer.writeAll(header.value); - try writer.writeAll("\r\n"); - } -} - -method: Method, -header_entries: Headers.Entries, -header_buf: string, -url: URL, -allocator: *std.mem.Allocator, -verbose: bool = isTest, -tcp_client: tcp.Client = undefined, -body_size: u32 = 0, -read_count: u32 = 0, -remaining_redirect_count: i8 = 127, -redirect_buf: [2048]u8 = undefined, -disable_shutdown: bool = true, -timeout: u32 = 0, -progress_node: ?*std.Progress.Node = null, -force_last_modified: bool = false, -if_modified_since: string = "", -response_headers_buf: [256]picohttp.Header = undefined, -request_headers_buf: [256]picohttp.Header = undefined, - -pub fn init(allocator: *std.mem.Allocator, method: Method, url: URL, header_entries: Headers.Entries, header_buf: string) HTTPClient { - return HTTPClient{ - .allocator = allocator, - .method = method, - .url = url, - .header_entries = header_entries, - .header_buf = header_buf, - }; -} - -threadlocal var request_content_len_buf: [64]u8 = undefined; -threadlocal var header_name_hashes: [256]u64 = undefined; -// threadlocal var resolver_cache -const tcp = std.x.net.tcp; -const ip = std.x.net.ip; - -const IPv4 = std.x.os.IPv4; -const IPv6 = std.x.os.IPv6; -const Socket = std.x.os.Socket; -const os = std.os; - -// lowercase hash header names so that we can be sure -pub fn hashHeaderName(name: string) u64 { - var hasher = std.hash.Wyhash.init(0); - var remain: string = name; - var buf: [32]u8 = undefined; - var buf_slice: []u8 = std.mem.span(&buf); - - while (remain.len > 0) { - var end = std.math.min(hasher.buf.len, remain.len); - - hasher.update(strings.copyLowercase(std.mem.span(remain[0..end]), buf_slice)); - remain = remain[end..]; - } - - return hasher.final(); -} - -const host_header_hash = hashHeaderName("Host"); -const connection_header_hash = hashHeaderName("Connection"); - -pub const Encoding = enum { - identity, - gzip, - deflate, - brotli, - chunked, -}; - -const content_encoding_hash = hashHeaderName("Content-Encoding"); -const transfer_encoding_header = hashHeaderName("Transfer-Encoding"); - -const host_header_name = "Host"; -const content_length_header_name = "Content-Length"; -const content_length_header_hash = hashHeaderName("Content-Length"); -const connection_header = picohttp.Header{ .name = "Connection", .value = "close" }; -const accept_header = picohttp.Header{ .name = "Accept", .value = "*/*" }; -const accept_header_hash = hashHeaderName("Accept"); - -const accept_encoding_no_compression = "identity"; -const accept_encoding_compression = "deflate, gzip"; -const accept_encoding_header_compression = picohttp.Header{ .name = "Accept-Encoding", .value = accept_encoding_compression }; -const accept_encoding_header_no_compression = picohttp.Header{ .name = "Accept-Encoding", .value = accept_encoding_no_compression }; - -const accept_encoding_header = if (FeatureFlags.disable_compression_in_http_client) - accept_encoding_header_no_compression -else - accept_encoding_header_compression; - -const accept_encoding_header_hash = hashHeaderName("Accept-Encoding"); - -const user_agent_header = picohttp.Header{ .name = "User-Agent", .value = "Bun.js " ++ Global.package_json_version }; -const user_agent_header_hash = hashHeaderName("User-Agent"); -const location_header_hash = hashHeaderName("Location"); - -pub fn headerStr(this: *const HTTPClient, ptr: Api.StringPointer) string { - return this.header_buf[ptr.offset..][0..ptr.length]; -} - -pub const HeaderBuilder = struct { - content: StringBuilder = StringBuilder{}, - header_count: u64 = 0, - entries: Headers.Entries = Headers.Entries{}, - - pub fn count(this: *HeaderBuilder, name: string, value: string) void { - this.header_count += 1; - this.content.count(name); - this.content.count(value); - } - - pub fn allocate(this: *HeaderBuilder, allocator: *std.mem.Allocator) !void { - try this.content.allocate(allocator); - try this.entries.ensureTotalCapacity(allocator, this.header_count); - } - pub fn append(this: *HeaderBuilder, name: string, value: string) void { - const name_ptr = Api.StringPointer{ - .offset = @truncate(u32, this.content.len), - .length = @truncate(u32, name.len), - }; - - _ = this.content.append(name); - - const value_ptr = Api.StringPointer{ - .offset = @truncate(u32, this.content.len), - .length = @truncate(u32, value.len), - }; - _ = this.content.append(value); - this.entries.appendAssumeCapacity(Headers.Kv{ .name = name_ptr, .value = value_ptr }); - } - - pub fn apply(this: *HeaderBuilder, client: *HTTPClient) void { - client.header_entries = this.entries; - client.header_buf = this.content.ptr.?[0..this.content.len]; - } -}; - -threadlocal var server_name_buf: [1024]u8 = undefined; - -pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request { - var header_count: usize = 0; - var header_entries = this.header_entries.slice(); - var header_names = header_entries.items(.name); - var header_values = header_entries.items(.value); - var request_headers_buf = &this.request_headers_buf; - - var override_accept_encoding = false; - - var override_user_agent = false; - for (header_names) |head, i| { - const name = this.headerStr(head); - // Hash it as lowercase - const hash = hashHeaderName(name); - - // Skip host and connection header - // we manage those - switch (hash) { - host_header_hash, - connection_header_hash, - content_length_header_hash, - => continue, - hashHeaderName("if-modified-since") => { - if (this.force_last_modified) { - this.if_modified_since = this.headerStr(header_values[i]); - } - }, - else => {}, - } - - override_user_agent = override_user_agent or hash == user_agent_header_hash; - - override_accept_encoding = override_accept_encoding or hash == accept_encoding_header_hash; - - request_headers_buf[header_count] = picohttp.Header{ - .name = name, - .value = this.headerStr(header_values[i]), - }; - - // header_name_hashes[header_count] = hash; - - // // ensure duplicate headers come after each other - // if (header_count > 2) { - // var head_i: usize = header_count - 1; - // while (head_i > 0) : (head_i -= 1) { - // if (header_name_hashes[head_i] == header_name_hashes[header_count]) { - // std.mem.swap(picohttp.Header, &header_name_hashes[header_count], &header_name_hashes[head_i + 1]); - // std.mem.swap(u64, &request_headers_buf[header_count], &request_headers_buf[head_i + 1]); - // break; - // } - // } - // } - header_count += 1; - } - - // request_headers_buf[header_count] = connection_header; - // header_count += 1; - - if (!override_user_agent) { - request_headers_buf[header_count] = user_agent_header; - header_count += 1; - } - - request_headers_buf[header_count] = accept_header; - header_count += 1; - - request_headers_buf[header_count] = picohttp.Header{ - .name = host_header_name, - .value = this.url.hostname, - }; - header_count += 1; - - if (!override_accept_encoding) { - request_headers_buf[header_count] = accept_encoding_header; - header_count += 1; - } - - if (body_len > 0) { - request_headers_buf[header_count] = picohttp.Header{ - .name = content_length_header_name, - .value = std.fmt.bufPrint(&request_content_len_buf, "{d}", .{body_len}) catch "0", - }; - header_count += 1; - } - - return picohttp.Request{ - .method = @tagName(this.method), - .path = this.url.pathname, - .minor_version = 1, - .headers = request_headers_buf[0..header_count], - }; -} - -pub fn connect( - this: *HTTPClient, -) !tcp.Client { - const port = this.url.getPortAuto(); - var client: tcp.Client = undefined; - - // if (this.url.isLocalhost()) { - // try client.connect( - // try std.x.os.Socket.Address.initIPv4(try std.net.Address.resolveIp("localhost", port), port), - // ); - // } else { - // } else if (this.url.isDomainName()) { - var stream = try std.net.tcpConnectToHost(default_allocator, this.url.hostname, port); - client = tcp.Client{ .socket = std.x.os.Socket.from(stream.handle) }; - - if (this.timeout > 0) { - client.setReadTimeout(this.timeout) catch {}; - client.setWriteTimeout(this.timeout) catch {}; - } - - client.setNoDelay(true) catch {}; - client.setReadBufferSize(http_req_buf.len) catch {}; - client.setQuickACK(true) catch {}; - - // } - // } else if (this.url.getIPv4Address()) |ip_addr| { - // try client.connect(std.x.os.Socket.Address(ip_addr, port)); - // } else if (this.url.getIPv6Address()) |ip_addr| { - // try client.connect(std.x.os.Socket.Address.initIPv6(ip_addr, port)); - // } else { - // return error.MissingHostname; - // } - - return client; -} - -threadlocal var http_req_buf: [65436]u8 = undefined; - -pub fn send(this: *HTTPClient, body: []const u8, body_out_str: *MutableString) !picohttp.Response { - // this prevents stack overflow - redirect: while (this.remaining_redirect_count >= -1) { - if (this.url.isHTTPS()) { - return this.sendHTTPS(body, body_out_str) catch |err| { - switch (err) { - error.Redirect => { - this.remaining_redirect_count -= 1; - continue :redirect; - }, - else => return err, - } - }; - } else { - return this.sendHTTP(body, body_out_str) catch |err| { - switch (err) { - error.Redirect => { - this.remaining_redirect_count -= 1; - continue :redirect; - }, - else => return err, - } - }; - } - } - - return error.TooManyRedirects; -} - -pub fn sendHTTP(this: *HTTPClient, body: []const u8, body_out_str: *MutableString) !picohttp.Response { - this.tcp_client = try this.connect(); - defer std.os.closeSocket(this.tcp_client.socket.fd); - var request = buildRequest(this, body.len); - if (this.verbose) { - Output.prettyErrorln("{s}", .{request}); - } - var client_writer = this.tcp_client.writer(SOCKET_FLAGS); - { - var client_writer_buffered = std.io.bufferedWriter(client_writer); - var client_writer_buffered_writer = client_writer_buffered.writer(); - - try writeRequest(@TypeOf(&client_writer_buffered_writer), &client_writer_buffered_writer, request, body); - try client_writer_buffered_writer.writeAll("\r\n"); - try client_writer_buffered.flush(); - } - - if (body.len > 0) { - try client_writer.writeAll(body); - } - - var client_reader = this.tcp_client.reader(SOCKET_FLAGS); - - if (this.progress_node == null) { - return this.processResponse( - false, - false, - @TypeOf(client_reader), - client_reader, - body_out_str, - ); - } else { - return this.processResponse( - false, - true, - @TypeOf(client_reader), - client_reader, - body_out_str, - ); - } -} - -const ZlibPool = struct { - lock: Lock = Lock.init(), - items: std.ArrayList(*MutableString), - allocator: *std.mem.Allocator, - pub var instance: ZlibPool = undefined; - pub var loaded: bool = false; - - pub fn init(allocator: *std.mem.Allocator) ZlibPool { - return ZlibPool{ - .allocator = allocator, - .items = std.ArrayList(*MutableString).init(allocator), - }; - } - - pub fn get(this: *ZlibPool) !*MutableString { - this.lock.lock(); - defer this.lock.unlock(); - switch (this.items.items.len) { - 0 => { - var mutable = try this.allocator.create(MutableString); - mutable.* = try MutableString.init(this.allocator, 0); - return mutable; - }, - else => { - return this.items.pop(); - }, - } - - return item; - } - - pub fn put(this: *ZlibPool, mutable: *MutableString) !void { - this.lock.lock(); - defer this.lock.unlock(); - mutable.reset(); - try this.items.append(mutable); - } -}; - -pub fn processResponse(this: *HTTPClient, comptime is_https: bool, comptime report_progress: bool, comptime Client: type, client: Client, body_out_str: *MutableString) !picohttp.Response { - var response: picohttp.Response = undefined; - var read_length: usize = 0; - { - var read_headers_up_to: usize = 0; - - var req_buf_read: usize = std.math.maxInt(usize); - defer this.read_count += @intCast(u32, read_length); - - restart: while (req_buf_read != 0) { - req_buf_read = try client.read(http_req_buf[read_length..]); - read_length += req_buf_read; - if (comptime report_progress) { - this.progress_node.?.activate(); - this.progress_node.?.setCompletedItems(read_length); - this.progress_node.?.context.maybeRefresh(); - } - - var request_buffer = http_req_buf[0..read_length]; - read_headers_up_to = if (read_headers_up_to > read_length) read_length else read_headers_up_to; - - response = picohttp.Response.parseParts(request_buffer, &this.response_headers_buf, &read_headers_up_to) catch |err| { - switch (err) { - error.ShortRead => { - continue :restart; - }, - else => { - return err; - }, - } - }; - break :restart; - } - } - - body_out_str.reset(); - var content_length: u32 = 0; - var encoding = Encoding.identity; - var transfer_encoding = Encoding.identity; - var pretend_its_304 = false; - - var location: string = ""; - - if (this.verbose) { - Output.prettyErrorln("Response: {s}", .{response}); - } - - for (response.headers) |header| { - switch (hashHeaderName(header.name)) { - hashHeaderName("Last-Modified") => { - if (this.force_last_modified and response.status_code > 199 and response.status_code < 300 and this.if_modified_since.len > 0) { - if (strings.eql(this.if_modified_since, header.value)) { - pretend_its_304 = true; - } - } - }, - content_length_header_hash => { - content_length = std.fmt.parseInt(u32, header.value, 10) catch 0; - try body_out_str.inflate(content_length); - body_out_str.list.expandToCapacity(); - this.body_size = content_length; - }, - content_encoding_hash => { - if (strings.eqlComptime(header.value, "gzip")) { - encoding = Encoding.gzip; - } else if (strings.eqlComptime(header.value, "deflate")) { - encoding = Encoding.deflate; - } else if (!strings.eqlComptime(header.value, "identity")) { - return error.UnsupportedContentEncoding; - } - }, - transfer_encoding_header => { - if (strings.eqlComptime(header.value, "gzip")) { - transfer_encoding = Encoding.gzip; - } else if (strings.eqlComptime(header.value, "deflate")) { - transfer_encoding = Encoding.deflate; - } else if (strings.eqlComptime(header.value, "identity")) { - transfer_encoding = Encoding.identity; - } else if (strings.eqlComptime(header.value, "chunked")) { - transfer_encoding = Encoding.chunked; - } else { - return error.UnsupportedTransferEncoding; - } - }, - location_header_hash => { - location = header.value; - }, - - else => {}, - } - } - - if (location.len > 0 and this.remaining_redirect_count > 0) { - switch (response.status_code) { - 302, 301, 307, 308, 303 => { - if (strings.indexOf(location, "://")) |i| { - const protocol_name = location[0..i]; - if (strings.eqlComptime(protocol_name, "http") or strings.eqlComptime(protocol_name, "https")) {} else { - return error.UnsupportedRedirectProtocol; - } - - std.mem.copy(u8, &this.redirect_buf, location); - this.url = URL.parse(location); - } else { - const original_url = this.url; - this.url = URL.parse(std.fmt.bufPrint( - &this.redirect_buf, - "{s}://{s}{s}", - .{ original_url.displayProtocol(), original_url.displayHostname(), location }, - ) catch return error.RedirectURLTooLong); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303 - if (response.status_code == 303) { - this.method = .GET; - } - - return error.Redirect; - }, - else => {}, - } - } - - body_getter: { - if (pretend_its_304) { - response.status_code = 304; - break :body_getter; - } - - if (transfer_encoding == Encoding.chunked) { - var decoder = std.mem.zeroes(picohttp.phr_chunked_decoder); - var buffer_: *MutableString = body_out_str; - - switch (encoding) { - Encoding.gzip, Encoding.deflate => { - if (!ZlibPool.loaded) { - ZlibPool.instance = ZlibPool.init(default_allocator); - ZlibPool.loaded = true; - } - - buffer_ = try ZlibPool.instance.get(); - }, - else => {}, - } - - var buffer = buffer_.*; - - var last_read: usize = 0; - { - var remainder = http_req_buf[@intCast(usize, response.bytes_read)..read_length]; - last_read = remainder.len; - try buffer.inflate(std.math.max(remainder.len, 2048)); - buffer.list.expandToCapacity(); - std.mem.copy(u8, buffer.list.items, remainder); - } - - // set consume_trailer to 1 to discard the trailing header - // using content-encoding per chunk is not supported - decoder.consume_trailer = 1; - - // these variable names are terrible - // it's copypasta from https://github.com/h2o/picohttpparser#phr_decode_chunked - // (but ported from C -> zig) - var rret: usize = 0; - var rsize: usize = last_read; - var pret: isize = picohttp.phr_decode_chunked(&decoder, buffer.list.items.ptr, &rsize); - var total_size = rsize; - - while (pret == -2) { - if (buffer.list.items[total_size..].len < @intCast(usize, decoder.bytes_left_in_chunk) or buffer.list.items[total_size..].len < 512) { - try buffer.inflate(std.math.max(total_size * 2, 1024)); - buffer.list.expandToCapacity(); - } - - rret = try client.read(buffer.list.items[total_size..]); - - if (rret == 0) { - return error.ChunkedEncodingError; - } - - rsize = rret; - pret = picohttp.phr_decode_chunked(&decoder, buffer.list.items[total_size..].ptr, &rsize); - if (pret == -1) return error.ChunkedEncodingParseError; - - total_size += rsize; - - if (comptime report_progress) { - this.progress_node.?.activate(); - this.progress_node.?.setCompletedItems(total_size); - this.progress_node.?.context.maybeRefresh(); - } - } - - buffer.list.shrinkRetainingCapacity(total_size); - buffer_.* = buffer; - - switch (encoding) { - Encoding.gzip, Encoding.deflate => { - body_out_str.list.expandToCapacity(); - defer ZlibPool.instance.put(buffer_) catch unreachable; - var reader = try Zlib.ZlibReaderArrayList.init(buffer.list.items, &body_out_str.list, default_allocator); - reader.readAll() catch |err| { - if (reader.errorMessage()) |msg| { - Output.prettyErrorln("<r><red>Zlib error<r>: <b>{s}<r>", .{msg}); - Output.flush(); - } - return err; - }; - }, - else => {}, - } - - if (comptime report_progress) { - this.progress_node.?.activate(); - this.progress_node.?.setCompletedItems(body_out_str.list.items.len); - this.progress_node.?.context.maybeRefresh(); - } - - this.body_size = @intCast(u32, body_out_str.list.items.len); - return response; - } - - if (content_length > 0) { - var remaining_content_length = content_length; - var remainder = http_req_buf[@intCast(usize, response.bytes_read)..read_length]; - remainder = remainder[0..std.math.min(remainder.len, content_length)]; - var buffer_: *MutableString = body_out_str; - - switch (encoding) { - Encoding.gzip, Encoding.deflate => { - if (!ZlibPool.loaded) { - ZlibPool.instance = ZlibPool.init(default_allocator); - ZlibPool.loaded = true; - } - - buffer_ = try ZlibPool.instance.get(); - if (buffer_.list.capacity < remaining_content_length) { - try buffer_.list.ensureUnusedCapacity(buffer_.allocator, remaining_content_length); - } - buffer_.list.items = buffer_.list.items.ptr[0..remaining_content_length]; - }, - else => {}, - } - var buffer = buffer_.*; - - var body_size: usize = 0; - if (remainder.len > 0) { - std.mem.copy(u8, buffer.list.items, remainder); - body_size = remainder.len; - this.read_count += @intCast(u32, body_size); - remaining_content_length -= @intCast(u32, remainder.len); - } - - while (remaining_content_length > 0) { - const size = @intCast(u32, try client.read( - buffer.list.items[body_size..], - )); - this.read_count += size; - if (size == 0) break; - - body_size += size; - remaining_content_length -= size; - - if (comptime report_progress) { - this.progress_node.?.activate(); - this.progress_node.?.setCompletedItems(body_size); - this.progress_node.?.context.maybeRefresh(); - } - } - - if (comptime report_progress) { - this.progress_node.?.activate(); - this.progress_node.?.setCompletedItems(body_size); - this.progress_node.?.context.maybeRefresh(); - } - - buffer.list.shrinkRetainingCapacity(body_size); - buffer_.* = buffer; - - switch (encoding) { - Encoding.gzip, Encoding.deflate => { - body_out_str.list.expandToCapacity(); - defer ZlibPool.instance.put(buffer_) catch unreachable; - var reader = try Zlib.ZlibReaderArrayList.init(buffer.list.items, &body_out_str.list, default_allocator); - reader.readAll() catch |err| { - if (reader.errorMessage()) |msg| { - Output.prettyErrorln("<r><red>Zlib error<r>: <b>{s}<r>", .{msg}); - Output.flush(); - } - return err; - }; - }, - else => {}, - } - } - } - - if (comptime report_progress) { - this.progress_node.?.activate(); - this.progress_node.?.setCompletedItems(body_out_str.list.items.len); - this.progress_node.?.context.maybeRefresh(); - } - - return response; -} - -pub fn sendHTTPS(this: *HTTPClient, body_str: []const u8, body_out_str: *MutableString) !picohttp.Response { - return error.Redirect; - // var connection = try this.connect(); - // S2n.boot(default_allocator); - // const hostname = this.url.displayHostname(); - // std.mem.copy(u8, &server_name_buf, hostname); - // server_name_buf[hostname.len] = 0; - // var server_name = server_name_buf[0..hostname.len :0]; - - // var client = S2n.Connection.init(connection.socket.fd); - // try client.start(server_name); - // client.disable_shutdown = this.disable_shutdown; - // defer client.close() catch {}; - - // var request = buildRequest(this, body_str.len); - // if (this.verbose) { - // Output.prettyErrorln("{s}", .{request}); - // } - // const body = body_str; - - // var client_writer = client.writer(); - // { - // var client_writer_buffered = std.io.bufferedWriter(client_writer); - // var client_writer_buffered_writer = client_writer_buffered.writer(); - - // try writeRequest(@TypeOf(&client_writer_buffered_writer), &client_writer_buffered_writer, request, body); - // try client_writer_buffered_writer.writeAll("\r\n"); - // try client_writer_buffered.flush(); - // } - - // if (body.len > 0) { - // try client_writer.writeAll(body); - // } - - // if (this.progress_node == null) { - // return try this.processResponse(true, false, @TypeOf(&client), &client, body_out_str); - // } else { - // return try this.processResponse(true, true, @TypeOf(&client), &client, body_out_str); - // } -} - -// zig test src/http_client.zig --test-filter "sendHTTP - only" -lc -lc++ /Users/jarred/Code/bun/src/deps/zlib/libz.a /Users/jarred/Code/bun/src/deps/picohttpparser.o --cache-dir /Users/jarred/Code/bun/zig-cache --global-cache-dir /Users/jarred/.cache/zig --name bun --pkg-begin clap /Users/jarred/Code/bun/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin picohttp /Users/jarred/Code/bun/src/deps/picohttp.zig --pkg-end --pkg-begin iguanaTLS /Users/jarred/Code/bun/src/deps/iguanaTLS/src/main.zig --pkg-end -I /Users/jarred/Code/bun/src/deps -I /Users/jarred/Code/bun/src/deps/mimalloc -I /usr/local/opt/icu4c/include -L src/deps/mimalloc -L /usr/local/opt/icu4c/lib --main-pkg-path /Users/jarred/Code/bun --enable-cache -femit-bin=zig-out/bin/test --test-no-exec -test "sendHTTP - only" { - Output.initTest(); - defer Output.flush(); - - var headers = try std.heap.c_allocator.create(Headers); - headers.* = Headers{ - .entries = @TypeOf(headers.entries){}, - .buf = @TypeOf(headers.buf){}, - .used = 0, - .allocator = std.heap.c_allocator, - }; - - // headers.appendHeader("X-What", "ok", true, true, false); - headers.appendHeader("Accept-Encoding", "identity", true, true, false); - - var client = HTTPClient.init( - std.heap.c_allocator, - .GET, - URL.parse("http://example.com/"), - headers.entries, - headers.buf.items, - ); - var body_out_str = try MutableString.init(std.heap.c_allocator, 0); - var response = try client.sendHTTP("", &body_out_str); - try std.testing.expectEqual(response.status_code, 200); - try std.testing.expectEqual(body_out_str.list.items.len, 1256); - try std.testing.expectEqualStrings(body_out_str.list.items, @embedFile("fixtures_example.com.html")); -} - -// zig test src/http_client.zig --test-filter "sendHTTP - gzip" -lc -lc++ /Users/jarred/Code/bun/src/deps/zlib/libz.a /Users/jarred/Code/bun/src/deps/picohttpparser.o --cache-dir /Users/jarred/Code/bun/zig-cache --global-cache-dir /Users/jarred/.cache/zig --name bun --pkg-begin clap /Users/jarred/Code/bun/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin picohttp /Users/jarred/Code/bun/src/deps/picohttp.zig --pkg-end --pkg-begin iguanaTLS /Users/jarred/Code/bun/src/deps/iguanaTLS/src/main.zig --pkg-end -I /Users/jarred/Code/bun/src/deps -I /Users/jarred/Code/bun/src/deps/mimalloc -I /usr/local/opt/icu4c/include -L src/deps/mimalloc -L /usr/local/opt/icu4c/lib --main-pkg-path /Users/jarred/Code/bun --enable-cache -femit-bin=zig-out/bin/test --test-no-exec -test "sendHTTP - gzip" { - Output.initTest(); - defer Output.flush(); - - var headers = try std.heap.c_allocator.create(Headers); - headers.* = Headers{ - .entries = @TypeOf(headers.entries){}, - .buf = @TypeOf(headers.buf){}, - .used = 0, - .allocator = std.heap.c_allocator, - }; - - // headers.appendHeader("X-What", "ok", true, true, false); - headers.appendHeader("Accept-Encoding", "gzip", true, true, false); - - var client = HTTPClient.init( - std.heap.c_allocator, - .GET, - URL.parse("http://example.com/"), - headers.entries, - headers.buf.items, - ); - var body_out_str = try MutableString.init(std.heap.c_allocator, 0); - var response = try client.sendHTTP("", &body_out_str); - try std.testing.expectEqual(response.status_code, 200); - try std.testing.expectEqualStrings(body_out_str.list.items, @embedFile("fixtures_example.com.html")); -} - -// zig test src/http_client.zig --test-filter "sendHTTPS - identity" -lc -lc++ /Users/jarred/Code/bun/src/deps/zlib/libz.a /Users/jarred/Code/bun/src/deps/picohttpparser.o --cache-dir /Users/jarred/Code/bun/zig-cache --global-cache-dir /Users/jarred/.cache/zig --name bun --pkg-begin clap /Users/jarred/Code/bun/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin picohttp /Users/jarred/Code/bun/src/deps/picohttp.zig --pkg-end --pkg-begin iguanaTLS /Users/jarred/Code/bun/src/deps/iguanaTLS/src/main.zig --pkg-end -I /Users/jarred/Code/bun/src/deps -I /Users/jarred/Code/bun/src/deps/mimalloc -I /usr/local/opt/icu4c/include -L src/deps/mimalloc -L /usr/local/opt/icu4c/lib --main-pkg-path /Users/jarred/Code/bun --enable-cache -femit-bin=zig-out/bin/test --test-no-exec -test "sendHTTPS - identity" { - Output.initTest(); - defer Output.flush(); - - var headers = try std.heap.c_allocator.create(Headers); - headers.* = Headers{ - .entries = @TypeOf(headers.entries){}, - .buf = @TypeOf(headers.buf){}, - .used = 0, - .allocator = std.heap.c_allocator, - }; - - headers.appendHeader("X-What", "ok", true, true, false); - headers.appendHeader("Accept-Encoding", "identity", true, true, false); - - var client = HTTPClient.init( - std.heap.c_allocator, - .GET, - URL.parse("https://example.com/"), - headers.entries, - headers.buf.items, - ); - var body_out_str = try MutableString.init(std.heap.c_allocator, 0); - var response = try client.sendHTTPS("", &body_out_str); - try std.testing.expectEqual(response.status_code, 200); - try std.testing.expectEqualStrings(body_out_str.list.items, @embedFile("fixtures_example.com.html")); -} - -test "sendHTTPS - gzip" { - Output.initTest(); - defer Output.flush(); - - var headers = try std.heap.c_allocator.create(Headers); - headers.* = Headers{ - .entries = @TypeOf(headers.entries){}, - .buf = @TypeOf(headers.buf){}, - .used = 0, - .allocator = std.heap.c_allocator, - }; - - headers.appendHeader("Accept-Encoding", "gzip", false, false, false); - - var client = HTTPClient.init( - std.heap.c_allocator, - .GET, - URL.parse("https://example.com/"), - headers.entries, - headers.buf.items, - ); - var body_out_str = try MutableString.init(std.heap.c_allocator, 0); - var response = try client.sendHTTPS("", &body_out_str); - try std.testing.expectEqual(response.status_code, 200); - try std.testing.expectEqualStrings(body_out_str.list.items, @embedFile("fixtures_example.com.html")); -} - -// zig test src/http_client.zig --test-filter "sendHTTPS - deflate" -lc -lc++ /Users/jarred/Code/bun/src/deps/zlib/libz.a /Users/jarred/Code/bun/src/deps/picohttpparser.o --cache-dir /Users/jarred/Code/bun/zig-cache --global-cache-dir /Users/jarred/.cache/zig --name bun --pkg-begin clap /Users/jarred/Code/bun/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin picohttp /Users/jarred/Code/bun/src/deps/picohttp.zig --pkg-end --pkg-begin iguanaTLS /Users/jarred/Code/bun/src/deps/iguanaTLS/src/main.zig --pkg-end -I /Users/jarred/Code/bun/src/deps -I /Users/jarred/Code/bun/src/deps/mimalloc -I /usr/local/opt/icu4c/include -L src/deps/mimalloc -L /usr/local/opt/icu4c/lib --main-pkg-path /Users/jarred/Code/bun --enable-cache -femit-bin=zig-out/bin/test -test "sendHTTPS - deflate" { - Output.initTest(); - defer Output.flush(); - - var headers = try std.heap.c_allocator.create(Headers); - headers.* = Headers{ - .entries = @TypeOf(headers.entries){}, - .buf = @TypeOf(headers.buf){}, - .used = 0, - .allocator = std.heap.c_allocator, - }; - - headers.appendHeader("Accept-Encoding", "deflate", false, false, false); - - var client = HTTPClient.init( - std.heap.c_allocator, - .GET, - URL.parse("https://example.com/"), - headers.entries, - headers.buf.items, - ); - var body_out_str = try MutableString.init(std.heap.c_allocator, 0); - var response = try client.sendHTTPS("", &body_out_str); - try std.testing.expectEqual(response.status_code, 200); - try std.testing.expectEqualStrings(body_out_str.list.items, @embedFile("fixtures_example.com.html")); -} - -// zig test src/http_client.zig --test-filter "sendHTTP" -lc -lc++ /Users/jarred/Code/bun/src/deps/zlib/libz.a /Users/jarred/Code/bun/src/deps/picohttpparser.o --cache-dir /Users/jarred/Code/bun/zig-cache --global-cache-dir /Users/jarred/.cache/zig --name bun --pkg-begin clap /Users/jarred/Code/bun/src/deps/zig-clap/clap.zig --pkg-end --pkg-begin picohttp /Users/jarred/Code/bun/src/deps/picohttp.zig --pkg-end --pkg-begin iguanaTLS /Users/jarred/Code/bun/src/deps/iguanaTLS/src/main.zig --pkg-end -I /Users/jarred/Code/bun/src/deps -I /Users/jarred/Code/bun/src/deps/mimalloc -I /usr/local/opt/icu4c/include -L src/deps/mimalloc -L /usr/local/opt/icu4c/lib --main-pkg-path /Users/jarred/Code/bun --enable-cache -femit-bin=zig-out/bin/test - -test "send - redirect" { - Output.initTest(); - defer Output.flush(); - - var headers = try std.heap.c_allocator.create(Headers); - headers.* = Headers{ - .entries = @TypeOf(headers.entries){}, - .buf = @TypeOf(headers.buf){}, - .used = 0, - .allocator = std.heap.c_allocator, - }; - - headers.appendHeader("Accept-Encoding", "gzip", false, false, false); - - var client = HTTPClient.init( - std.heap.c_allocator, - .GET, - URL.parse("https://www.bun.sh/"), - headers.entries, - headers.buf.items, - ); - try std.testing.expectEqualStrings(client.url.hostname, "www.bun.sh"); - var body_out_str = try MutableString.init(std.heap.c_allocator, 0); - var response = try client.send("", &body_out_str); - try std.testing.expectEqual(response.status_code, 200); - try std.testing.expectEqual(client.url.hostname, "bun.sh"); - try std.testing.expectEqualStrings(body_out_str.list.items, @embedFile("fixtures_example.com.html")); -} diff --git a/src/install/install.zig b/src/install/install.zig index c46e9784d..aa143f2f9 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -23,7 +23,7 @@ const DotEnv = @import("../env_loader.zig"); const which = @import("../which.zig").which; const Run = @import("../bun_js.zig").Run; const NewBunQueue = @import("../bun_queue.zig").NewBunQueue; -const HTTPClient = @import("../http_client.zig"); +const HeaderBuilder = @import("http").HeaderBuilder; const Fs = @import("../fs.zig"); const FileSystem = Fs.FileSystem; const Lock = @import("../lock.zig").Lock; @@ -225,7 +225,7 @@ const NetworkTask = struct { etag = manifest.pkg.etag.slice(manifest.string_buf); } - var header_builder = HTTPClient.HeaderBuilder{}; + var header_builder = HeaderBuilder{}; if (etag.len != 0) { header_builder.count("If-None-Match", etag); |