Skip to content

Commit

Permalink
Ignore error of writing responses to aborted requests (#546)
Browse files Browse the repository at this point in the history
  • Loading branch information
kt3k authored and ry committed Jul 28, 2019
1 parent 5e77e8a commit 826deb1
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 10 deletions.
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";

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)
);
}

0 comments on commit 826deb1

Please sign in to comment.