diff options
author | 2023-08-06 22:49:10 -0700 | |
---|---|---|
committer | 2023-08-06 22:49:10 -0700 | |
commit | 00a907c7de842787e87b0d0a42d8d33704c5aaae (patch) | |
tree | d7b29475801dced2597f3f7406c0b9deaabe81b8 | |
parent | 0665733b0302ac7e743e4c131c7697741fa923a5 (diff) | |
download | bun-00a907c7de842787e87b0d0a42d8d33704c5aaae.tar.gz bun-00a907c7de842787e87b0d0a42d8d33704c5aaae.tar.zst bun-00a907c7de842787e87b0d0a42d8d33704c5aaae.zip |
Fixes #4001 (#4034)
* Avoid a utf8 conversion in isDetached
* Fixes #4001
* hit the long url codepath
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r-- | src/bun.js/webcore/blob.zig | 3 | ||||
-rw-r--r-- | src/bun.js/webcore/request.zig | 32 | ||||
-rw-r--r-- | test/js/bun/http/bun-server.test.ts | 82 |
3 files changed, 108 insertions, 9 deletions
diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 7036babfb..b384048ce 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -3943,7 +3943,8 @@ pub const AnyBlob = union(enum) { pub fn isDetached(this: *const AnyBlob) bool { return switch (this.*) { .Blob => |blob| blob.isDetached(), - else => this.slice().len == 0, + .InternalBlob => this.InternalBlob.bytes.items.len == 0, + .WTFStringImpl => this.WTFStringImpl.length() == 0, }; } diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig index af212e4cf..e2b104016 100644 --- a/src/bun.js/webcore/request.zig +++ b/src/bun.js/webcore/request.zig @@ -322,7 +322,7 @@ pub const Request = struct { pub fn sizeOfURL(this: *const Request) usize { if (this.url.length() > 0) - return this.url.length(); + return this.url.byteSlice().len; if (this.uws_request) |req| { const req_url = req.url(); @@ -369,8 +369,8 @@ pub const Request = struct { std.debug.assert(this.sizeOfURL() == url_bytelength); } - if (url_bytelength < 64) { - var buffer: [64]u8 = undefined; + if (url_bytelength < 128) { + var buffer: [128]u8 = undefined; const url = std.fmt.bufPrint(&buffer, "{s}{any}{s}", .{ this.getProtocol(), fmt, @@ -381,21 +381,30 @@ pub const Request = struct { std.debug.assert(this.sizeOfURL() == url.len); } - if (bun.String.tryCreateAtom(url)) |atomized| { - this.url = atomized; - return; + var href = bun.JSC.URL.hrefFromString(bun.String.fromBytes(url)); + if (!href.isEmpty()) { + if (href.byteSlice().ptr == url.ptr) { + this.url = bun.String.createLatin1(url[0..href.length()]); + href.deref(); + } else { + this.url = href; + } + } else { + // TODO: what is the right thing to do for invalid URLS? + this.url = bun.String.create(url); } + + return; } if (strings.isAllASCII(host) and strings.isAllASCII(req_url)) { this.url = bun.String.createUninitializedLatin1(url_bytelength); var bytes = @constCast(this.url.byteSlice()); - const url = std.fmt.bufPrint(bytes, "{s}{any}{s}", .{ + _ = std.fmt.bufPrint(bytes, "{s}{any}{s}", .{ this.getProtocol(), fmt, req_url, }) catch @panic("Unexpected error while printing URL"); - _ = url; } else { // slow path var temp_url = std.fmt.allocPrint(bun.default_allocator, "{s}{any}{s}", .{ @@ -406,6 +415,13 @@ pub const Request = struct { defer bun.default_allocator.free(temp_url); this.url = bun.String.create(temp_url); } + + const href = bun.JSC.URL.hrefFromString(this.url); + // TODO: what is the right thing to do for invalid URLS? + if (!href.isEmpty()) { + this.url = href; + } + return; } } diff --git a/test/js/bun/http/bun-server.test.ts b/test/js/bun/http/bun-server.test.ts index 9fd97e3cb..6220b6a54 100644 --- a/test/js/bun/http/bun-server.test.ts +++ b/test/js/bun/http/bun-server.test.ts @@ -1,6 +1,88 @@ import { describe, expect, test } from "bun:test"; describe("Server", () => { + test.only("normlizes incoming request URLs", async () => { + const server = Bun.serve({ + fetch(request) { + return new Response(request.url, { + headers: { + "Connection": "close", + }, + }); + }, + port: 0, + }); + const received: string[] = []; + const expected: string[] = []; + for (let path of [ + "/", + "/../", + "/./", + "/foo", + "/foo/", + "/foo/bar", + "/foo/bar/", + "/foo/bar/..", + "/foo/bar/../", + "/foo/bar/../?123", + "/foo/bar/../?123=456", + "/foo/bar/../#123=456", + "/", + "/../", + "/./", + "/foo", + "/foo/", + "/foo/bar", + "/foo/bar/", + "/foo/bar/..", + "/foo/bar/../", + "/foo/bar/../?123", + "/foo/bar/../?123=456", + "/foo/bar/../#123=456", + "/../".repeat(128), + "/./".repeat(128), + "/foo".repeat(128), + "/foo/".repeat(128), + "/foo/bar".repeat(128), + "/foo/bar/".repeat(128), + "/foo/bar/..".repeat(128), + "/foo/bar/../".repeat(128), + "/../".repeat(128), + "/./".repeat(128), + "/foo".repeat(128), + "/foo/".repeat(128), + "/foo/bar".repeat(128), + "/foo/bar/".repeat(128), + "/foo/bar/..".repeat(128), + "/foo/bar/../".repeat(128), + ]) { + expected.push(new URL(path, "http://localhost:" + server.port).href); + + const { promise, resolve } = Promise.withResolvers(); + Bun.connect({ + hostname: server.hostname, + port: server.port, + + socket: { + async open(socket) { + socket.write(`GET ${path} HTTP/1.1\r\nHost: localhost:${server.port}\r\n\r\n`); + await socket.flush(); + }, + async data(socket, data) { + const lines = Buffer.from(data).toString("utf8"); + received.push(lines.split("\r\n\r\n").at(-1)!); + await socket.end(); + resolve(); + }, + }, + }); + await promise; + } + + server.stop(true); + expect(received).toEqual(expected); + }); + test("should not allow Bun.serve without first argument being a object", () => { expect(() => { //@ts-ignore |