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

Redesign of http server module #188

Merged
merged 12 commits into from
Feb 15, 2019
19 changes: 14 additions & 5 deletions http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@ A framework for creating HTTP/HTTPS server.
## Example

```typescript
import { serve } from "https://deno.land/x/http/server.ts";
const s = serve("0.0.0.0:8000");
import { createServer } from "https://deno.land/x/http/server.ts";
import { encode } from "https://deno.land/x/strings/strings.ts";

async function main() {
for await (const req of s) {
req.respond({ body: new TextEncoder().encode("Hello World\n") });
}
const server = createServer();
server.handle("/", async (req, res) => {
await res.respond({
status: 200,
body: encode("ok")
});
});
server.handle(new RegExp("/foo/(?<id>.+)"), async (req, res) => {
const { id } = req.match.groups;
await res.respondJson({ id });
});
server.listen("127.0.0.1:8080");
}

main();
Expand Down
12 changes: 6 additions & 6 deletions http/file_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
listenAndServe,
ServerRequest,
setContentLength,
Response
ServerResponse
} from "./server.ts";
import { cwd, DenoError, ErrorKind, args, stat, readDir, open } from "deno";
import { extname } from "../fs/path.ts";
Expand Down Expand Up @@ -195,14 +195,14 @@ async function serveFallback(req: ServerRequest, e: Error) {
}
}

function serverLog(req: ServerRequest, res: Response) {
function serverLog(req: ServerRequest, res: ServerResponse) {
const d = new Date().toISOString();
const dateFmt = `[${d.slice(0, 10)} ${d.slice(11, 19)}]`;
const s = `${dateFmt} "${req.method} ${req.url} ${req.proto}" ${res.status}`;
console.log(s);
}

function setCORS(res: Response) {
function setCORS(res: ServerResponse) {
if (!res.headers) {
res.headers = new Headers();
}
Expand All @@ -213,11 +213,11 @@ function setCORS(res: Response) {
);
}

listenAndServe(addr, async req => {
listenAndServe(addr, async (req, res) => {
const fileName = req.url.replace(/\/$/, "");
const filePath = currentDir + fileName;

let response: Response;
let response: ServerResponse;

try {
const fileInfo = await stat(filePath);
Expand All @@ -235,7 +235,7 @@ listenAndServe(addr, async req => {
setCORS(response);
}
serverLog(req, response);
req.respond(response);
res.respond(response);
}
});

Expand Down
11 changes: 8 additions & 3 deletions http/http_bench.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as deno from "deno";
import { serve } from "./mod.ts";
import { serve } from "./server.ts";

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

const body = new TextEncoder().encode("Hello World");

async function main(): Promise<void> {
for await (const request of server) {
await request.respond({ status: 200, body });
try {
for await (const request of server) {
await request.responder.respond({ status: 200, body });
}
} catch (e) {
console.log(e.stack);
console.error(e);
}
}

Expand Down
78 changes: 78 additions & 0 deletions http/readers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Reader, ReadResult } from "deno";
import { BufReader } from "../io/bufio.ts";
import { TextProtoReader } from "../textproto/mod.ts";
import { assert } from "../testing/mod.ts";

export class BodyReader implements Reader {
total: number;
bufReader: BufReader;

constructor(reader: Reader, private contentLength: number) {
this.total = 0;
this.bufReader = new BufReader(reader);
}

async read(p: Uint8Array): Promise<ReadResult> {
if (p.length > this.contentLength - this.total) {
const buf = new Uint8Array(this.contentLength - this.total);
const [nread, err] = await this.bufReader.readFull(buf);
if (err && err !== "EOF") {
throw err;
}
p.set(buf);
this.total += nread;
assert.assert(
this.total === this.contentLength,
`${this.total}, ${this.contentLength}`
);
return { nread, eof: true };
} else {
const { nread } = await this.bufReader.read(p);
this.total += nread;
return { nread, eof: false };
}
}
}

export class ChunkedBodyReader implements Reader {
bufReader = new BufReader(this.reader);
tpReader = new TextProtoReader(this.bufReader);

constructor(private reader: Reader) {}

chunks: Uint8Array[] = [];
crlfBuf = new Uint8Array(2);
finished: boolean = false;

async read(p: Uint8Array): Promise<ReadResult> {
const [line, sizeErr] = await this.tpReader.readLine();
if (sizeErr) {
throw sizeErr;
}
const len = parseInt(line, 16);
if (len === 0) {
this.finished = true;
await this.bufReader.readFull(this.crlfBuf);
return { nread: 0, eof: true };
} else {
const buf = new Uint8Array(len);
await this.bufReader.readFull(buf);
await this.bufReader.readFull(this.crlfBuf);
this.chunks.push(buf);
}
const buf = this.chunks[0];
if (buf) {
if (buf.byteLength <= p.byteLength) {
p.set(buf);
this.chunks.shift();
return { nread: buf.byteLength, eof: false };
} else {
p.set(buf.slice(0, p.byteLength));
this.chunks[0] = buf.slice(p.byteLength, buf.byteLength);
return { nread: p.byteLength, eof: false };
}
} else {
return { nread: 0, eof: true };
}
}
}
12 changes: 12 additions & 0 deletions http/readers_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { assert, runTests, test } from "../testing/mod.ts";
import { ChunkedBodyReader } from "./readers.ts";
import { StringReader } from "../io/readers.ts";
import { Buffer, copy } from "deno";

test(async function httpChunkedBodyReader() {
const chunked = "3\r\nabc\r\n5\r\ndefgh\r\n0\r\n\r\n";
const r = new ChunkedBodyReader(new StringReader(chunked));
const w = new Buffer();
await copy(w, r);
assert.equal(w.toString(), "abcdefgh");
});
Loading