1
0
Fork 0
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:
Divy Srivastava 2022-07-12 23:01:37 +05:30 committed by cjihrig
parent d382126a12
commit 1b45d6fd23
No known key found for this signature in database
GPG key ID: 7434390BDBE9B9C5
3 changed files with 77 additions and 9 deletions

View file

@ -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() {

View file

@ -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;
}, },

View file

@ -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!(),
}; };