mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
fix(ext/http): reading headers with ongoing body reader (#15161)
This commit is contained in:
parent
d382126a12
commit
1b45d6fd23
3 changed files with 77 additions and 9 deletions
|
@ -73,6 +73,68 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
|
||||||
await promise;
|
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(
|
Deno.test(
|
||||||
{ permissions: { net: true } },
|
{ permissions: { net: true } },
|
||||||
async function httpServerGetRequestBody() {
|
async function httpServerGetRequestBody() {
|
||||||
|
|
|
@ -81,7 +81,11 @@
|
||||||
headerListInner: null,
|
headerListInner: null,
|
||||||
get headerList() {
|
get headerList() {
|
||||||
if (this.headerListInner === null) {
|
if (this.headerListInner === null) {
|
||||||
this.headerListInner = headerList();
|
try {
|
||||||
|
this.headerListInner = headerList();
|
||||||
|
} catch {
|
||||||
|
throw new TypeError("cannot read headers: request closed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this.headerListInner;
|
return this.headerListInner;
|
||||||
},
|
},
|
||||||
|
|
|
@ -44,6 +44,7 @@ use hyper::header::HeaderValue;
|
||||||
use hyper::server::conn::Http;
|
use hyper::server::conn::Http;
|
||||||
use hyper::service::Service;
|
use hyper::service::Service;
|
||||||
use hyper::Body;
|
use hyper::Body;
|
||||||
|
use hyper::HeaderMap;
|
||||||
use hyper::Request;
|
use hyper::Request;
|
||||||
use hyper::Response;
|
use hyper::Response;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -341,7 +342,7 @@ impl Resource for HttpStreamResource {
|
||||||
/// The read half of an HTTP stream.
|
/// The read half of an HTTP stream.
|
||||||
pub enum HttpRequestReader {
|
pub enum HttpRequestReader {
|
||||||
Headers(Request<Body>),
|
Headers(Request<Body>),
|
||||||
Body(Peekable<Body>),
|
Body(HeaderMap<HeaderValue>, Peekable<Body>),
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +451,7 @@ fn req_url(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn req_headers(
|
fn req_headers(
|
||||||
req: &hyper::Request<hyper::Body>,
|
header_map: &HeaderMap<HeaderValue>,
|
||||||
) -> Vec<(ByteString, ByteString)> {
|
) -> Vec<(ByteString, ByteString)> {
|
||||||
// We treat cookies specially, because we don't want them to get them
|
// We treat cookies specially, because we don't want them to get them
|
||||||
// mangled by the `Headers` object in JS. What we do is take all cookie
|
// mangled by the `Headers` object in JS. What we do is take all cookie
|
||||||
|
@ -459,8 +460,8 @@ fn req_headers(
|
||||||
let cookie_sep = "; ".as_bytes();
|
let cookie_sep = "; ".as_bytes();
|
||||||
let mut cookies = vec![];
|
let mut cookies = vec![];
|
||||||
|
|
||||||
let mut headers = Vec::with_capacity(req.headers().len());
|
let mut headers = Vec::with_capacity(header_map.len());
|
||||||
for (name, value) in req.headers().iter() {
|
for (name, value) in header_map.iter() {
|
||||||
if name == hyper::header::COOKIE {
|
if name == hyper::header::COOKIE {
|
||||||
cookies.push(value.as_bytes());
|
cookies.push(value.as_bytes());
|
||||||
} else {
|
} else {
|
||||||
|
@ -557,7 +558,8 @@ fn op_http_headers(
|
||||||
.try_borrow()
|
.try_borrow()
|
||||||
.ok_or_else(|| http_error("already in use"))?;
|
.ok_or_else(|| http_error("already in use"))?;
|
||||||
match &*rd {
|
match &*rd {
|
||||||
HttpRequestReader::Headers(request) => Ok(req_headers(request)),
|
HttpRequestReader::Headers(request) => Ok(req_headers(request.headers())),
|
||||||
|
HttpRequestReader::Body(headers, _) => Ok(req_headers(headers)),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -829,13 +831,13 @@ async fn op_http_read(
|
||||||
let body = loop {
|
let body = loop {
|
||||||
match &mut *rd {
|
match &mut *rd {
|
||||||
HttpRequestReader::Headers(_) => {}
|
HttpRequestReader::Headers(_) => {}
|
||||||
HttpRequestReader::Body(body) => break body,
|
HttpRequestReader::Body(_, body) => break body,
|
||||||
HttpRequestReader::Closed => return Ok(0),
|
HttpRequestReader::Closed => return Ok(0),
|
||||||
}
|
}
|
||||||
match take(&mut *rd) {
|
match take(&mut *rd) {
|
||||||
HttpRequestReader::Headers(request) => {
|
HttpRequestReader::Headers(request) => {
|
||||||
let body = request.into_body().peekable();
|
let (parts, body) = request.into_parts();
|
||||||
*rd = HttpRequestReader::Body(body);
|
*rd = HttpRequestReader::Body(parts.headers, body.peekable());
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue