Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore error of writing responses to aborted requests #546

Merged
merged 3 commits into from
Jul 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions http/racing_server.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { serve, ServerRequest } from "./server.ts";
import { delay } from "../util/async.ts";

const addr = Deno.args[1] || "127.0.0.1:4501";
const server = serve(addr);

const body = new TextEncoder().encode("Hello 1\n");
const body4 = new TextEncoder().encode("World 4\n");

function sleep(ms: number): Promise<void> {
return new Promise((res): number => setTimeout(res, ms));
}

async function delayedRespond(request: ServerRequest): Promise<void> {
await sleep(3000);
await delay(3000);
await request.respond({ status: 200, body });
}

Expand Down
2 changes: 1 addition & 1 deletion http/racing_server_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ async function startServer(): Promise<void> {
args: ["deno", "run", "-A", "http/racing_server.ts"],
stdout: "piped"
});
// Once fileServer is ready it will write to its stdout.
// Once racing server is ready it will write to its stdout.
const r = new TextProtoReader(new BufReader(server.stdout!));
const s = await r.readLine();
assert(s !== Deno.EOF && s.includes("Racing server listening..."));
Expand Down
13 changes: 9 additions & 4 deletions http/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,10 +342,15 @@ export class Server implements AsyncIterable<ServerRequest> {
// The connection was gracefully closed.
} else if (err) {
// An error was thrown while parsing request headers.
await writeResponse(req!.w, {
status: 400,
body: new TextEncoder().encode(`${err.message}\r\n\r\n`)
});
try {
await writeResponse(req!.w, {
status: 400,
body: new TextEncoder().encode(`${err.message}\r\n\r\n`)
});
} catch (_) {
// The connection is destroyed.
// Ignores the error.
}
} else if (this.closing) {
// There are more requests incoming but the server is closing.
// TODO(ry): send a back a HTTP 503 Service Unavailable status.
Expand Down
45 changes: 45 additions & 0 deletions http/server_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// https://github.com/golang/go/blob/master/src/net/http/responsewrite_test.go

const { Buffer } = Deno;
import { TextProtoReader } from "../textproto/mod.ts";
import { test, runIfMain } from "../testing/mod.ts";
import { assert, assertEquals, assertNotEquals } from "../testing/asserts.ts";
import {
Expand All @@ -15,6 +16,7 @@ import {
readRequest,
parseHTTPVersion
} from "./server.ts";
import { delay } from "../util/async.ts";
import {
BufReader,
BufWriter,
Expand Down Expand Up @@ -456,4 +458,47 @@ test({
}
});

test({
name: "[http] destroyed connection",
async fn(): Promise<void> {
// TODO: don't skip on windows when process.kill is implemented on windows.
if (Deno.build.os === "win") {
return;
}
// Runs a simple server as another process
const p = Deno.run({
args: [Deno.execPath, "http/testdata/simple_server.ts", "--allow-net"],
stdout: "piped"
});

try {
const r = new TextProtoReader(new BufReader(p.stdout!));
const s = await r.readLine();
assert(s !== Deno.EOF && s.includes("server listening"));

let serverIsRunning = true;
p.status().then(
(): void => {
serverIsRunning = false;
}
);

await delay(100);

// Reqeusts to the server and immediately closes the connection
const conn = await Deno.dial("tcp", "127.0.0.1:4502");
await conn.write(new TextEncoder().encode("GET / HTTP/1.0\n\n"));
conn.close();

// Waits for the server to handle the above (broken) request
await delay(100);

assert(serverIsRunning);
} finally {
// Stops the sever.
p.kill(Deno.Signal.SIGINT);
}
}
});

runIfMain(import.meta);
11 changes: 11 additions & 0 deletions http/testdata/simple_server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
// This is an example of a server that responds with an empty body
import { serve } from "../server.ts";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment describing the purpose of this

// This is an example of a server that responds with an empty body


window.onload = async function main() {
const addr = "0.0.0.0:4502";
console.log(`Simple server listening on ${addr}`);
for await (let req of serve(addr)) {
req.respond({});
}
}
10 changes: 10 additions & 0 deletions util/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,13 @@ export async function collectUint8Arrays(
}
return collected;
}

// Delays the given milliseconds and resolves.
export function delay(ms: number): Promise<void> {
return new Promise(
(res): number =>
setTimeout((): void => {
res();
}, ms)
);
}