diff options
author | 2023-09-16 21:55:41 -0700 | |
---|---|---|
committer | 2023-09-16 21:55:41 -0700 | |
commit | 383d5b55d611b62492178eae4833bf3c134de246 (patch) | |
tree | 2abd5a03b05939c4c5f80608e5a0a0b5250e9146 | |
parent | 80e1f32ca1236dcf6f7ed559c596afbced0f8f3a (diff) | |
download | bun-383d5b55d611b62492178eae4833bf3c134de246.tar.gz bun-383d5b55d611b62492178eae4833bf3c134de246.tar.zst bun-383d5b55d611b62492178eae4833bf3c134de246.zip |
fix(fetch) handle 100 continue (#5496)
* handle 100 continue
* move comment
* cleanup
* fmt
-rw-r--r-- | src/http_client_async.zig | 10 | ||||
-rw-r--r-- | test/js/web/fetch/fetch.test.ts | 60 |
2 files changed, 69 insertions, 1 deletions
diff --git a/src/http_client_async.zig b/src/http_client_async.zig index 79fe74899..b3c06df56 100644 --- a/src/http_client_async.zig +++ b/src/http_client_async.zig @@ -2637,6 +2637,14 @@ pub fn onData(this: *HTTPClient, comptime is_ssl: bool, incoming_data: []const u this.state.pending_response = response; var body_buf = to_read[@min(@as(usize, @intCast(response.bytes_read)), to_read.len)..]; + // handle the case where we have a 100 Continue + if (response.status_code == 100) { + // we still can have the 200 OK in the same buffer sometimes + if (body_buf.len > 0) { + this.onData(is_ssl, body_buf, ctx, socket); + } + return; + } var deferred_redirect: ?*URLBufferPool.Node = null; const can_continue = this.handleResponseMetadata( @@ -3399,6 +3407,7 @@ pub fn handleResponseMetadata( this.proxy_tunneling = false; } + // if is no redirect or if is redirect == "manual" just proceed const is_redirect = response.status_code >= 300 and response.status_code <= 399; if (is_redirect) { if (this.redirect_type == FetchRedirect.follow and location.len > 0 and this.remaining_redirect_count > 0) { @@ -3513,7 +3522,6 @@ pub fn handleResponseMetadata( } } - // if is no redirect or if is redirect == "manual" just proceed this.state.response_stage = if (this.state.transfer_encoding == .chunked) .body_chunk else .body; const content_length = this.state.content_length orelse 0; // if no body is expected we should stop processing diff --git a/test/js/web/fetch/fetch.test.ts b/test/js/web/fetch/fetch.test.ts index 4ef5d7bba..fc4ce7a18 100644 --- a/test/js/web/fetch/fetch.test.ts +++ b/test/js/web/fetch/fetch.test.ts @@ -5,6 +5,7 @@ import { mkfifo } from "mkfifo"; import { tmpdir } from "os"; import { join } from "path"; import { gc, withoutAggressiveGC, gcTick } from "harness"; +import net from "net"; const tmp_dir = mkdtempSync(join(realpathSync(tmpdir()), "fetch.test")); @@ -1415,3 +1416,62 @@ it("cloned response headers are independent after accessing", () => { cloned.headers.set("content-type", "text/plain"); expect(response.headers.get("content-type")).toBe("text/html; charset=utf-8"); }); + +it("should work with http 100 continue", async () => { + let server: net.Server | undefined; + try { + server = net.createServer(socket => { + socket.on("data", data => { + const lines = data.toString().split("\r\n"); + for (const line of lines) { + if (line.length == 0) { + socket.write("HTTP/1.1 100 Continue\r\n\r\n"); + socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nHello, World!"); + break; + } + } + }); + }); + + const { promise: start, resolve } = Promise.withResolvers(); + server.listen(8080, resolve); + + await start; + + const address = server.address() as net.AddressInfo; + const result = await fetch(`http://localhost:${address.port}`).then(r => r.text()); + expect(result).toBe("Hello, World!"); + } finally { + server?.close(); + } +}); + +it("should work with http 100 continue on the same buffer", async () => { + let server: net.Server | undefined; + try { + server = net.createServer(socket => { + socket.on("data", data => { + const lines = data.toString().split("\r\n"); + for (const line of lines) { + if (line.length == 0) { + socket.write( + "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nHello, World!", + ); + break; + } + } + }); + }); + + const { promise: start, resolve } = Promise.withResolvers(); + server.listen(8080, resolve); + + await start; + + const address = server.address() as net.AddressInfo; + const result = await fetch(`http://localhost:${address.port}`).then(r => r.text()); + expect(result).toBe("Hello, World!"); + } finally { + server?.close(); + } +}); |