std/http: allow response body to be string (#3705)

This commit is contained in:
Kevin (Kun) "Kassimo" Qian 2020-01-17 15:44:35 -08:00 committed by Ry Dahl
parent 5fa056e53b
commit fc077cd315
4 changed files with 53 additions and 10 deletions

View file

@ -233,6 +233,8 @@ test(async function emptyDirPermission(): Promise<void> {
await Deno.remove(testfolder, { recursive: true }); await Deno.remove(testfolder, { recursive: true });
throw err; throw err;
} }
// Make the test rerunnable
// Otherwise would throw error due to mkdir fail.
await Deno.remove(testfolder, { recursive: true });
// done // done
}); });

View file

@ -2,11 +2,10 @@
```typescript ```typescript
import { serve } from "https://deno.land/std/http/server.ts"; import { serve } from "https://deno.land/std/http/server.ts";
const body = new TextEncoder().encode("Hello World\n");
const s = serve({ port: 8000 }); const s = serve({ port: 8000 });
console.log("http://localhost:8000/"); console.log("http://localhost:8000/");
for await (const req of s) { for await (const req of s) {
req.respond({ body }); req.respond({ body: "Hello World\n" });
} }
``` ```

View file

@ -10,6 +10,8 @@ import { STATUS_TEXT } from "./http_status.ts";
import { assert } from "../testing/asserts.ts"; import { assert } from "../testing/asserts.ts";
import { deferred, Deferred, MuxAsyncIterator } from "../util/async.ts"; import { deferred, Deferred, MuxAsyncIterator } from "../util/async.ts";
const encoder = new TextEncoder();
function bufWriter(w: Writer): BufWriter { function bufWriter(w: Writer): BufWriter {
if (w instanceof BufWriter) { if (w instanceof BufWriter) {
return w; return w;
@ -25,6 +27,7 @@ export function setContentLength(r: Response): void {
if (r.body) { if (r.body) {
if (!r.headers.has("content-length")) { if (!r.headers.has("content-length")) {
// typeof r.body === "string" handled in writeResponse.
if (r.body instanceof Uint8Array) { if (r.body instanceof Uint8Array) {
const bodyLength = r.body.byteLength; const bodyLength = r.body.byteLength;
r.headers.append("Content-Length", bodyLength.toString()); r.headers.append("Content-Length", bodyLength.toString());
@ -37,7 +40,6 @@ export function setContentLength(r: Response): void {
async function writeChunkedBody(w: Writer, r: Reader): Promise<void> { async function writeChunkedBody(w: Writer, r: Reader): Promise<void> {
const writer = bufWriter(w); const writer = bufWriter(w);
const encoder = new TextEncoder();
for await (const chunk of toAsyncIterator(r)) { for await (const chunk of toAsyncIterator(r)) {
if (chunk.byteLength <= 0) continue; if (chunk.byteLength <= 0) continue;
@ -64,6 +66,9 @@ export async function writeResponse(w: Writer, r: Response): Promise<void> {
if (!r.body) { if (!r.body) {
r.body = new Uint8Array(); r.body = new Uint8Array();
} }
if (typeof r.body === "string") {
r.body = encoder.encode(r.body);
}
let out = `HTTP/${protoMajor}.${protoMinor} ${statusCode} ${statusText}\r\n`; let out = `HTTP/${protoMajor}.${protoMinor} ${statusCode} ${statusText}\r\n`;
@ -75,7 +80,7 @@ export async function writeResponse(w: Writer, r: Response): Promise<void> {
} }
out += "\r\n"; out += "\r\n";
const header = new TextEncoder().encode(out); const header = encoder.encode(out);
const n = await writer.write(header); const n = await writer.write(header);
assert(n === header.byteLength); assert(n === header.byteLength);
@ -424,7 +429,7 @@ export class Server implements AsyncIterable<ServerRequest> {
try { try {
await writeResponse(req!.w, { await writeResponse(req!.w, {
status: 400, status: 400,
body: new TextEncoder().encode(`${err.message}\r\n\r\n`) body: encoder.encode(`${err.message}\r\n\r\n`)
}); });
} catch (_) { } catch (_) {
// The connection is destroyed. // The connection is destroyed.
@ -472,7 +477,7 @@ interface ServerConfig {
* Start a HTTP server * Start a HTTP server
* *
* import { serve } from "https://deno.land/std/http/server.ts"; * import { serve } from "https://deno.land/std/http/server.ts";
* const body = new TextEncoder().encode("Hello World\n"); * const body = "Hello World\n";
* const s = serve({ port: 8000 }); * const s = serve({ port: 8000 });
* for await (const req of s) { * for await (const req of s) {
* req.respond({ body }); * req.respond({ body });
@ -505,7 +510,7 @@ export type HTTPSOptions = Omit<Deno.ListenTLSOptions, "transport">;
/** /**
* Create an HTTPS server with given options * Create an HTTPS server with given options
* *
* const body = new TextEncoder().encode("Hello HTTPS"); * const body = "Hello HTTPS";
* const options = { * const options = {
* hostname: "localhost", * hostname: "localhost",
* port: 443, * port: 443,
@ -531,7 +536,7 @@ export function serveTLS(options: HTTPSOptions): Server {
/** /**
* Create an HTTPS server with given options and request handler * Create an HTTPS server with given options and request handler
* *
* const body = new TextEncoder().encode("Hello HTTPS"); * const body = "Hello HTTPS";
* const options = { * const options = {
* hostname: "localhost", * hostname: "localhost",
* port: 443, * port: 443,
@ -556,8 +561,13 @@ export async function listenAndServeTLS(
} }
} }
/**
* Interface of HTTP server response.
* If body is a Reader, response would be chunked.
* If body is a string, it would be UTF-8 encoded by default.
*/
export interface Response { export interface Response {
status?: number; status?: number;
headers?: Headers; headers?: Headers;
body?: Uint8Array | Reader; body?: Uint8Array | Reader | string;
} }

View file

@ -346,6 +346,38 @@ test(async function writeUint8ArrayResponse(): Promise<void> {
assertEquals(eof, Deno.EOF); assertEquals(eof, Deno.EOF);
}); });
test(async function writeStringResponse(): Promise<void> {
const body = "Hello";
const res: Response = { body };
const buf = new Deno.Buffer();
await writeResponse(buf, res);
const decoder = new TextDecoder("utf-8");
const reader = new BufReader(buf);
let r: ReadLineResult;
r = assertNotEOF(await reader.readLine());
assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK");
assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine());
assertEquals(decoder.decode(r.line), `content-length: ${body.length}`);
assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine());
assertEquals(r.line.byteLength, 0);
assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine());
assertEquals(decoder.decode(r.line), body);
assertEquals(r.more, false);
const eof = await reader.readLine();
assertEquals(eof, Deno.EOF);
});
test(async function writeStringReaderResponse(): Promise<void> { test(async function writeStringReaderResponse(): Promise<void> {
const shortText = "Hello"; const shortText = "Hello";