Skip to content

Commit

Permalink
Redesign of http server module (denoland/std#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
keroxp authored and ry committed Feb 15, 2019
1 parent 34ece9f commit 57f4e6a
Show file tree
Hide file tree
Showing 11 changed files with 642 additions and 372 deletions.
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

0 comments on commit 57f4e6a

Please sign in to comment.