提交 92455a0b 编写于 作者: R Ryan Dahl
上级 5880827f
......@@ -12,7 +12,13 @@ const MAX_CONSECUTIVE_EMPTY_READS = 100;
const CR = charCode("\r");
const LF = charCode("\n");
export type BufState = null | "EOF" | "BufferFull" | "NoProgress" | Error;
export type BufState =
| null
| "EOF"
| "BufferFull"
| "ShortWrite"
| "NoProgress"
| Error;
/** BufReader implements buffering for a Reader object. */
export class BufReader implements Reader {
......@@ -102,7 +108,7 @@ export class BufReader implements Reader {
* At EOF, the count will be zero and err will be io.EOF.
* To read exactly len(p) bytes, use io.ReadFull(b, p).
*/
async read(p: ArrayBufferView): Promise<ReadResult> {
async read(p: Uint8Array): Promise<ReadResult> {
let rr: ReadResult = { nread: p.byteLength, eof: false };
if (rr.nread === 0) {
if (this.err) {
......@@ -334,7 +340,7 @@ export class BufReader implements Reader {
export class BufWriter implements Writer {
buf: Uint8Array;
n: number = 0;
err: null | Error = null;
err: null | BufState = null;
constructor(private wr: Writer, size = DEFAULT_BUF_SIZE) {
if (size <= 0) {
......@@ -358,16 +364,16 @@ export class BufWriter implements Writer {
}
/** Flush writes any buffered data to the underlying io.Writer. */
async flush(): Promise<void> {
async flush(): Promise<BufState> {
if (this.err != null) {
throw this.err;
return this.err;
}
if (this.n == 0) {
return;
return null;
}
let n: number;
let err: Error = null;
let err: BufState = null;
try {
n = await this.wr.write(this.buf.subarray(0, this.n));
} catch (e) {
......@@ -375,7 +381,7 @@ export class BufWriter implements Writer {
}
if (n < this.n && err == null) {
err = new Error("ShortWrite");
err = "ShortWrite";
}
if (err != null) {
......@@ -384,7 +390,7 @@ export class BufWriter implements Writer {
}
this.n -= n;
this.err = err;
return;
return err;
}
this.n = 0;
}
......
// Fake headers to work around
// https://github.com/denoland/deno/issues/1173
function normalize(name: string, value?: string): [string, string] {
name = String(name).toLowerCase();
value = String(value).trim();
return [name, value];
}
export class Headers {
private map = new Map<string, string>();
get(name: string): string | null {
let [name_] = normalize(name);
return this.map.get(name_);
}
append(name: string, value: string): void {
[name, value] = normalize(name, value);
this.map.set(name, value);
}
toString(): string {
let out = "";
this.map.forEach((v, k) => {
out += `${k}: ${v}\n`;
});
return out;
}
[Symbol.iterator](): IterableIterator<[string, string]> {
return this.map[Symbol.iterator]();
}
}
import { listen, Conn } from "deno";
import { BufReader, BufState } from "./bufio.ts";
import { BufReader, BufState, BufWriter } from "./bufio.ts";
import { TextProtoReader } from "./textproto.ts";
import { Headers } from "./headers.ts";
import { STATUS_TEXT } from "./http_status";
import { assert } from "./util";
export async function* serve(addr: string) {
const listener = listen("tcp", addr);
......@@ -14,9 +15,21 @@ export async function* serve(addr: string) {
export async function* serveConn(c: Conn) {
let bufr = new BufReader(c);
let bufw = new BufWriter(c);
try {
while (true) {
const req = await readRequest(bufr);
const [req, err] = await readRequest(bufr);
if (err == "EOF") {
break;
}
if (err == "ShortWrite") {
console.log("ShortWrite error");
break;
}
if (err) {
throw err;
}
req.w = bufw;
yield req;
}
} finally {
......@@ -26,7 +39,19 @@ export async function* serveConn(c: Conn) {
interface Response {
status?: number;
body: string;
headers?: Headers;
body?: Uint8Array;
}
function setContentLength(r: Response): void {
if (r.body) {
if (!r.headers) {
r.headers = new Headers();
}
if (!r.headers.has("content-length")) {
r.headers.append("Content-Length", r.body.byteLength.toString());
}
}
}
class ServerRequest {
......@@ -34,13 +59,41 @@ class ServerRequest {
method: string;
proto: string;
headers: Headers;
w: BufWriter;
async respond(r: Response): Promise<void> {
const protoMajor = 1;
const protoMinor = 1;
const statusCode = r.status || 200;
const statusText = STATUS_TEXT.get(statusCode);
if (!statusText) {
throw Error("bad status code");
}
respond(r: Response): Promise<void> {
throw Error("not implemented");
let out = `HTTP/${protoMajor}.${protoMinor} ${r.status} ${statusText}\r\n`;
setContentLength(r);
if (r.headers) {
for (let [key, value] of r.headers) {
out += `${key}: ${value}\r\n`;
}
}
out += "\r\n";
const header = new TextEncoder().encode(out);
let n = await this.w.write(header);
assert(header.byteLength == n);
if (r.body) {
n = await this.w.write(r.body);
assert(r.body.byteLength == n);
}
await this.w.flush();
}
}
async function readRequest(b: BufReader): Promise<ServerRequest> {
async function readRequest(b: BufReader): Promise<[ServerRequest, BufState]> {
const tp = new TextProtoReader(b);
const req = new ServerRequest();
......@@ -49,9 +102,12 @@ async function readRequest(b: BufReader): Promise<ServerRequest> {
// First line: GET /index.html HTTP/1.0
[s, err] = await tp.readLine();
if (err) {
return [null, err];
}
[req.method, req.url, req.proto] = s.split(" ", 3);
[req.headers, err] = await tp.readMIMEHeader();
return req;
return [req, err];
}
export enum Status {
Continue = 100, // RFC 7231, 6.2.1
SwitchingProtocols = 101, // RFC 7231, 6.2.2
Processing = 102, // RFC 2518, 10.1
OK = 200, // RFC 7231, 6.3.1
Created = 201, // RFC 7231, 6.3.2
Accepted = 202, // RFC 7231, 6.3.3
NonAuthoritativeInfo = 203, // RFC 7231, 6.3.4
NoContent = 204, // RFC 7231, 6.3.5
ResetContent = 205, // RFC 7231, 6.3.6
PartialContent = 206, // RFC 7233, 4.1
MultiStatus = 207, // RFC 4918, 11.1
AlreadyReported = 208, // RFC 5842, 7.1
IMUsed = 226, // RFC 3229, 10.4.1
MultipleChoices = 300, // RFC 7231, 6.4.1
MovedPermanently = 301, // RFC 7231, 6.4.2
Found = 302, // RFC 7231, 6.4.3
SeeOther = 303, // RFC 7231, 6.4.4
NotModified = 304, // RFC 7232, 4.1
UseProxy = 305, // RFC 7231, 6.4.5
// _ = 306, // RFC 7231, 6.4.6 (Unused)
TemporaryRedirect = 307, // RFC 7231, 6.4.7
PermanentRedirect = 308, // RFC 7538, 3
BadRequest = 400, // RFC 7231, 6.5.1
Unauthorized = 401, // RFC 7235, 3.1
PaymentRequired = 402, // RFC 7231, 6.5.2
Forbidden = 403, // RFC 7231, 6.5.3
NotFound = 404, // RFC 7231, 6.5.4
MethodNotAllowed = 405, // RFC 7231, 6.5.5
NotAcceptable = 406, // RFC 7231, 6.5.6
ProxyAuthRequired = 407, // RFC 7235, 3.2
RequestTimeout = 408, // RFC 7231, 6.5.7
Conflict = 409, // RFC 7231, 6.5.8
Gone = 410, // RFC 7231, 6.5.9
LengthRequired = 411, // RFC 7231, 6.5.10
PreconditionFailed = 412, // RFC 7232, 4.2
RequestEntityTooLarge = 413, // RFC 7231, 6.5.11
RequestURITooLong = 414, // RFC 7231, 6.5.12
UnsupportedMediaType = 415, // RFC 7231, 6.5.13
RequestedRangeNotSatisfiable = 416, // RFC 7233, 4.4
ExpectationFailed = 417, // RFC 7231, 6.5.14
Teapot = 418, // RFC 7168, 2.3.3
MisdirectedRequest = 421, // RFC 7540, 9.1.2
UnprocessableEntity = 422, // RFC 4918, 11.2
Locked = 423, // RFC 4918, 11.3
FailedDependency = 424, // RFC 4918, 11.4
UpgradeRequired = 426, // RFC 7231, 6.5.15
PreconditionRequired = 428, // RFC 6585, 3
TooManyRequests = 429, // RFC 6585, 4
RequestHeaderFieldsTooLarge = 431, // RFC 6585, 5
UnavailableForLegalReasons = 451, // RFC 7725, 3
InternalServerError = 500, // RFC 7231, 6.6.1
NotImplemented = 501, // RFC 7231, 6.6.2
BadGateway = 502, // RFC 7231, 6.6.3
ServiceUnavailable = 503, // RFC 7231, 6.6.4
GatewayTimeout = 504, // RFC 7231, 6.6.5
HTTPVersionNotSupported = 505, // RFC 7231, 6.6.6
VariantAlsoNegotiates = 506, // RFC 2295, 8.1
InsufficientStorage = 507, // RFC 4918, 11.5
LoopDetected = 508, // RFC 5842, 7.2
NotExtended = 510, // RFC 2774, 7
NetworkAuthenticationRequired = 511 // RFC 6585, 6
}
export const STATUS_TEXT = new Map<Status, string>([
[Status.Continue, "Continue"],
[Status.SwitchingProtocols, "Switching Protocols"],
[Status.Processing, "Processing"],
[Status.OK, "OK"],
[Status.Created, "Created"],
[Status.Accepted, "Accepted"],
[Status.NonAuthoritativeInfo, "Non-Authoritative Information"],
[Status.NoContent, "No Content"],
[Status.ResetContent, "Reset Content"],
[Status.PartialContent, "Partial Content"],
[Status.MultiStatus, "Multi-Status"],
[Status.AlreadyReported, "Already Reported"],
[Status.IMUsed, "IM Used"],
[Status.MultipleChoices, "Multiple Choices"],
[Status.MovedPermanently, "Moved Permanently"],
[Status.Found, "Found"],
[Status.SeeOther, "See Other"],
[Status.NotModified, "Not Modified"],
[Status.UseProxy, "Use Proxy"],
[Status.TemporaryRedirect, "Temporary Redirect"],
[Status.PermanentRedirect, "Permanent Redirect"],
[Status.BadRequest, "Bad Request"],
[Status.Unauthorized, "Unauthorized"],
[Status.PaymentRequired, "Payment Required"],
[Status.Forbidden, "Forbidden"],
[Status.NotFound, "Not Found"],
[Status.MethodNotAllowed, "Method Not Allowed"],
[Status.NotAcceptable, "Not Acceptable"],
[Status.ProxyAuthRequired, "Proxy Authentication Required"],
[Status.RequestTimeout, "Request Timeout"],
[Status.Conflict, "Conflict"],
[Status.Gone, "Gone"],
[Status.LengthRequired, "Length Required"],
[Status.PreconditionFailed, "Precondition Failed"],
[Status.RequestEntityTooLarge, "Request Entity Too Large"],
[Status.RequestURITooLong, "Request URI Too Long"],
[Status.UnsupportedMediaType, "Unsupported Media Type"],
[Status.RequestedRangeNotSatisfiable, "Requested Range Not Satisfiable"],
[Status.ExpectationFailed, "Expectation Failed"],
[Status.Teapot, "I'm a teapot"],
[Status.MisdirectedRequest, "Misdirected Request"],
[Status.UnprocessableEntity, "Unprocessable Entity"],
[Status.Locked, "Locked"],
[Status.FailedDependency, "Failed Dependency"],
[Status.UpgradeRequired, "Upgrade Required"],
[Status.PreconditionRequired, "Precondition Required"],
[Status.TooManyRequests, "Too Many Requests"],
[Status.RequestHeaderFieldsTooLarge, "Request Header Fields Too Large"],
[Status.UnavailableForLegalReasons, "Unavailable For Legal Reasons"],
[Status.InternalServerError, "Internal Server Error"],
[Status.NotImplemented, "Not Implemented"],
[Status.BadGateway, "Bad Gateway"],
[Status.ServiceUnavailable, "Service Unavailable"],
[Status.GatewayTimeout, "Gateway Timeout"],
[Status.HTTPVersionNotSupported, "HTTP Version Not Supported"],
[Status.VariantAlsoNegotiates, "Variant Also Negotiates"],
[Status.InsufficientStorage, "Insufficient Storage"],
[Status.LoopDetected, "Loop Detected"],
[Status.NotExtended, "Not Extended"],
[Status.NetworkAuthenticationRequired, "Network Authentication Required"]
]);
......@@ -5,10 +5,11 @@ const addr = "0.0.0.0:8000";
const s = serve(addr);
console.log(`listening on http://${addr}/`);
const body = new TextEncoder().encode("Hello World\n");
async function main() {
for await (const req of s) {
console.log("Req", req);
req.respond({ body: "Hello World\n" });
await req.respond({ status: 200, body });
}
}
......
......@@ -5,7 +5,6 @@
import { BufReader, BufState } from "./bufio.ts";
import { charCode } from "./util.ts";
import { Headers } from "./headers.ts";
const asciiDecoder = new TextDecoder("ascii");
function str(buf: Uint8Array): string {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册