diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts index 779f11e564..9afe22553c 100644 --- a/cli/tests/unit/http_test.ts +++ b/cli/tests/unit/http_test.ts @@ -73,6 +73,68 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() { await promise; }); +// https://github.com/denoland/deno/issues/15107 +Deno.test( + { permissions: { net: true } }, + async function httpLazyHeadersIssue15107() { + let headers: Headers; + const promise = (async () => { + const listener = Deno.listen({ port: 2333 }); + const conn = await listener.accept(); + listener.close(); + const httpConn = Deno.serveHttp(conn); + const e = await httpConn.nextRequest(); + assert(e); + const { request } = e; + request.text(); + headers = request.headers; + httpConn.close(); + })(); + + const conn = await Deno.connect({ port: 2333 }); + // Send GET request with a body + content-length. + const encoder = new TextEncoder(); + const body = + `GET / HTTP/1.1\r\nHost: 127.0.0.1:2333\r\nContent-Length: 5\r\n\r\n12345`; + const writeResult = await conn.write(encoder.encode(body)); + assertEquals(body.length, writeResult); + await promise; + conn.close(); + assertEquals(headers!.get("content-length"), "5"); + }, +); + +Deno.test( + { permissions: { net: true } }, + async function httpReadHeadersAfterClose() { + const promise = (async () => { + const listener = Deno.listen({ port: 2334 }); + const conn = await listener.accept(); + listener.close(); + const httpConn = Deno.serveHttp(conn); + const e = await httpConn.nextRequest(); + assert(e); + const { request, respondWith } = e; + + await request.text(); // Read body + await respondWith(new Response("Hello World")); // Closes request + + assertThrows(() => request.headers, TypeError, "request closed"); + httpConn.close(); + })(); + + const conn = await Deno.connect({ port: 2334 }); + // Send GET request with a body + content-length. + const encoder = new TextEncoder(); + const body = + `GET / HTTP/1.1\r\nHost: 127.0.0.1:2333\r\nContent-Length: 5\r\n\r\n12345`; + const writeResult = await conn.write(encoder.encode(body)); + assertEquals(body.length, writeResult); + await promise; + conn.close(); + }, +); + Deno.test( { permissions: { net: true } }, async function httpServerGetRequestBody() { diff --git a/ext/fetch/23_request.js b/ext/fetch/23_request.js index 3058db6f71..cbc6a887b7 100644 --- a/ext/fetch/23_request.js +++ b/ext/fetch/23_request.js @@ -81,7 +81,11 @@ headerListInner: null, get headerList() { if (this.headerListInner === null) { - this.headerListInner = headerList(); + try { + this.headerListInner = headerList(); + } catch { + throw new TypeError("cannot read headers: request closed"); + } } return this.headerListInner; }, diff --git a/ext/http/lib.rs b/ext/http/lib.rs index 6a900eb8ea..27d277654c 100644 --- a/ext/http/lib.rs +++ b/ext/http/lib.rs @@ -44,6 +44,7 @@ use hyper::header::HeaderValue; use hyper::server::conn::Http; use hyper::service::Service; use hyper::Body; +use hyper::HeaderMap; use hyper::Request; use hyper::Response; use serde::Serialize; @@ -341,7 +342,7 @@ impl Resource for HttpStreamResource { /// The read half of an HTTP stream. pub enum HttpRequestReader { Headers(Request
), - Body(Peekable), + Body(HeaderMap