1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-12 00:54:02 -05:00

BREAKING: Remove Deno.EOF, use null instead (#4953)

This commit is contained in:
Nayeem Rahman 2020-04-28 17:40:43 +01:00 committed by GitHub
parent 47c2f034e9
commit 678313b176
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 329 additions and 325 deletions

View file

@ -4,7 +4,7 @@
// Copyright 2009 The Go Authors. All rights reserved. BSD license. // Copyright 2009 The Go Authors. All rights reserved. BSD license.
// https://github.com/golang/go/blob/master/LICENSE // https://github.com/golang/go/blob/master/LICENSE
import { Reader, Writer, EOF, ReaderSync, WriterSync } from "./io.ts"; import { Reader, Writer, ReaderSync, WriterSync } from "./io.ts";
import { assert } from "./util.ts"; import { assert } from "./util.ts";
import { TextDecoder } from "./web/text_encoding.ts"; import { TextDecoder } from "./web/text_encoding.ts";
@ -91,7 +91,7 @@ export class Buffer implements Reader, ReaderSync, Writer, WriterSync {
this.#buf = new Uint8Array(this.#buf.buffer, 0, len); this.#buf = new Uint8Array(this.#buf.buffer, 0, len);
}; };
readSync(p: Uint8Array): number | EOF { readSync(p: Uint8Array): number | null {
if (this.empty()) { if (this.empty()) {
// Buffer is empty, reset to recover space. // Buffer is empty, reset to recover space.
this.reset(); this.reset();
@ -99,14 +99,14 @@ export class Buffer implements Reader, ReaderSync, Writer, WriterSync {
// this edge case is tested in 'bufferReadEmptyAtEOF' test // this edge case is tested in 'bufferReadEmptyAtEOF' test
return 0; return 0;
} }
return EOF; return null;
} }
const nread = copyBytes(p, this.#buf.subarray(this.#off)); const nread = copyBytes(p, this.#buf.subarray(this.#off));
this.#off += nread; this.#off += nread;
return nread; return nread;
} }
read(p: Uint8Array): Promise<number | EOF> { read(p: Uint8Array): Promise<number | null> {
const rr = this.readSync(p); const rr = this.readSync(p);
return Promise.resolve(rr); return Promise.resolve(rr);
} }
@ -169,7 +169,7 @@ export class Buffer implements Reader, ReaderSync, Writer, WriterSync {
this.#reslice(i); this.#reslice(i);
const fub = new Uint8Array(this.#buf.buffer, i); const fub = new Uint8Array(this.#buf.buffer, i);
const nread = await r.read(fub); const nread = await r.read(fub);
if (nread === EOF) { if (nread === null) {
return n; return n;
} }
this.#reslice(i + nread); this.#reslice(i + nread);
@ -188,7 +188,7 @@ export class Buffer implements Reader, ReaderSync, Writer, WriterSync {
this.#reslice(i); this.#reslice(i);
const fub = new Uint8Array(this.#buf.buffer, i); const fub = new Uint8Array(this.#buf.buffer, i);
const nread = r.readSync(fub); const nread = r.readSync(fub);
if (nread === EOF) { if (nread === null) {
return n; return n;
} }
this.#reslice(i + nread); this.#reslice(i + nread);

View file

@ -40,7 +40,6 @@ export { read, readSync, write, writeSync } from "./ops/io.ts";
export { FsEvent, watchFs } from "./ops/fs_events.ts"; export { FsEvent, watchFs } from "./ops/fs_events.ts";
export { internalSymbol as internal } from "./internals.ts"; export { internalSymbol as internal } from "./internals.ts";
export { export {
EOF,
copy, copy,
iter, iter,
iterSync, iterSync,

View file

@ -1,6 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { import {
EOF,
Reader, Reader,
Writer, Writer,
Seeker, Seeker,
@ -76,11 +75,11 @@ export class File
return writeSync(this.rid, p); return writeSync(this.rid, p);
} }
read(p: Uint8Array): Promise<number | EOF> { read(p: Uint8Array): Promise<number | null> {
return read(this.rid, p); return read(this.rid, p);
} }
readSync(p: Uint8Array): number | EOF { readSync(p: Uint8Array): number | null {
return readSync(this.rid, p); return readSync(this.rid, p);
} }
@ -103,11 +102,11 @@ class Stdin implements Reader, ReaderSync, Closer {
this.rid = 0; this.rid = 0;
} }
read(p: Uint8Array): Promise<number | EOF> { read(p: Uint8Array): Promise<number | null> {
return read(this.rid, p); return read(this.rid, p);
} }
readSync(p: Uint8Array): number | EOF { readSync(p: Uint8Array): number | null {
return readSync(this.rid, p); return readSync(this.rid, p);
} }

View file

@ -3,9 +3,6 @@
// Documentation liberally lifted from them too. // Documentation liberally lifted from them too.
// Thank you! We love Go! // Thank you! We love Go!
export const EOF: unique symbol = Symbol("EOF");
export type EOF = typeof EOF;
const DEFAULT_BUFFER_SIZE = 32 * 1024; const DEFAULT_BUFFER_SIZE = 32 * 1024;
// Seek whence values. // Seek whence values.
@ -19,11 +16,11 @@ export enum SeekMode {
// Reader is the interface that wraps the basic read() method. // Reader is the interface that wraps the basic read() method.
// https://golang.org/pkg/io/#Reader // https://golang.org/pkg/io/#Reader
export interface Reader { export interface Reader {
read(p: Uint8Array): Promise<number | EOF>; read(p: Uint8Array): Promise<number | null>;
} }
export interface ReaderSync { export interface ReaderSync {
readSync(p: Uint8Array): number | EOF; readSync(p: Uint8Array): number | null;
} }
// Writer is the interface that wraps the basic write() method. // Writer is the interface that wraps the basic write() method.
@ -65,7 +62,7 @@ export async function copy(
let gotEOF = false; let gotEOF = false;
while (gotEOF === false) { while (gotEOF === false) {
const result = await src.read(b); const result = await src.read(b);
if (result === EOF) { if (result === null) {
gotEOF = true; gotEOF = true;
} else { } else {
n += await dst.write(b.subarray(0, result)); n += await dst.write(b.subarray(0, result));
@ -84,7 +81,7 @@ export async function* iter(
const b = new Uint8Array(bufSize); const b = new Uint8Array(bufSize);
while (true) { while (true) {
const result = await r.read(b); const result = await r.read(b);
if (result === EOF) { if (result === null) {
break; break;
} }
@ -102,7 +99,7 @@ export function* iterSync(
const b = new Uint8Array(bufSize); const b = new Uint8Array(bufSize);
while (true) { while (true) {
const result = r.readSync(b); const result = r.readSync(b);
if (result === EOF) { if (result === null) {
break; break;
} }

View file

@ -361,10 +361,6 @@ declare namespace Deno {
*/ */
export function umask(mask?: number): number; export function umask(mask?: number): number;
/** **UNSTABLE**: might be removed in favor of `null` (#3932). */
export const EOF: unique symbol;
export type EOF = typeof EOF;
export enum SeekMode { export enum SeekMode {
Start = 0, Start = 0,
Current = 1, Current = 1,
@ -379,20 +375,21 @@ declare namespace Deno {
* available but not `p.byteLength` bytes, `read()` conventionally resolves * available but not `p.byteLength` bytes, `read()` conventionally resolves
* to what is available instead of waiting for more. * to what is available instead of waiting for more.
* *
* When `read()` encounters end-of-file condition, it resolves to * When `read()` encounters end-of-file condition, it resolves to EOF
* `Deno.EOF` symbol. * (`null`).
* *
* When `read()` encounters an error, it rejects with an error. * When `read()` encounters an error, it rejects with an error.
* *
* Callers should always process the `n` > `0` bytes returned before * Callers should always process the `n` > `0` bytes returned before
* considering the `EOF`. Doing so correctly handles I/O errors that happen * considering the EOF (`null`). Doing so correctly handles I/O errors that
* after reading some bytes and also both of the allowed EOF behaviors. * happen after reading some bytes and also both of the allowed EOF
* behaviors.
* *
* Implementations should not retain a reference to `p`. * Implementations should not retain a reference to `p`.
* *
* Use Deno.iter() to turn a Reader into an AsyncIterator. * Use Deno.iter() to turn a Reader into an AsyncIterator.
*/ */
read(p: Uint8Array): Promise<number | EOF>; read(p: Uint8Array): Promise<number | null>;
} }
export interface ReaderSync { export interface ReaderSync {
@ -403,20 +400,20 @@ declare namespace Deno {
* but not `p.byteLength` bytes, `read()` conventionally returns what is * but not `p.byteLength` bytes, `read()` conventionally returns what is
* available instead of waiting for more. * available instead of waiting for more.
* *
* When `readSync()` encounters end-of-file condition, it returns `Deno.EOF` * When `readSync()` encounters end-of-file condition, it returns EOF
* symbol. * (`null`).
* *
* When `readSync()` encounters an error, it throws with an error. * When `readSync()` encounters an error, it throws with an error.
* *
* Callers should always process the `n` > `0` bytes returned before * Callers should always process the `n` > `0` bytes returned before
* considering the `EOF`. Doing so correctly handles I/O errors that happen * considering the EOF (`null`). Doing so correctly handles I/O errors that happen
* after reading some bytes and also both of the allowed EOF behaviors. * after reading some bytes and also both of the allowed EOF behaviors.
* *
* Implementations should not retain a reference to `p`. * Implementations should not retain a reference to `p`.
* *
* Use Deno.iterSync() to turn a ReaderSync into an Iterator. * Use Deno.iterSync() to turn a ReaderSync into an Iterator.
*/ */
readSync(p: Uint8Array): number | EOF; readSync(p: Uint8Array): number | null;
} }
export interface Writer { export interface Writer {
@ -477,8 +474,8 @@ declare namespace Deno {
seekSync(offset: number, whence: SeekMode): number; seekSync(offset: number, whence: SeekMode): number;
} }
/** Copies from `src` to `dst` until either `EOF` is reached on `src` or an /** Copies from `src` to `dst` until either EOF (`null`) is read from `src` or
* error occurs. It resolves to the number of bytes copied or rejects with * an error occurs. It resolves to the number of bytes copied or rejects with
* the first error encountered while copying. * the first error encountered while copying.
* *
* const source = await Deno.open("my_file.txt"); * const source = await Deno.open("my_file.txt");
@ -486,9 +483,6 @@ declare namespace Deno {
* const bytesCopied1 = await Deno.copy(source, Deno.stdout); * const bytesCopied1 = await Deno.copy(source, Deno.stdout);
* const bytesCopied2 = await Deno.copy(source, buffer); * const bytesCopied2 = await Deno.copy(source, buffer);
* *
* Because `copy()` is defined to read from `src` until `EOF`, it does not
* treat an `EOF` from `read()` as an error to be reported.
*
* @param src The source to copy from * @param src The source to copy from
* @param dst The destination to copy to * @param dst The destination to copy to
* @param options Can be used to tune size of the buffer. Default size is 32kB * @param options Can be used to tune size of the buffer. Default size is 32kB
@ -611,8 +605,11 @@ declare namespace Deno {
/** Synchronously read from a resource ID (`rid`) into an array buffer (`buffer`). /** Synchronously read from a resource ID (`rid`) into an array buffer (`buffer`).
* *
* Returns either the number of bytes read during the operation or End Of File * Returns either the number of bytes read during the operation or EOF
* (`Symbol(EOF)`) if there was nothing to read. * (`null`) if there was nothing more to read.
*
* It is possible for a read to successfully return with `0` bytes. This does
* not indicate EOF.
* *
* // if "/foo/bar.txt" contains the text "hello world": * // if "/foo/bar.txt" contains the text "hello world":
* const file = Deno.openSync("/foo/bar.txt"); * const file = Deno.openSync("/foo/bar.txt");
@ -621,12 +618,15 @@ declare namespace Deno {
* const text = new TextDecoder().decode(buf); // "hello world" * const text = new TextDecoder().decode(buf); // "hello world"
* Deno.close(file.rid); * Deno.close(file.rid);
*/ */
export function readSync(rid: number, buffer: Uint8Array): number | EOF; export function readSync(rid: number, buffer: Uint8Array): number | null;
/** Read from a resource ID (`rid`) into an array buffer (`buffer`). /** Read from a resource ID (`rid`) into an array buffer (`buffer`).
* *
* Resolves to either the number of bytes read during the operation or End Of * Resolves to either the number of bytes read during the operation or EOF
* File (`Symbol(EOF)`) if there was nothing to read. * (`null`) if there was nothing more to read.
*
* It is possible for a read to successfully return with `0` bytes. This does
* not indicate EOF.
* *
* // if "/foo/bar.txt" contains the text "hello world": * // if "/foo/bar.txt" contains the text "hello world":
* const file = await Deno.open("/foo/bar.txt"); * const file = await Deno.open("/foo/bar.txt");
@ -635,7 +635,7 @@ declare namespace Deno {
* const text = new TextDecoder().decode(buf); // "hello world" * const text = new TextDecoder().decode(buf); // "hello world"
* Deno.close(file.rid); * Deno.close(file.rid);
*/ */
export function read(rid: number, buffer: Uint8Array): Promise<number | EOF>; export function read(rid: number, buffer: Uint8Array): Promise<number | null>;
/** Synchronously write to the resource ID (`rid`) the contents of the array /** Synchronously write to the resource ID (`rid`) the contents of the array
* buffer (`data`). * buffer (`data`).
@ -743,8 +743,8 @@ declare namespace Deno {
constructor(rid: number); constructor(rid: number);
write(p: Uint8Array): Promise<number>; write(p: Uint8Array): Promise<number>;
writeSync(p: Uint8Array): number; writeSync(p: Uint8Array): number;
read(p: Uint8Array): Promise<number | EOF>; read(p: Uint8Array): Promise<number | null>;
readSync(p: Uint8Array): number | EOF; readSync(p: Uint8Array): number | null;
seek(offset: number, whence: SeekMode): Promise<number>; seek(offset: number, whence: SeekMode): Promise<number>;
seekSync(offset: number, whence: SeekMode): number; seekSync(offset: number, whence: SeekMode): number;
close(): void; close(): void;
@ -863,12 +863,12 @@ declare namespace Deno {
reset(): void; reset(): void;
/** Reads the next `p.length` bytes from the buffer or until the buffer is /** Reads the next `p.length` bytes from the buffer or until the buffer is
* drained. Returns the number of bytes read. If the buffer has no data to * drained. Returns the number of bytes read. If the buffer has no data to
* return, the return is `Deno.EOF`. */ * return, the return is EOF (`null`). */
readSync(p: Uint8Array): number | EOF; readSync(p: Uint8Array): number | null;
/** Reads the next `p.length` bytes from the buffer or until the buffer is /** Reads the next `p.length` bytes from the buffer or until the buffer is
* drained. Resolves to the number of bytes read. If the buffer has no * drained. Resolves to the number of bytes read. If the buffer has no
* data to return, resolves to `Deno.EOF`. */ * data to return, resolves to EOF (`null`). */
read(p: Uint8Array): Promise<number | EOF>; read(p: Uint8Array): Promise<number | null>;
writeSync(p: Uint8Array): number; writeSync(p: Uint8Array): number;
write(p: Uint8Array): Promise<number>; write(p: Uint8Array): Promise<number>;
/** Grows the buffer's capacity, if necessary, to guarantee space for /** Grows the buffer's capacity, if necessary, to guarantee space for
@ -879,14 +879,14 @@ declare namespace Deno {
* Based on Go Lang's * Based on Go Lang's
* [Buffer.Grow](https://golang.org/pkg/bytes/#Buffer.Grow). */ * [Buffer.Grow](https://golang.org/pkg/bytes/#Buffer.Grow). */
grow(n: number): void; grow(n: number): void;
/** Reads data from `r` until `Deno.EOF` and appends it to the buffer, /** Reads data from `r` until EOF (`null`) and appends it to the buffer,
* growing the buffer as needed. It resolves to the number of bytes read. * growing the buffer as needed. It resolves to the number of bytes read.
* If the buffer becomes too large, `.readFrom()` will reject with an error. * If the buffer becomes too large, `.readFrom()` will reject with an error.
* *
* Based on Go Lang's * Based on Go Lang's
* [Buffer.ReadFrom](https://golang.org/pkg/bytes/#Buffer.ReadFrom). */ * [Buffer.ReadFrom](https://golang.org/pkg/bytes/#Buffer.ReadFrom). */
readFrom(r: Reader): Promise<number>; readFrom(r: Reader): Promise<number>;
/** Reads data from `r` until `Deno.EOF` and appends it to the buffer, /** Reads data from `r` until EOF (`null`) and appends it to the buffer,
* growing the buffer as needed. It returns the number of bytes read. If the * growing the buffer as needed. It returns the number of bytes read. If the
* buffer becomes too large, `.readFromSync()` will throw an error. * buffer becomes too large, `.readFromSync()` will throw an error.
* *
@ -895,8 +895,8 @@ declare namespace Deno {
readFromSync(r: ReaderSync): number; readFromSync(r: ReaderSync): number;
} }
/** Read Reader `r` until end of file (`Deno.EOF`) and resolve to the content /** Read Reader `r` until EOF (`null`) and resolve to the content as
* as `Uint8Array`. * Uint8Array`.
* *
* // Example from stdin * // Example from stdin
* const stdinContent = await Deno.readAll(Deno.stdin); * const stdinContent = await Deno.readAll(Deno.stdin);
@ -914,8 +914,8 @@ declare namespace Deno {
*/ */
export function readAll(r: Reader): Promise<Uint8Array>; export function readAll(r: Reader): Promise<Uint8Array>;
/** Synchronously reads Reader `r` until end of file (`Deno.EOF`) and returns /** Synchronously reads Reader `r` until EOF (`null`) and returns the content
* the content as `Uint8Array`. * as `Uint8Array`.
* *
* // Example from stdin * // Example from stdin
* const stdinContent = Deno.readAllSync(Deno.stdin); * const stdinContent = Deno.readAllSync(Deno.stdin);
@ -2183,13 +2183,13 @@ declare namespace Deno {
readonly stderr?: Reader & Closer; readonly stderr?: Reader & Closer;
/** Resolves to the current status of the process. */ /** Resolves to the current status of the process. */
status(): Promise<ProcessStatus>; status(): Promise<ProcessStatus>;
/** Buffer the stdout and return it as `Uint8Array` after `Deno.EOF`. /** Buffer the stdout until EOF and return it as `Uint8Array`.
* *
* You must set stdout to `"piped"` when creating the process. * You must set stdout to `"piped"` when creating the process.
* *
* This calls `close()` on stdout after its done. */ * This calls `close()` on stdout after its done. */
output(): Promise<Uint8Array>; output(): Promise<Uint8Array>;
/** Buffer the stderr and return it as `Uint8Array` after `Deno.EOF`. /** Buffer the stderr until EOF and return it as `Uint8Array`.
* *
* You must set stderr to `"piped"` when creating the process. * You must set stderr to `"piped"` when creating the process.
* *

View file

@ -1,6 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { errors } from "./errors.ts"; import { errors } from "./errors.ts";
import { EOF, Reader, Writer, Closer } from "./io.ts"; import { Reader, Writer, Closer } from "./io.ts";
import { read, write } from "./ops/io.ts"; import { read, write } from "./ops/io.ts";
import { close } from "./ops/resources.ts"; import { close } from "./ops/resources.ts";
import * as netOps from "./ops/net.ts"; import * as netOps from "./ops/net.ts";
@ -40,7 +40,7 @@ export class ConnImpl implements Conn {
return write(this.rid, p); return write(this.rid, p);
} }
read(p: Uint8Array): Promise<number | EOF> { read(p: Uint8Array): Promise<number | null> {
return read(this.rid, p); return read(this.rid, p);
} }

View file

@ -1,7 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { sendAsyncMinimal, sendSyncMinimal } from "./dispatch_minimal.ts"; import { sendAsyncMinimal, sendSyncMinimal } from "./dispatch_minimal.ts";
import { EOF } from "../io.ts";
// TODO(bartlomieju): remove this import and maybe lazy-initialize // TODO(bartlomieju): remove this import and maybe lazy-initialize
// OPS_CACHE that belongs only to this module // OPS_CACHE that belongs only to this module
import { OPS_CACHE } from "../runtime.ts"; import { OPS_CACHE } from "../runtime.ts";
@ -10,7 +9,7 @@ import { OPS_CACHE } from "../runtime.ts";
let OP_READ = -1; let OP_READ = -1;
let OP_WRITE = -1; let OP_WRITE = -1;
export function readSync(rid: number, buffer: Uint8Array): number | EOF { export function readSync(rid: number, buffer: Uint8Array): number | null {
if (buffer.length == 0) { if (buffer.length == 0) {
return 0; return 0;
} }
@ -21,7 +20,7 @@ export function readSync(rid: number, buffer: Uint8Array): number | EOF {
if (nread < 0) { if (nread < 0) {
throw new Error("read error"); throw new Error("read error");
} else if (nread == 0) { } else if (nread == 0) {
return EOF; return null;
} else { } else {
return nread; return nread;
} }
@ -30,7 +29,7 @@ export function readSync(rid: number, buffer: Uint8Array): number | EOF {
export async function read( export async function read(
rid: number, rid: number,
buffer: Uint8Array buffer: Uint8Array
): Promise<number | EOF> { ): Promise<number | null> {
if (buffer.length == 0) { if (buffer.length == 0) {
return 0; return 0;
} }
@ -41,7 +40,7 @@ export async function read(
if (nread < 0) { if (nread < 0) {
throw new Error("read error"); throw new Error("read error");
} else if (nread == 0) { } else if (nread == 0) {
return EOF; return null;
} else { } else {
return nread; return nread;
} }

View file

@ -65,7 +65,7 @@ async function empty(buf: Buffer, s: string, fub: Uint8Array): Promise<void> {
check(buf, s); check(buf, s);
while (true) { while (true) {
const r = await buf.read(fub); const r = await buf.read(fub);
if (r === Deno.EOF) { if (r === null) {
break; break;
} }
s = s.slice(r); s = s.slice(r);
@ -231,8 +231,7 @@ unitTest(async function bufferTestGrow(): Promise<void> {
for (const growLen of [0, 100, 1000, 10000, 100000]) { for (const growLen of [0, 100, 1000, 10000, 100000]) {
const buf = new Buffer(xBytes.buffer as ArrayBuffer); const buf = new Buffer(xBytes.buffer as ArrayBuffer);
// If we read, this affects buf.off, which is good to test. // If we read, this affects buf.off, which is good to test.
const result = await buf.read(tmp); const nread = (await buf.read(tmp)) ?? 0;
const nread = result === Deno.EOF ? 0 : result;
buf.grow(growLen); buf.grow(growLen);
const yBytes = repeat("y", growLen); const yBytes = repeat("y", growLen);
await buf.write(yBytes); await buf.write(yBytes);

View file

@ -101,13 +101,13 @@ unitTest(async function readerIter(): Promise<void> {
this.#buf = new Uint8Array(encoder.encode(s)); this.#buf = new Uint8Array(encoder.encode(s));
} }
read(p: Uint8Array): Promise<number | Deno.EOF> { read(p: Uint8Array): Promise<number | null> {
const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset);
p.set(this.#buf.slice(this.#offset, this.#offset + n)); p.set(this.#buf.slice(this.#offset, this.#offset + n));
this.#offset += n; this.#offset += n;
if (n === 0) { if (n === 0) {
return Promise.resolve(Deno.EOF); return Promise.resolve(null);
} }
return Promise.resolve(n); return Promise.resolve(n);
@ -136,13 +136,13 @@ unitTest(async function readerIterSync(): Promise<void> {
this.#buf = new Uint8Array(encoder.encode(s)); this.#buf = new Uint8Array(encoder.encode(s));
} }
readSync(p: Uint8Array): number | Deno.EOF { readSync(p: Uint8Array): number | null {
const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset);
p.set(this.#buf.slice(this.#offset, this.#offset + n)); p.set(this.#buf.slice(this.#offset, this.#offset + n));
this.#offset += n; this.#offset += n;
if (n === 0) { if (n === 0) {
return Deno.EOF; return null;
} }
return n; return n;

View file

@ -19,7 +19,7 @@ function spyRead(obj: Deno.Buffer): Spy {
const orig = obj.read.bind(obj); const orig = obj.read.bind(obj);
obj.read = (p: Uint8Array): Promise<number | Deno.EOF> => { obj.read = (p: Uint8Array): Promise<number | null> => {
spy.calls++; spy.calls++;
return orig(p); return orig(p);
}; };

View file

@ -179,10 +179,10 @@ unitTest({ perms: { net: true } }, async function netTcpDialListen(): Promise<
assertEquals(3, buf[2]); assertEquals(3, buf[2]);
assert(conn.rid > 0); assert(conn.rid > 0);
assert(readResult !== Deno.EOF); assert(readResult !== null);
const readResult2 = await conn.read(buf); const readResult2 = await conn.read(buf);
assertEquals(Deno.EOF, readResult2); assertEquals(readResult2, null);
listener.close(); listener.close();
conn.close(); conn.close();
@ -214,10 +214,10 @@ unitTest(
assertEquals(3, buf[2]); assertEquals(3, buf[2]);
assert(conn.rid > 0); assert(conn.rid > 0);
assert(readResult !== Deno.EOF); assert(readResult !== null);
const readResult2 = await conn.read(buf); const readResult2 = await conn.read(buf);
assertEquals(Deno.EOF, readResult2); assertEquals(readResult2, null);
listener.close(); listener.close();
conn.close(); conn.close();
@ -358,10 +358,10 @@ unitTest(
assertEquals(3, buf[2]); assertEquals(3, buf[2]);
assert(conn.rid > 0); assert(conn.rid > 0);
assert(readResult !== Deno.EOF); assert(readResult !== null);
const readResult2 = await conn.read(buf); const readResult2 = await conn.read(buf);
assertEquals(Deno.EOF, readResult2); assertEquals(readResult2, null);
listener.close(); listener.close();
conn.close(); conn.close();
@ -396,7 +396,7 @@ unitTest(
closeReadDeferred.resolve(); closeReadDeferred.resolve();
const buf = new Uint8Array(1024); const buf = new Uint8Array(1024);
const readResult = await conn.read(buf); const readResult = await conn.read(buf);
assertEquals(Deno.EOF, readResult); // with immediate EOF assertEquals(readResult, null); // with immediate EOF
// Ensure closeRead does not impact write // Ensure closeRead does not impact write
await conn.write(new Uint8Array([4, 5, 6])); await conn.write(new Uint8Array([4, 5, 6]));
await closeDeferred; await closeDeferred;
@ -523,7 +523,7 @@ unitTest(
try { try {
while (true) { while (true) {
const nread = await conn.read(p); const nread = await conn.read(p);
if (nread === Deno.EOF) { if (nread === null) {
break; break;
} }
await conn.write(new Uint8Array([1, 2, 3])); await conn.write(new Uint8Array([1, 2, 3]));

View file

@ -154,14 +154,14 @@ unitTest({ perms: { run: true } }, async function runStdoutPiped(): Promise<
const data = new Uint8Array(10); const data = new Uint8Array(10);
let r = await p.stdout!.read(data); let r = await p.stdout!.read(data);
if (r === Deno.EOF) { if (r === null) {
throw new Error("p.stdout.read(...) should not be EOF"); throw new Error("p.stdout.read(...) should not be null");
} }
assertEquals(r, 5); assertEquals(r, 5);
const s = new TextDecoder().decode(data.subarray(0, r)); const s = new TextDecoder().decode(data.subarray(0, r));
assertEquals(s, "hello"); assertEquals(s, "hello");
r = await p.stdout!.read(data); r = await p.stdout!.read(data);
assertEquals(r, Deno.EOF); assertEquals(r, null);
p.stdout!.close(); p.stdout!.close();
const status = await p.status(); const status = await p.status();
@ -183,14 +183,14 @@ unitTest({ perms: { run: true } }, async function runStderrPiped(): Promise<
const data = new Uint8Array(10); const data = new Uint8Array(10);
let r = await p.stderr!.read(data); let r = await p.stderr!.read(data);
if (r === Deno.EOF) { if (r === null) {
throw new Error("p.stderr.read should not return EOF here"); throw new Error("p.stderr.read should not return null here");
} }
assertEquals(r, 5); assertEquals(r, 5);
const s = new TextDecoder().decode(data.subarray(0, r)); const s = new TextDecoder().decode(data.subarray(0, r));
assertEquals(s, "hello"); assertEquals(s, "hello");
r = await p.stderr!.read(data); r = await p.stderr!.read(data);
assertEquals(r, Deno.EOF); assertEquals(r, null);
p.stderr!.close(); p.stderr!.close();
const status = await p.status(); const status = await p.status();
@ -313,7 +313,7 @@ unitTest({ perms: { run: true } }, async function runClose(): Promise<void> {
const data = new Uint8Array(10); const data = new Uint8Array(10);
const r = await p.stderr!.read(data); const r = await p.stderr!.read(data);
assertEquals(r, Deno.EOF); assertEquals(r, null);
p.stderr!.close(); p.stderr!.close();
}); });

View file

@ -191,7 +191,7 @@ unitTest(
await w.flush(); await w.flush();
const tpr = new TextProtoReader(r); const tpr = new TextProtoReader(r);
const statusLine = await tpr.readLine(); const statusLine = await tpr.readLine();
assert(statusLine !== Deno.EOF, `line must be read: ${String(statusLine)}`); assert(statusLine !== null, `line must be read: ${String(statusLine)}`);
const m = statusLine.match(/^(.+?) (.+?) (.+?)$/); const m = statusLine.match(/^(.+?) (.+?) (.+?)$/);
assert(m !== null, "must be matched"); assert(m !== null, "must be matched");
const [_, proto, status, ok] = m; const [_, proto, status, ok] = m;
@ -199,7 +199,7 @@ unitTest(
assertEquals(status, "200"); assertEquals(status, "200");
assertEquals(ok, "OK"); assertEquals(ok, "OK");
const headers = await tpr.readMIMEHeader(); const headers = await tpr.readMIMEHeader();
assert(headers !== Deno.EOF); assert(headers !== null);
const contentLength = parseInt(headers.get("content-length")!); const contentLength = parseInt(headers.get("content-length")!);
const bodyBuf = new Uint8Array(contentLength); const bodyBuf = new Uint8Array(contentLength);
await r.readFull(bodyBuf); await r.readFull(bodyBuf);
@ -225,7 +225,7 @@ unitTest(
let writer = new BufWriter(conn); let writer = new BufWriter(conn);
let reader = new TextProtoReader(new BufReader(conn)); let reader = new TextProtoReader(new BufReader(conn));
let line: string | Deno.EOF = (await reader.readLine()) as string; let line: string | null = (await reader.readLine()) as string;
assert(line.startsWith("220")); assert(line.startsWith("220"));
await writer.write(encoder.encode(`EHLO ${hostname}\r\n`)); await writer.write(encoder.encode(`EHLO ${hostname}\r\n`));

View file

@ -220,7 +220,7 @@ class Body
return decoder.decode(ab); return decoder.decode(ab);
} }
read(p: Uint8Array): Promise<number | io.EOF> { read(p: Uint8Array): Promise<number | null> {
this.#bodyUsed = true; this.#bodyUsed = true;
return read(this.#rid, p); return read(this.#rid, p);
} }

View file

@ -6,7 +6,7 @@ Deno.stdout.writeSync(new TextEncoder().encode("S"));
const buf = new Uint8Array(3); const buf = new Uint8Array(3);
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
const nread = await Deno.stdin.read(buf); const nread = await Deno.stdin.read(buf);
if (nread === Deno.EOF) { if (nread === null) {
break; break;
} else { } else {
const data = new TextDecoder().decode(buf.subarray(0, nread)); const data = new TextDecoder().decode(buf.subarray(0, nread));

View file

@ -41,12 +41,12 @@ class FileReader implements Deno.Reader {
constructor(private filePath: string) {} constructor(private filePath: string) {}
public async read(p: Uint8Array): Promise<number | Deno.EOF> { public async read(p: Uint8Array): Promise<number | null> {
if (!this.file) { if (!this.file) {
this.file = await Deno.open(this.filePath, { read: true }); this.file = await Deno.open(this.filePath, { read: true });
} }
const res = await Deno.read(this.file.rid, p); const res = await Deno.read(this.file.rid, p);
if (res === Deno.EOF) { if (res === null) {
Deno.close(this.file.rid); Deno.close(this.file.rid);
this.file = undefined; this.file = undefined;
} }

View file

@ -17,8 +17,8 @@ Available Functions:
```typescript ```typescript
sizeof(dataType: RawTypes): number sizeof(dataType: RawTypes): number
getNBytes(r: Deno.Reader, n: number): Promise<Uint8Array> getNBytes(r: Deno.Reader, n: number): Promise<Uint8Array>
varnum(b: Uint8Array, o: VarnumOptions = {}): number | Deno.EOF varnum(b: Uint8Array, o: VarnumOptions = {}): number | null
varbig(b: Uint8Array, o: VarbigOptions = {}): bigint | Deno.EOF varbig(b: Uint8Array, o: VarbigOptions = {}): bigint | null
putVarnum(b: Uint8Array, x: number, o: VarnumOptions = {}): number putVarnum(b: Uint8Array, x: number, o: VarnumOptions = {}): number
putVarbig(b: Uint8Array, x: bigint, o: VarbigOptions = {}): number putVarbig(b: Uint8Array, x: bigint, o: VarbigOptions = {}): number
readVarnum(r: Deno.Reader, o: VarnumOptions = {}): Promise<number> readVarnum(r: Deno.Reader, o: VarnumOptions = {}): Promise<number>

View file

@ -51,19 +51,16 @@ export async function getNBytes(
): Promise<Uint8Array> { ): Promise<Uint8Array> {
const scratch = new Uint8Array(n); const scratch = new Uint8Array(n);
const nRead = await r.read(scratch); const nRead = await r.read(scratch);
if (nRead === Deno.EOF || nRead < n) throw new Deno.errors.UnexpectedEof(); if (nRead === null || nRead < n) throw new Deno.errors.UnexpectedEof();
return scratch; return scratch;
} }
/** Decode a number from `b`, and return it as a `number`. Data-type defaults to `int32`. /** Decode a number from `b`, and return it as a `number`. Data-type defaults to `int32`.
* Returns `EOF` if `b` is too short for the data-type given in `o`. */ * Returns `null` if `b` is too short for the data-type given in `o`. */
export function varnum( export function varnum(b: Uint8Array, o: VarnumOptions = {}): number | null {
b: Uint8Array,
o: VarnumOptions = {}
): number | Deno.EOF {
o.dataType = o.dataType ?? "int32"; o.dataType = o.dataType ?? "int32";
const littleEndian = (o.endian ?? "big") === "little" ? true : false; const littleEndian = (o.endian ?? "big") === "little" ? true : false;
if (b.length < sizeof(o.dataType)) return Deno.EOF; if (b.length < sizeof(o.dataType)) return null;
const view = new DataView(b.buffer); const view = new DataView(b.buffer);
switch (o.dataType) { switch (o.dataType) {
case "int8": case "int8":
@ -86,14 +83,11 @@ export function varnum(
} }
/** Decode an integer from `b`, and return it as a `bigint`. Data-type defaults to `int64`. /** Decode an integer from `b`, and return it as a `bigint`. Data-type defaults to `int64`.
* Returns `EOF` if `b` is too short for the data-type given in `o`. */ * Returns `null` if `b` is too short for the data-type given in `o`. */
export function varbig( export function varbig(b: Uint8Array, o: VarbigOptions = {}): bigint | null {
b: Uint8Array,
o: VarbigOptions = {}
): bigint | Deno.EOF {
o.dataType = o.dataType ?? "int64"; o.dataType = o.dataType ?? "int64";
const littleEndian = (o.endian ?? "big") === "little" ? true : false; const littleEndian = (o.endian ?? "big") === "little" ? true : false;
if (b.length < sizeof(o.dataType)) return Deno.EOF; if (b.length < sizeof(o.dataType)) return null;
const view = new DataView(b.buffer); const view = new DataView(b.buffer);
switch (o.dataType) { switch (o.dataType) {
case "int8": case "int8":

View file

@ -64,12 +64,12 @@ async function readRecord(
Startline: number, Startline: number,
reader: BufReader, reader: BufReader,
opt: ReadOptions = { comma: ",", trimLeadingSpace: false } opt: ReadOptions = { comma: ",", trimLeadingSpace: false }
): Promise<string[] | Deno.EOF> { ): Promise<string[] | null> {
const tp = new TextProtoReader(reader); const tp = new TextProtoReader(reader);
const lineIndex = Startline; const lineIndex = Startline;
let line = await readLine(tp); let line = await readLine(tp);
if (line === Deno.EOF) return Deno.EOF; if (line === null) return null;
if (line.length === 0) { if (line.length === 0) {
return []; return [];
} }
@ -147,7 +147,7 @@ async function readRecord(
// Hit end of line (copy all data so far). // Hit end of line (copy all data so far).
recordBuffer += line; recordBuffer += line;
const r = await readLine(tp); const r = await readLine(tp);
if (r === Deno.EOF) { if (r === null) {
if (!opt.lazyQuotes) { if (!opt.lazyQuotes) {
quoteError = ERR_QUOTE; quoteError = ERR_QUOTE;
break parseField; break parseField;
@ -182,13 +182,13 @@ async function readRecord(
} }
async function isEOF(tp: TextProtoReader): Promise<boolean> { async function isEOF(tp: TextProtoReader): Promise<boolean> {
return (await tp.r.peek(0)) === Deno.EOF; return (await tp.r.peek(0)) === null;
} }
async function readLine(tp: TextProtoReader): Promise<string | Deno.EOF> { async function readLine(tp: TextProtoReader): Promise<string | null> {
let line: string; let line: string;
const r = await tp.readLine(); const r = await tp.readLine();
if (r === Deno.EOF) return Deno.EOF; if (r === null) return null;
line = r; line = r;
// For backwards compatibility, drop trailing \r before EOF. // For backwards compatibility, drop trailing \r before EOF.
@ -226,7 +226,7 @@ export async function readMatrix(
for (;;) { for (;;) {
const r = await readRecord(lineIndex, reader, opt); const r = await readRecord(lineIndex, reader, opt);
if (r === Deno.EOF) break; if (r === null) break;
lineResult = r; lineResult = r;
lineIndex++; lineIndex++;
// If fieldsPerRecord is 0, Read sets it to // If fieldsPerRecord is 0, Read sets it to

View file

@ -17,7 +17,7 @@ async function startServer(): Promise<Deno.Process> {
assert(server.stdout != null); assert(server.stdout != null);
const r = new TextProtoReader(new BufReader(server.stdout)); const r = new TextProtoReader(new BufReader(server.stdout));
const s = await r.readLine(); const s = await r.readLine();
assert(s !== Deno.EOF && s.includes("chat server starting")); assert(s !== null && s.includes("chat server starting"));
} catch (err) { } catch (err) {
server.stdout!.close(); server.stdout!.close();
server.close(); server.close();

View file

@ -1,6 +1,6 @@
#!/usr/bin/env -S deno --allow-net --allow-env #!/usr/bin/env -S deno --allow-net --allow-env
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { parse } from "https://deno.land/std/flags/mod.ts"; import { parse } from "../flags/mod.ts";
function pathBase(p: string): string { function pathBase(p: string): string {
const parts = p.split("/"); const parts = p.split("/");

View file

@ -16,7 +16,7 @@ Deno.test("[examples/echo_server]", async () => {
const processReader = new BufReader(process.stdout!); const processReader = new BufReader(process.stdout!);
const message = await processReader.readLine(); const message = await processReader.readLine();
assertNotEquals(message, Deno.EOF); assertNotEquals(message, null);
assertStrictEq( assertStrictEq(
decoder.decode((message as ReadLineResult).line).trim(), decoder.decode((message as ReadLineResult).line).trim(),
"Listening on 0.0.0.0:8080" "Listening on 0.0.0.0:8080"
@ -28,7 +28,7 @@ Deno.test("[examples/echo_server]", async () => {
await conn.write(encoder.encode("Hello echo_server\n")); await conn.write(encoder.encode("Hello echo_server\n"));
const result = await connReader.readLine(); const result = await connReader.readLine();
assertNotEquals(result, Deno.EOF); assertNotEquals(result, null);
const actualResponse = decoder const actualResponse = decoder
.decode((result as ReadLineResult).line) .decode((result as ReadLineResult).line)

View file

@ -25,7 +25,7 @@ async function startFileServer(): Promise<void> {
assert(fileServer.stdout != null); assert(fileServer.stdout != null);
const r = new TextProtoReader(new BufReader(fileServer.stdout)); const r = new TextProtoReader(new BufReader(fileServer.stdout));
const s = await r.readLine(); const s = await r.readLine();
assert(s !== Deno.EOF && s.includes("server listening")); assert(s !== null && s.includes("server listening"));
} }
function killFileServer(): void { function killFileServer(): void {
@ -114,7 +114,7 @@ test("servePermissionDenied", async function (): Promise<void> {
assert(deniedServer.stderr != null); assert(deniedServer.stderr != null);
const errReader = new TextProtoReader(new BufReader(deniedServer.stderr)); const errReader = new TextProtoReader(new BufReader(deniedServer.stderr));
const s = await reader.readLine(); const s = await reader.readLine();
assert(s !== Deno.EOF && s.includes("server listening")); assert(s !== null && s.includes("server listening"));
try { try {
const res = await fetch("http://localhost:4500/"); const res = await fetch("http://localhost:4500/");
@ -138,7 +138,7 @@ test("printHelp", async function (): Promise<void> {
assert(helpProcess.stdout != null); assert(helpProcess.stdout != null);
const r = new TextProtoReader(new BufReader(helpProcess.stdout)); const r = new TextProtoReader(new BufReader(helpProcess.stdout));
const s = await r.readLine(); const s = await r.readLine();
assert(s !== Deno.EOF && s.includes("Deno File Server")); assert(s !== null && s.includes("Deno File Server"));
helpProcess.close(); helpProcess.close();
helpProcess.stdout.close(); helpProcess.stdout.close();
}); });

View file

@ -7,8 +7,8 @@ import { STATUS_TEXT } from "./http_status.ts";
export function emptyReader(): Deno.Reader { export function emptyReader(): Deno.Reader {
return { return {
read(_: Uint8Array): Promise<number | Deno.EOF> { read(_: Uint8Array): Promise<number | null> {
return Promise.resolve(Deno.EOF); return Promise.resolve(null);
}, },
}; };
} }
@ -16,9 +16,9 @@ export function emptyReader(): Deno.Reader {
export function bodyReader(contentLength: number, r: BufReader): Deno.Reader { export function bodyReader(contentLength: number, r: BufReader): Deno.Reader {
let totalRead = 0; let totalRead = 0;
let finished = false; let finished = false;
async function read(buf: Uint8Array): Promise<number | Deno.EOF> { async function read(buf: Uint8Array): Promise<number | null> {
if (finished) return Deno.EOF; if (finished) return null;
let result: number | Deno.EOF; let result: number | null;
const remaining = contentLength - totalRead; const remaining = contentLength - totalRead;
if (remaining >= buf.byteLength) { if (remaining >= buf.byteLength) {
result = await r.read(buf); result = await r.read(buf);
@ -26,7 +26,7 @@ export function bodyReader(contentLength: number, r: BufReader): Deno.Reader {
const readBuf = buf.subarray(0, remaining); const readBuf = buf.subarray(0, remaining);
result = await r.read(readBuf); result = await r.read(readBuf);
} }
if (result !== Deno.EOF) { if (result !== null) {
totalRead += result; totalRead += result;
} }
finished = totalRead === contentLength; finished = totalRead === contentLength;
@ -43,8 +43,8 @@ export function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader {
offset: number; offset: number;
data: Uint8Array; data: Uint8Array;
}> = []; }> = [];
async function read(buf: Uint8Array): Promise<number | Deno.EOF> { async function read(buf: Uint8Array): Promise<number | null> {
if (finished) return Deno.EOF; if (finished) return null;
const [chunk] = chunks; const [chunk] = chunks;
if (chunk) { if (chunk) {
const chunkRemaining = chunk.data.byteLength - chunk.offset; const chunkRemaining = chunk.data.byteLength - chunk.offset;
@ -56,14 +56,14 @@ export function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader {
if (chunk.offset === chunk.data.byteLength) { if (chunk.offset === chunk.data.byteLength) {
chunks.shift(); chunks.shift();
// Consume \r\n; // Consume \r\n;
if ((await tp.readLine()) === Deno.EOF) { if ((await tp.readLine()) === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
} }
return readLength; return readLength;
} }
const line = await tp.readLine(); const line = await tp.readLine();
if (line === Deno.EOF) throw new Deno.errors.UnexpectedEof(); if (line === null) throw new Deno.errors.UnexpectedEof();
// TODO: handle chunk extension // TODO: handle chunk extension
const [chunkSizeString] = line.split(";"); const [chunkSizeString] = line.split(";");
const chunkSize = parseInt(chunkSizeString, 16); const chunkSize = parseInt(chunkSizeString, 16);
@ -73,12 +73,12 @@ export function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader {
if (chunkSize > 0) { if (chunkSize > 0) {
if (chunkSize > buf.byteLength) { if (chunkSize > buf.byteLength) {
let eof = await r.readFull(buf); let eof = await r.readFull(buf);
if (eof === Deno.EOF) { if (eof === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
const restChunk = new Uint8Array(chunkSize - buf.byteLength); const restChunk = new Uint8Array(chunkSize - buf.byteLength);
eof = await r.readFull(restChunk); eof = await r.readFull(restChunk);
if (eof === Deno.EOF) { if (eof === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} else { } else {
chunks.push({ chunks.push({
@ -90,11 +90,11 @@ export function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader {
} else { } else {
const bufToFill = buf.subarray(0, chunkSize); const bufToFill = buf.subarray(0, chunkSize);
const eof = await r.readFull(bufToFill); const eof = await r.readFull(bufToFill);
if (eof === Deno.EOF) { if (eof === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
// Consume \r\n // Consume \r\n
if ((await tp.readLine()) === Deno.EOF) { if ((await tp.readLine()) === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
return chunkSize; return chunkSize;
@ -102,12 +102,12 @@ export function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader {
} else { } else {
assert(chunkSize === 0); assert(chunkSize === 0);
// Consume \r\n // Consume \r\n
if ((await r.readLine()) === Deno.EOF) { if ((await r.readLine()) === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
await readTrailers(h, r); await readTrailers(h, r);
finished = true; finished = true;
return Deno.EOF; return null;
} }
} }
return { read }; return { read };
@ -131,7 +131,7 @@ export async function readTrailers(
if (!keys) return; if (!keys) return;
const tp = new TextProtoReader(r); const tp = new TextProtoReader(r);
const result = await tp.readMIMEHeader(); const result = await tp.readMIMEHeader();
assert(result != Deno.EOF, "trailer must be set"); assert(result !== null, "trailer must be set");
for (const [k, v] of result) { for (const [k, v] of result) {
if (!keys.has(k)) { if (!keys.has(k)) {
throw new Error("Undeclared trailer field"); throw new Error("Undeclared trailer field");
@ -332,12 +332,12 @@ export function parseHTTPVersion(vers: string): [number, number] {
export async function readRequest( export async function readRequest(
conn: Deno.Conn, conn: Deno.Conn,
bufr: BufReader bufr: BufReader
): Promise<ServerRequest | Deno.EOF> { ): Promise<ServerRequest | null> {
const tp = new TextProtoReader(bufr); const tp = new TextProtoReader(bufr);
const firstLine = await tp.readLine(); // e.g. GET /index.html HTTP/1.0 const firstLine = await tp.readLine(); // e.g. GET /index.html HTTP/1.0
if (firstLine === Deno.EOF) return Deno.EOF; if (firstLine === null) return null;
const headers = await tp.readMIMEHeader(); const headers = await tp.readMIMEHeader();
if (headers === Deno.EOF) throw new Deno.errors.UnexpectedEof(); if (headers === null) throw new Deno.errors.UnexpectedEof();
const req = new ServerRequest(); const req = new ServerRequest();
req.conn = conn; req.conn = conn;

View file

@ -3,7 +3,6 @@ import {
assertThrowsAsync, assertThrowsAsync,
assertEquals, assertEquals,
assert, assert,
assertNotEOF,
assertNotEquals, assertNotEquals,
} from "../testing/asserts.ts"; } from "../testing/asserts.ts";
import { import {
@ -43,11 +42,11 @@ test("chunkedBodyReader", async () => {
].join(""); ].join("");
const h = new Headers(); const h = new Headers();
const r = chunkedBodyReader(h, new BufReader(new Buffer(encode(body)))); const r = chunkedBodyReader(h, new BufReader(new Buffer(encode(body))));
let result: number | Deno.EOF; let result: number | null;
// Use small buffer as some chunks exceed buffer size // Use small buffer as some chunks exceed buffer size
const buf = new Uint8Array(5); const buf = new Uint8Array(5);
const dest = new Buffer(); const dest = new Buffer();
while ((result = await r.read(buf)) !== Deno.EOF) { while ((result = await r.read(buf)) !== null) {
const len = Math.min(buf.byteLength, result); const len = Math.min(buf.byteLength, result);
await dest.write(buf.subarray(0, len)); await dest.write(buf.subarray(0, len));
} }
@ -223,25 +222,28 @@ test("writeUint8ArrayResponse", async function (): Promise<void> {
const decoder = new TextDecoder("utf-8"); const decoder = new TextDecoder("utf-8");
const reader = new BufReader(buf); const reader = new BufReader(buf);
let r: ReadLineResult; let r: ReadLineResult | null = await reader.readLine();
r = assertNotEOF(await reader.readLine()); assert(r !== null);
assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK"); assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK");
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), `content-length: ${shortText.length}`); assertEquals(decoder.decode(r.line), `content-length: ${shortText.length}`);
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(r.line.byteLength, 0); assertEquals(r.line.byteLength, 0);
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), shortText); assertEquals(decoder.decode(r.line), shortText);
assertEquals(r.more, false); assertEquals(r.more, false);
const eof = await reader.readLine(); const eof = await reader.readLine();
assertEquals(eof, Deno.EOF); assertEquals(eof, null);
}); });
test("writeStringResponse", async function (): Promise<void> { test("writeStringResponse", async function (): Promise<void> {
@ -255,25 +257,28 @@ test("writeStringResponse", async function (): Promise<void> {
const decoder = new TextDecoder("utf-8"); const decoder = new TextDecoder("utf-8");
const reader = new BufReader(buf); const reader = new BufReader(buf);
let r: ReadLineResult; let r: ReadLineResult | null = await reader.readLine();
r = assertNotEOF(await reader.readLine()); assert(r !== null);
assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK"); assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK");
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), `content-length: ${body.length}`); assertEquals(decoder.decode(r.line), `content-length: ${body.length}`);
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(r.line.byteLength, 0); assertEquals(r.line.byteLength, 0);
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), body); assertEquals(decoder.decode(r.line), body);
assertEquals(r.more, false); assertEquals(r.more, false);
const eof = await reader.readLine(); const eof = await reader.readLine();
assertEquals(eof, Deno.EOF); assertEquals(eof, null);
}); });
test("writeStringReaderResponse", async function (): Promise<void> { test("writeStringReaderResponse", async function (): Promise<void> {
@ -288,28 +293,33 @@ test("writeStringReaderResponse", async function (): Promise<void> {
const decoder = new TextDecoder("utf-8"); const decoder = new TextDecoder("utf-8");
const reader = new BufReader(buf); const reader = new BufReader(buf);
let r: ReadLineResult; let r: ReadLineResult | null = await reader.readLine();
r = assertNotEOF(await reader.readLine()); assert(r !== null);
assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK"); assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK");
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), "transfer-encoding: chunked"); assertEquals(decoder.decode(r.line), "transfer-encoding: chunked");
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(r.line.byteLength, 0); assertEquals(r.line.byteLength, 0);
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), shortText.length.toString()); assertEquals(decoder.decode(r.line), shortText.length.toString());
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), shortText); assertEquals(decoder.decode(r.line), shortText);
assertEquals(r.more, false); assertEquals(r.more, false);
r = assertNotEOF(await reader.readLine()); r = await reader.readLine();
assert(r !== null);
assertEquals(decoder.decode(r.line), "0"); assertEquals(decoder.decode(r.line), "0");
assertEquals(r.more, false); assertEquals(r.more, false);
}); });
@ -372,7 +382,7 @@ test("testReadRequestError", async function (): Promise<void> {
in: "GET / HTTP/1.1\r\nheader:foo\r\n", in: "GET / HTTP/1.1\r\nheader:foo\r\n",
err: Deno.errors.UnexpectedEof, err: Deno.errors.UnexpectedEof,
}, },
{ in: "", err: Deno.EOF }, { in: "", eof: true },
{ {
in: "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n", in: "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n",
err: "http: method cannot contain a Content-Length", err: "http: method cannot contain a Content-Length",
@ -427,14 +437,14 @@ test("testReadRequestError", async function (): Promise<void> {
for (const test of testCases) { for (const test of testCases) {
const reader = new BufReader(new StringReader(test.in)); const reader = new BufReader(new StringReader(test.in));
let err; let err;
let req: ServerRequest | Deno.EOF | undefined; let req: ServerRequest | null = null;
try { try {
req = await readRequest(mockConn(), reader); req = await readRequest(mockConn(), reader);
} catch (e) { } catch (e) {
err = e; err = e;
} }
if (test.err === Deno.EOF) { if (test.eof) {
assertEquals(req, Deno.EOF); assertEquals(req, null);
} else if (typeof test.err === "string") { } else if (typeof test.err === "string") {
assertEquals(err.message, test.err); assertEquals(err.message, test.err);
} else if (test.err) { } else if (test.err) {
@ -443,7 +453,7 @@ test("testReadRequestError", async function (): Promise<void> {
assert(req instanceof ServerRequest); assert(req instanceof ServerRequest);
assert(test.headers); assert(test.headers);
assertEquals(err, undefined); assertEquals(err, undefined);
assertNotEquals(req, Deno.EOF); assertNotEquals(req, null);
for (const h of test.headers) { for (const h of test.headers) {
assertEquals(req.headers.get(h.key), h.value); assertEquals(req.headers.get(h.key), h.value);
} }

View file

@ -14,7 +14,7 @@ export function mockConn(base: Partial<Deno.Conn> = {}): Deno.Conn {
rid: -1, rid: -1,
closeRead: (): void => {}, closeRead: (): void => {},
closeWrite: (): void => {}, closeWrite: (): void => {},
read: (): Promise<number | Deno.EOF> => { read: (): Promise<number | null> => {
return Promise.resolve(0); return Promise.resolve(0);
}, },
write: (): Promise<number> => { write: (): Promise<number> => {

View file

@ -13,7 +13,7 @@ async function startServer(): Promise<void> {
assert(server.stdout != null); assert(server.stdout != null);
const r = new TextProtoReader(new BufReader(server.stdout)); const r = new TextProtoReader(new BufReader(server.stdout));
const s = await r.readLine(); const s = await r.readLine();
assert(s !== Deno.EOF && s.includes("Racing server listening...")); assert(s !== null && s.includes("Racing server listening..."));
} }
function killServer(): void { function killServer(): void {
server.close(); server.close();

View file

@ -60,7 +60,7 @@ export class ServerRequest {
* let totRead = 0; * let totRead = 0;
* while (true) { * while (true) {
* const nread = await req.body.read(bufSlice); * const nread = await req.body.read(bufSlice);
* if (nread === Deno.EOF) break; * if (nread === null) break;
* totRead += nread; * totRead += nread;
* if (totRead >= req.contentLength) break; * if (totRead >= req.contentLength) break;
* bufSlice = bufSlice.subarray(nread); * bufSlice = bufSlice.subarray(nread);
@ -117,7 +117,7 @@ export class ServerRequest {
// Consume unread body // Consume unread body
const body = this.body; const body = this.body;
const buf = new Uint8Array(1024); const buf = new Uint8Array(1024);
while ((await body.read(buf)) !== Deno.EOF) {} while ((await body.read(buf)) !== null) {}
this.finalized = true; this.finalized = true;
} }
} }
@ -151,7 +151,7 @@ export class Server implements AsyncIterable<ServerRequest> {
const writer = new BufWriter(conn); const writer = new BufWriter(conn);
while (!this.closing) { while (!this.closing) {
let request: ServerRequest | Deno.EOF; let request: ServerRequest | null;
try { try {
request = await readRequest(conn, reader); request = await readRequest(conn, reader);
} catch (error) { } catch (error) {
@ -167,7 +167,7 @@ export class Server implements AsyncIterable<ServerRequest> {
} }
break; break;
} }
if (request == Deno.EOF) { if (request === null) {
break; break;
} }

View file

@ -10,7 +10,6 @@ import {
assert, assert,
assertEquals, assertEquals,
assertMatch, assertMatch,
assertNotEOF,
assertStrContains, assertStrContains,
assertThrowsAsync, assertThrowsAsync,
} from "../testing/asserts.ts"; } from "../testing/asserts.ts";
@ -108,7 +107,7 @@ interface TotalReader extends Deno.Reader {
} }
function totalReader(r: Deno.Reader): TotalReader { function totalReader(r: Deno.Reader): TotalReader {
let _total = 0; let _total = 0;
async function read(p: Uint8Array): Promise<number | Deno.EOF> { async function read(p: Uint8Array): Promise<number | null> {
const result = await r.read(p); const result = await r.read(p);
if (typeof result === "number") { if (typeof result === "number") {
_total += result; _total += result;
@ -252,13 +251,13 @@ test("requestBodyReaderWithContentLength", async function (): Promise<void> {
let offset = 0; let offset = 0;
while (offset < shortText.length) { while (offset < shortText.length) {
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertNotEOF(nread); assert(nread !== null);
const s = decode(readBuf.subarray(0, nread as number)); const s = decode(readBuf.subarray(0, nread as number));
assertEquals(shortText.substr(offset, nread as number), s); assertEquals(shortText.substr(offset, nread as number), s);
offset += nread as number; offset += nread as number;
} }
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertEquals(nread, Deno.EOF); assertEquals(nread, null);
} }
// Larger than given buf // Larger than given buf
@ -273,13 +272,13 @@ test("requestBodyReaderWithContentLength", async function (): Promise<void> {
let offset = 0; let offset = 0;
while (offset < longText.length) { while (offset < longText.length) {
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertNotEOF(nread); assert(nread !== null);
const s = decode(readBuf.subarray(0, nread as number)); const s = decode(readBuf.subarray(0, nread as number));
assertEquals(longText.substr(offset, nread as number), s); assertEquals(longText.substr(offset, nread as number), s);
offset += nread as number; offset += nread as number;
} }
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertEquals(nread, Deno.EOF); assertEquals(nread, null);
} }
}); });
@ -307,13 +306,13 @@ test("requestBodyReaderWithTransferEncoding", async function (): Promise<void> {
let offset = 0; let offset = 0;
while (offset < shortText.length) { while (offset < shortText.length) {
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertNotEOF(nread); assert(nread !== null);
const s = decode(readBuf.subarray(0, nread as number)); const s = decode(readBuf.subarray(0, nread as number));
assertEquals(shortText.substr(offset, nread as number), s); assertEquals(shortText.substr(offset, nread as number), s);
offset += nread as number; offset += nread as number;
} }
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertEquals(nread, Deno.EOF); assertEquals(nread, null);
} }
// Larger than internal buf // Larger than internal buf
@ -340,13 +339,13 @@ test("requestBodyReaderWithTransferEncoding", async function (): Promise<void> {
let offset = 0; let offset = 0;
while (offset < longText.length) { while (offset < longText.length) {
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertNotEOF(nread); assert(nread !== null);
const s = decode(readBuf.subarray(0, nread as number)); const s = decode(readBuf.subarray(0, nread as number));
assertEquals(longText.substr(offset, nread as number), s); assertEquals(longText.substr(offset, nread as number), s);
offset += nread as number; offset += nread as number;
} }
const nread = await req.body.read(readBuf); const nread = await req.body.read(readBuf);
assertEquals(nread, Deno.EOF); assertEquals(nread, null);
} }
}); });
@ -372,7 +371,7 @@ test({
try { try {
const r = new TextProtoReader(new BufReader(p.stdout!)); const r = new TextProtoReader(new BufReader(p.stdout!));
const s = await r.readLine(); const s = await r.readLine();
assert(s !== Deno.EOF && s.includes("server listening")); assert(s !== null && s.includes("server listening"));
await delay(100); await delay(100);
// Reqeusts to the server and immediately closes the connection // Reqeusts to the server and immediately closes the connection
const conn = await Deno.connect({ port: 4502 }); const conn = await Deno.connect({ port: 4502 });
@ -419,7 +418,7 @@ test({
const r = new TextProtoReader(new BufReader(p.stdout!)); const r = new TextProtoReader(new BufReader(p.stdout!));
const s = await r.readLine(); const s = await r.readLine();
assert( assert(
s !== Deno.EOF && s.includes("server listening"), s !== null && s.includes("server listening"),
"server must be started" "server must be started"
); );
// Requests to the server and immediately closes the connection // Requests to the server and immediately closes the connection
@ -433,7 +432,8 @@ test({
new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n") new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n")
); );
const res = new Uint8Array(100); const res = new Uint8Array(100);
const nread = assertNotEOF(await conn.read(res)); const nread = await conn.read(res);
assert(nread !== null);
conn.close(); conn.close();
const resStr = new TextDecoder().decode(res.subarray(0, nread)); const resStr = new TextDecoder().decode(res.subarray(0, nread));
assert(resStr.includes("Hello HTTPS")); assert(resStr.includes("Hello HTTPS"));
@ -476,7 +476,7 @@ test({
); );
const res = new Uint8Array(100); const res = new Uint8Array(100);
const nread = await conn.read(res); const nread = await conn.read(res);
assert(nread !== Deno.EOF); assert(nread !== null);
const resStr = new TextDecoder().decode(res.subarray(0, nread)); const resStr = new TextDecoder().decode(res.subarray(0, nread));
assertStrContains(resStr, "/hello"); assertStrContains(resStr, "/hello");
server.close(); server.close();

View file

@ -83,7 +83,7 @@ export class BufReader implements Reader {
// Read new data: try a limited number of times. // Read new data: try a limited number of times.
for (let i = MAX_CONSECUTIVE_EMPTY_READS; i > 0; i--) { for (let i = MAX_CONSECUTIVE_EMPTY_READS; i > 0; i--) {
const rr = await this.rd.read(this.buf.subarray(this.w)); const rr = await this.rd.read(this.buf.subarray(this.w));
if (rr === Deno.EOF) { if (rr === null) {
this.eof = true; this.eof = true;
return; return;
} }
@ -120,8 +120,8 @@ export class BufReader implements Reader {
* hence n may be less than len(p). * hence n may be less than len(p).
* To read exactly len(p) bytes, use io.ReadFull(b, p). * To read exactly len(p) bytes, use io.ReadFull(b, p).
*/ */
async read(p: Uint8Array): Promise<number | Deno.EOF> { async read(p: Uint8Array): Promise<number | null> {
let rr: number | Deno.EOF = p.byteLength; let rr: number | null = p.byteLength;
if (p.byteLength === 0) return rr; if (p.byteLength === 0) return rr;
if (this.r === this.w) { if (this.r === this.w) {
@ -129,7 +129,7 @@ export class BufReader implements Reader {
// Large read, empty buffer. // Large read, empty buffer.
// Read directly into p to avoid copy. // Read directly into p to avoid copy.
const rr = await this.rd.read(p); const rr = await this.rd.read(p);
const nread = rr === Deno.EOF ? 0 : rr; const nread = rr ?? 0;
assert(nread >= 0, "negative read"); assert(nread >= 0, "negative read");
// if (rr.nread > 0) { // if (rr.nread > 0) {
// this.lastByte = p[rr.nread - 1]; // this.lastByte = p[rr.nread - 1];
@ -143,7 +143,7 @@ export class BufReader implements Reader {
this.r = 0; this.r = 0;
this.w = 0; this.w = 0;
rr = await this.rd.read(this.buf); rr = await this.rd.read(this.buf);
if (rr === 0 || rr === Deno.EOF) return rr; if (rr === 0 || rr === null) return rr;
assert(rr >= 0, "negative read"); assert(rr >= 0, "negative read");
this.w += rr; this.w += rr;
} }
@ -161,7 +161,7 @@ export class BufReader implements Reader {
* If successful, `p` is returned. * If successful, `p` is returned.
* *
* If the end of the underlying stream has been reached, and there are no more * If the end of the underlying stream has been reached, and there are no more
* bytes available in the buffer, `readFull()` returns `EOF` instead. * bytes available in the buffer, `readFull()` returns `null` instead.
* *
* An error is thrown if some bytes could be read, but not enough to fill `p` * An error is thrown if some bytes could be read, but not enough to fill `p`
* entirely before the underlying stream reported an error or EOF. Any error * entirely before the underlying stream reported an error or EOF. Any error
@ -170,14 +170,14 @@ export class BufReader implements Reader {
* *
* Ported from https://golang.org/pkg/io/#ReadFull * Ported from https://golang.org/pkg/io/#ReadFull
*/ */
async readFull(p: Uint8Array): Promise<Uint8Array | Deno.EOF> { async readFull(p: Uint8Array): Promise<Uint8Array | null> {
let bytesRead = 0; let bytesRead = 0;
while (bytesRead < p.length) { while (bytesRead < p.length) {
try { try {
const rr = await this.read(p.subarray(bytesRead)); const rr = await this.read(p.subarray(bytesRead));
if (rr === Deno.EOF) { if (rr === null) {
if (bytesRead === 0) { if (bytesRead === 0) {
return Deno.EOF; return null;
} else { } else {
throw new PartialReadError(); throw new PartialReadError();
} }
@ -191,10 +191,10 @@ export class BufReader implements Reader {
return p; return p;
} }
/** Returns the next byte [0, 255] or `EOF`. */ /** Returns the next byte [0, 255] or `null`. */
async readByte(): Promise<number | Deno.EOF> { async readByte(): Promise<number | null> {
while (this.r === this.w) { while (this.r === this.w) {
if (this.eof) return Deno.EOF; if (this.eof) return null;
await this._fill(); // buffer is empty. await this._fill(); // buffer is empty.
} }
const c = this.buf[this.r]; const c = this.buf[this.r];
@ -207,17 +207,17 @@ export class BufReader implements Reader {
* returning a string containing the data up to and including the delimiter. * returning a string containing the data up to and including the delimiter.
* If ReadString encounters an error before finding a delimiter, * If ReadString encounters an error before finding a delimiter,
* it returns the data read before the error and the error itself * it returns the data read before the error and the error itself
* (often io.EOF). * (often `null`).
* ReadString returns err != nil if and only if the returned data does not end * ReadString returns err != nil if and only if the returned data does not end
* in delim. * in delim.
* For simple uses, a Scanner may be more convenient. * For simple uses, a Scanner may be more convenient.
*/ */
async readString(delim: string): Promise<string | Deno.EOF> { async readString(delim: string): Promise<string | null> {
if (delim.length !== 1) { if (delim.length !== 1) {
throw new Error("Delimiter should be a single character"); throw new Error("Delimiter should be a single character");
} }
const buffer = await this.readSlice(delim.charCodeAt(0)); const buffer = await this.readSlice(delim.charCodeAt(0));
if (buffer == Deno.EOF) return Deno.EOF; if (buffer === null) return null;
return new TextDecoder().decode(buffer); return new TextDecoder().decode(buffer);
} }
@ -237,14 +237,14 @@ export class BufReader implements Reader {
* When the end of the underlying stream is reached, the final bytes in the * When the end of the underlying stream is reached, the final bytes in the
* stream are returned. No indication or error is given if the input ends * stream are returned. No indication or error is given if the input ends
* without a final line end. When there are no more trailing bytes to read, * without a final line end. When there are no more trailing bytes to read,
* `readLine()` returns the `EOF` symbol. * `readLine()` returns `null`.
* *
* Calling `unreadByte()` after `readLine()` will always unread the last byte * Calling `unreadByte()` after `readLine()` will always unread the last byte
* read (possibly a character belonging to the line end) even if that byte is * read (possibly a character belonging to the line end) even if that byte is
* not part of the line returned by `readLine()`. * not part of the line returned by `readLine()`.
*/ */
async readLine(): Promise<ReadLineResult | Deno.EOF> { async readLine(): Promise<ReadLineResult | null> {
let line: Uint8Array | Deno.EOF; let line: Uint8Array | null;
try { try {
line = await this.readSlice(LF); line = await this.readSlice(LF);
@ -277,8 +277,8 @@ export class BufReader implements Reader {
return { line: partial, more: !this.eof }; return { line: partial, more: !this.eof };
} }
if (line === Deno.EOF) { if (line === null) {
return Deno.EOF; return null;
} }
if (line.byteLength === 0) { if (line.byteLength === 0) {
@ -306,12 +306,12 @@ export class BufReader implements Reader {
* If `readSlice()` encounters the end of the underlying stream and there are * If `readSlice()` encounters the end of the underlying stream and there are
* any bytes left in the buffer, the rest of the buffer is returned. In other * any bytes left in the buffer, the rest of the buffer is returned. In other
* words, EOF is always treated as a delimiter. Once the buffer is empty, * words, EOF is always treated as a delimiter. Once the buffer is empty,
* it returns `EOF`. * it returns `null`.
* *
* Because the data returned from `readSlice()` will be overwritten by the * Because the data returned from `readSlice()` will be overwritten by the
* next I/O operation, most clients should use `readString()` instead. * next I/O operation, most clients should use `readString()` instead.
*/ */
async readSlice(delim: number): Promise<Uint8Array | Deno.EOF> { async readSlice(delim: number): Promise<Uint8Array | null> {
let s = 0; // search start index let s = 0; // search start index
let slice: Uint8Array | undefined; let slice: Uint8Array | undefined;
@ -328,7 +328,7 @@ export class BufReader implements Reader {
// EOF? // EOF?
if (this.eof) { if (this.eof) {
if (this.r === this.w) { if (this.r === this.w) {
return Deno.EOF; return null;
} }
slice = this.buf.subarray(this.r, this.w); slice = this.buf.subarray(this.r, this.w);
this.r = this.w; this.r = this.w;
@ -367,13 +367,13 @@ export class BufReader implements Reader {
* *
* When the end of the underlying stream is reached, but there are unread * When the end of the underlying stream is reached, but there are unread
* bytes left in the buffer, those bytes are returned. If there are no bytes * bytes left in the buffer, those bytes are returned. If there are no bytes
* left in the buffer, it returns `EOF`. * left in the buffer, it returns `null`.
* *
* If an error is encountered before `n` bytes are available, `peek()` throws * If an error is encountered before `n` bytes are available, `peek()` throws
* an error with the `partial` property set to a slice of the buffer that * an error with the `partial` property set to a slice of the buffer that
* contains the bytes that were available before the error occurred. * contains the bytes that were available before the error occurred.
*/ */
async peek(n: number): Promise<Uint8Array | Deno.EOF> { async peek(n: number): Promise<Uint8Array | null> {
if (n < 0) { if (n < 0) {
throw Error("negative count"); throw Error("negative count");
} }
@ -390,7 +390,7 @@ export class BufReader implements Reader {
} }
if (avail === 0 && this.eof) { if (avail === 0 && this.eof) {
return Deno.EOF; return null;
} else if (avail < n && this.eof) { } else if (avail < n && this.eof) {
return this.buf.subarray(this.r, this.r + avail); return this.buf.subarray(this.r, this.r + avail);
} else if (avail < n) { } else if (avail < n) {
@ -656,7 +656,7 @@ export async function* readDelim(
let matchIndex = 0; let matchIndex = 0;
while (true) { while (true) {
const result = await reader.read(inspectArr); const result = await reader.read(inspectArr);
if (result === Deno.EOF) { if (result === null) {
// Yield last chunk. // Yield last chunk.
yield inputBuffer.bytes(); yield inputBuffer.bytes();
return; return;

View file

@ -5,12 +5,7 @@
const { Buffer } = Deno; const { Buffer } = Deno;
type Reader = Deno.Reader; type Reader = Deno.Reader;
import { import { assert, assertEquals, fail } from "../testing/asserts.ts";
assert,
assertEquals,
fail,
assertNotEOF,
} from "../testing/asserts.ts";
import { import {
BufReader, BufReader,
BufWriter, BufWriter,
@ -30,7 +25,7 @@ async function readBytes(buf: BufReader): Promise<string> {
let nb = 0; let nb = 0;
while (true) { while (true) {
const c = await buf.readByte(); const c = await buf.readByte();
if (c === Deno.EOF) { if (c === null) {
break; // EOF break; // EOF
} }
b[nb] = c; b[nb] = c;
@ -69,7 +64,7 @@ async function reads(buf: BufReader, m: number): Promise<string> {
let nb = 0; let nb = 0;
while (true) { while (true) {
const result = await buf.read(b.subarray(nb, nb + m)); const result = await buf.read(b.subarray(nb, nb + m));
if (result === Deno.EOF) { if (result === null) {
break; break;
} }
nb += result; nb += result;
@ -152,7 +147,8 @@ Deno.test("bufioBufferFull", async function (): Promise<void> {
assertEquals(decoder.decode(err.partial), "And now, hello, "); assertEquals(decoder.decode(err.partial), "And now, hello, ");
} }
const line = assertNotEOF(await buf.readSlice(charCode("!"))); const line = await buf.readSlice(charCode("!"));
assert(line !== null);
const actual = decoder.decode(line); const actual = decoder.decode(line);
assertEquals(actual, "world!"); assertEquals(actual, "world!");
}); });
@ -161,14 +157,16 @@ Deno.test("bufioReadString", async function (): Promise<void> {
const string = "And now, hello world!"; const string = "And now, hello world!";
const buf = new BufReader(stringsReader(string), MIN_READ_BUFFER_SIZE); const buf = new BufReader(stringsReader(string), MIN_READ_BUFFER_SIZE);
const line = assertNotEOF(await buf.readString(",")); const line = await buf.readString(",");
assert(line !== null);
assertEquals(line, "And now,"); assertEquals(line, "And now,");
assertEquals(line.length, 8); assertEquals(line.length, 8);
const line2 = assertNotEOF(await buf.readString(",")); const line2 = await buf.readString(",");
assert(line2 !== null);
assertEquals(line2, " hello world!"); assertEquals(line2, " hello world!");
assertEquals(await buf.readString(","), Deno.EOF); assertEquals(await buf.readString(","), null);
try { try {
await buf.readString("deno"); await buf.readString("deno");
@ -192,7 +190,7 @@ const testOutput = encoder.encode("0123456789abcdefghijklmnopqrstuvwxy");
class TestReader implements Reader { class TestReader implements Reader {
constructor(private data: Uint8Array, private stride: number) {} constructor(private data: Uint8Array, private stride: number) {}
read(buf: Uint8Array): Promise<number | Deno.EOF> { read(buf: Uint8Array): Promise<number | null> {
let nread = this.stride; let nread = this.stride;
if (nread > this.data.byteLength) { if (nread > this.data.byteLength) {
nread = this.data.byteLength; nread = this.data.byteLength;
@ -201,7 +199,7 @@ class TestReader implements Reader {
nread = buf.byteLength; nread = buf.byteLength;
} }
if (nread === 0) { if (nread === 0) {
return Promise.resolve(Deno.EOF); return Promise.resolve(null);
} }
copyBytes(buf as Uint8Array, this.data); copyBytes(buf as Uint8Array, this.data);
this.data = this.data.subarray(nread); this.data = this.data.subarray(nread);
@ -216,7 +214,7 @@ async function testReadLine(input: Uint8Array): Promise<void> {
const l = new BufReader(reader, input.byteLength + 1); const l = new BufReader(reader, input.byteLength + 1);
while (true) { while (true) {
const r = await l.readLine(); const r = await l.readLine();
if (r === Deno.EOF) { if (r === null) {
break; break;
} }
const { line, more } = r; const { line, more } = r;
@ -253,10 +251,12 @@ Deno.test("bufioPeek", async function (): Promise<void> {
MIN_READ_BUFFER_SIZE MIN_READ_BUFFER_SIZE
); );
let actual = assertNotEOF(await buf.peek(1)); let actual = await buf.peek(1);
assert(actual !== null);
assertEquals(decoder.decode(actual), "a"); assertEquals(decoder.decode(actual), "a");
actual = assertNotEOF(await buf.peek(4)); actual = await buf.peek(4);
assert(actual !== null);
assertEquals(decoder.decode(actual), "abcd"); assertEquals(decoder.decode(actual), "abcd");
try { try {
@ -271,33 +271,39 @@ Deno.test("bufioPeek", async function (): Promise<void> {
await buf.read(p.subarray(0, 3)); await buf.read(p.subarray(0, 3));
assertEquals(decoder.decode(p.subarray(0, 3)), "abc"); assertEquals(decoder.decode(p.subarray(0, 3)), "abc");
actual = assertNotEOF(await buf.peek(1)); actual = await buf.peek(1);
assert(actual !== null);
assertEquals(decoder.decode(actual), "d"); assertEquals(decoder.decode(actual), "d");
actual = assertNotEOF(await buf.peek(1)); actual = await buf.peek(1);
assert(actual !== null);
assertEquals(decoder.decode(actual), "d"); assertEquals(decoder.decode(actual), "d");
actual = assertNotEOF(await buf.peek(1)); actual = await buf.peek(1);
assert(actual !== null);
assertEquals(decoder.decode(actual), "d"); assertEquals(decoder.decode(actual), "d");
actual = assertNotEOF(await buf.peek(2)); actual = await buf.peek(2);
assert(actual !== null);
assertEquals(decoder.decode(actual), "de"); assertEquals(decoder.decode(actual), "de");
const res = await buf.read(p.subarray(0, 3)); const res = await buf.read(p.subarray(0, 3));
assertEquals(decoder.decode(p.subarray(0, 3)), "def"); assertEquals(decoder.decode(p.subarray(0, 3)), "def");
assert(res !== Deno.EOF); assert(res !== null);
actual = assertNotEOF(await buf.peek(4)); actual = await buf.peek(4);
assert(actual !== null);
assertEquals(decoder.decode(actual), "ghij"); assertEquals(decoder.decode(actual), "ghij");
await buf.read(p); await buf.read(p);
assertEquals(decoder.decode(p), "ghijklmnop"); assertEquals(decoder.decode(p), "ghijklmnop");
actual = assertNotEOF(await buf.peek(0)); actual = await buf.peek(0);
assert(actual !== null);
assertEquals(decoder.decode(actual), ""); assertEquals(decoder.decode(actual), "");
const r = await buf.peek(1); const r = await buf.peek(1);
assert(r === Deno.EOF); assert(r === null);
/* TODO /* TODO
Test for issue 3022, not exposing a reader's error on a successful Peek. Test for issue 3022, not exposing a reader's error on a successful Peek.
buf = NewReaderSize(dataAndEOFReader("abcd"), 32) buf = NewReaderSize(dataAndEOFReader("abcd"), 32)
@ -396,7 +402,8 @@ Deno.test("bufReaderReadFull", async function (): Promise<void> {
const bufr = new BufReader(data, 3); const bufr = new BufReader(data, 3);
{ {
const buf = new Uint8Array(6); const buf = new Uint8Array(6);
const r = assertNotEOF(await bufr.readFull(buf)); const r = await bufr.readFull(buf);
assert(r !== null);
assertEquals(r, buf); assertEquals(r, buf);
assertEquals(dec.decode(buf), "Hello "); assertEquals(dec.decode(buf), "Hello ");
} }

View file

@ -10,7 +10,7 @@ type Reader = Deno.Reader;
export class OneByteReader implements Reader { export class OneByteReader implements Reader {
constructor(readonly r: Reader) {} constructor(readonly r: Reader) {}
read(p: Uint8Array): Promise<number | Deno.EOF> { read(p: Uint8Array): Promise<number | null> {
if (p.byteLength === 0) { if (p.byteLength === 0) {
return Promise.resolve(0); return Promise.resolve(0);
} }
@ -27,7 +27,7 @@ export class OneByteReader implements Reader {
export class HalfReader implements Reader { export class HalfReader implements Reader {
constructor(readonly r: Reader) {} constructor(readonly r: Reader) {}
read(p: Uint8Array): Promise<number | Deno.EOF> { read(p: Uint8Array): Promise<number | null> {
if (!(p instanceof Uint8Array)) { if (!(p instanceof Uint8Array)) {
throw Error("expected Uint8Array"); throw Error("expected Uint8Array");
} }
@ -43,7 +43,7 @@ export class TimeoutReader implements Reader {
count = 0; count = 0;
constructor(readonly r: Reader) {} constructor(readonly r: Reader) {}
read(p: Uint8Array): Promise<number | Deno.EOF> { read(p: Uint8Array): Promise<number | null> {
this.count++; this.count++;
if (this.count === 2) { if (this.count === 2) {
throw new Deno.errors.TimedOut(); throw new Deno.errors.TimedOut();

View file

@ -21,13 +21,13 @@ export async function copyN(
buf = new Uint8Array(size - bytesRead); buf = new Uint8Array(size - bytesRead);
} }
const result = await r.read(buf); const result = await r.read(buf);
const nread = result === Deno.EOF ? 0 : result; const nread = result ?? 0;
bytesRead += nread; bytesRead += nread;
if (nread > 0) { if (nread > 0) {
const n = await dest.write(buf.slice(0, nread)); const n = await dest.write(buf.slice(0, nread));
assert(n === nread, "could not write"); assert(n === nread, "could not write");
} }
if (result === Deno.EOF) { if (result === null) {
break; break;
} }
} }
@ -35,31 +35,31 @@ export async function copyN(
} }
/** Read big endian 16bit short from BufReader */ /** Read big endian 16bit short from BufReader */
export async function readShort(buf: BufReader): Promise<number | Deno.EOF> { export async function readShort(buf: BufReader): Promise<number | null> {
const high = await buf.readByte(); const high = await buf.readByte();
if (high === Deno.EOF) return Deno.EOF; if (high === null) return null;
const low = await buf.readByte(); const low = await buf.readByte();
if (low === Deno.EOF) throw new Deno.errors.UnexpectedEof(); if (low === null) throw new Deno.errors.UnexpectedEof();
return (high << 8) | low; return (high << 8) | low;
} }
/** Read big endian 32bit integer from BufReader */ /** Read big endian 32bit integer from BufReader */
export async function readInt(buf: BufReader): Promise<number | Deno.EOF> { export async function readInt(buf: BufReader): Promise<number | null> {
const high = await readShort(buf); const high = await readShort(buf);
if (high === Deno.EOF) return Deno.EOF; if (high === null) return null;
const low = await readShort(buf); const low = await readShort(buf);
if (low === Deno.EOF) throw new Deno.errors.UnexpectedEof(); if (low === null) throw new Deno.errors.UnexpectedEof();
return (high << 16) | low; return (high << 16) | low;
} }
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
/** Read big endian 64bit long from BufReader */ /** Read big endian 64bit long from BufReader */
export async function readLong(buf: BufReader): Promise<number | Deno.EOF> { export async function readLong(buf: BufReader): Promise<number | null> {
const high = await readInt(buf); const high = await readInt(buf);
if (high === Deno.EOF) return Deno.EOF; if (high === null) return null;
const low = await readInt(buf); const low = await readInt(buf);
if (low === Deno.EOF) throw new Deno.errors.UnexpectedEof(); if (low === null) throw new Deno.errors.UnexpectedEof();
const big = (BigInt(high) << 32n) | BigInt(low); const big = (BigInt(high) << 32n) | BigInt(low);
// We probably should provide a similar API that returns BigInt values. // We probably should provide a similar API that returns BigInt values.
if (big > MAX_SAFE_INTEGER) { if (big > MAX_SAFE_INTEGER) {

View file

@ -17,7 +17,7 @@ class BinaryReader implements Reader {
constructor(private bytes: Uint8Array = new Uint8Array(0)) {} constructor(private bytes: Uint8Array = new Uint8Array(0)) {}
read(p: Uint8Array): Promise<number | Deno.EOF> { read(p: Uint8Array): Promise<number | null> {
p.set(this.bytes.subarray(this.index, p.byteLength)); p.set(this.bytes.subarray(this.index, p.byteLength));
this.index += p.byteLength; this.index += p.byteLength;
return Promise.resolve(p.byteLength); return Promise.resolve(p.byteLength);

View file

@ -9,12 +9,12 @@ export class StringReader implements Reader {
constructor(private readonly s: string) {} constructor(private readonly s: string) {}
read(p: Uint8Array): Promise<number | Deno.EOF> { read(p: Uint8Array): Promise<number | null> {
const n = Math.min(p.byteLength, this.buf.byteLength - this.offs); const n = Math.min(p.byteLength, this.buf.byteLength - this.offs);
p.set(this.buf.slice(this.offs, this.offs + n)); p.set(this.buf.slice(this.offs, this.offs + n));
this.offs += n; this.offs += n;
if (n === 0) { if (n === 0) {
return Promise.resolve(Deno.EOF); return Promise.resolve(null);
} }
return Promise.resolve(n); return Promise.resolve(n);
} }
@ -29,11 +29,11 @@ export class MultiReader implements Reader {
this.readers = readers; this.readers = readers;
} }
async read(p: Uint8Array): Promise<number | Deno.EOF> { async read(p: Uint8Array): Promise<number | null> {
const r = this.readers[this.currentIndex]; const r = this.readers[this.currentIndex];
if (!r) return Deno.EOF; if (!r) return null;
const result = await r.read(p); const result = await r.read(p);
if (result === Deno.EOF) { if (result === null) {
this.currentIndex++; this.currentIndex++;
return 0; return 0;
} }

View file

@ -10,7 +10,7 @@ test("ioStringReader", async function (): Promise<void> {
const res0 = await r.read(new Uint8Array(6)); const res0 = await r.read(new Uint8Array(6));
assertEquals(res0, 6); assertEquals(res0, 6);
const res1 = await r.read(new Uint8Array(6)); const res1 = await r.read(new Uint8Array(6));
assertEquals(res1, Deno.EOF); assertEquals(res1, null);
}); });
test("ioStringReader", async function (): Promise<void> { test("ioStringReader", async function (): Promise<void> {
@ -23,7 +23,7 @@ test("ioStringReader", async function (): Promise<void> {
assertEquals(res2, 3); assertEquals(res2, 3);
assertEquals(decode(buf), "def"); assertEquals(decode(buf), "def");
const res3 = await r.read(buf); const res3 = await r.read(buf);
assertEquals(res3, Deno.EOF); assertEquals(res3, null);
assertEquals(decode(buf), "def"); assertEquals(decode(buf), "def");
}); });

View file

@ -105,7 +105,7 @@ export function scanUntilBoundary(
newLineDashBoundary: Uint8Array, newLineDashBoundary: Uint8Array,
total: number, total: number,
eof: boolean eof: boolean
): number | Deno.EOF { ): number | null {
if (total === 0) { if (total === 0) {
// At beginning of body, allow dashBoundary. // At beginning of body, allow dashBoundary.
if (hasPrefix(buf, dashBoundary)) { if (hasPrefix(buf, dashBoundary)) {
@ -115,7 +115,7 @@ export function scanUntilBoundary(
case 0: case 0:
return 0; return 0;
case 1: case 1:
return Deno.EOF; return null;
} }
} }
if (hasPrefix(dashBoundary, buf)) { if (hasPrefix(dashBoundary, buf)) {
@ -132,7 +132,7 @@ export function scanUntilBoundary(
case 0: case 0:
return i; return i;
case 1: case 1:
return i > 0 ? i : Deno.EOF; return i > 0 ? i : null;
} }
} }
if (hasPrefix(newLineDashBoundary, buf)) { if (hasPrefix(newLineDashBoundary, buf)) {
@ -151,12 +151,12 @@ export function scanUntilBoundary(
} }
class PartReader implements Reader, Closer { class PartReader implements Reader, Closer {
n: number | Deno.EOF = 0; n: number | null = 0;
total = 0; total = 0;
constructor(private mr: MultipartReader, public readonly headers: Headers) {} constructor(private mr: MultipartReader, public readonly headers: Headers) {}
async read(p: Uint8Array): Promise<number | Deno.EOF> { async read(p: Uint8Array): Promise<number | null> {
const br = this.mr.bufReader; const br = this.mr.bufReader;
// Read into buffer until we identify some data to return, // Read into buffer until we identify some data to return,
@ -165,7 +165,7 @@ class PartReader implements Reader, Closer {
while (this.n === 0) { while (this.n === 0) {
peekLength = max(peekLength, br.buffered()); peekLength = max(peekLength, br.buffered());
const peekBuf = await br.peek(peekLength); const peekBuf = await br.peek(peekLength);
if (peekBuf === Deno.EOF) { if (peekBuf === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
const eof = peekBuf.length < peekLength; const eof = peekBuf.length < peekLength;
@ -183,8 +183,8 @@ class PartReader implements Reader, Closer {
} }
} }
if (this.n === Deno.EOF) { if (this.n === null) {
return Deno.EOF; return null;
} }
const nread = min(p.length, this.n); const nread = min(p.length, this.n);
@ -288,7 +288,7 @@ export class MultipartReader {
const buf = new Buffer(new Uint8Array(maxValueBytes)); const buf = new Buffer(new Uint8Array(maxValueBytes));
for (;;) { for (;;) {
const p = await this.nextPart(); const p = await this.nextPart();
if (p === Deno.EOF) { if (p === null) {
break; break;
} }
if (p.formName === "") { if (p.formName === "") {
@ -354,7 +354,7 @@ export class MultipartReader {
private currentPart: PartReader | undefined; private currentPart: PartReader | undefined;
private partsRead = 0; private partsRead = 0;
private async nextPart(): Promise<PartReader | Deno.EOF> { private async nextPart(): Promise<PartReader | null> {
if (this.currentPart) { if (this.currentPart) {
this.currentPart.close(); this.currentPart.close();
} }
@ -364,14 +364,14 @@ export class MultipartReader {
let expectNewPart = false; let expectNewPart = false;
for (;;) { for (;;) {
const line = await this.bufReader.readSlice("\n".charCodeAt(0)); const line = await this.bufReader.readSlice("\n".charCodeAt(0));
if (line === Deno.EOF) { if (line === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
if (this.isBoundaryDelimiterLine(line)) { if (this.isBoundaryDelimiterLine(line)) {
this.partsRead++; this.partsRead++;
const r = new TextProtoReader(this.bufReader); const r = new TextProtoReader(this.bufReader);
const headers = await r.readMIMEHeader(); const headers = await r.readMIMEHeader();
if (headers === Deno.EOF) { if (headers === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
const np = new PartReader(this, headers); const np = new PartReader(this, headers);
@ -379,7 +379,7 @@ export class MultipartReader {
return np; return np;
} }
if (this.isFinalBoundary(line)) { if (this.isFinalBoundary(line)) {
return Deno.EOF; return null;
} }
if (expectNewPart) { if (expectNewPart) {
throw new Error(`expecting a new Part; got line ${line}`); throw new Error(`expecting a new Part; got line ${line}`);

View file

@ -31,7 +31,7 @@ test("multipartScanUntilBoundary1", function (): void {
0, 0,
true true
); );
assertEquals(n, Deno.EOF); assertEquals(n, null);
}); });
test("multipartScanUntilBoundary2", function (): void { test("multipartScanUntilBoundary2", function (): void {

View file

@ -379,8 +379,3 @@ export function unimplemented(msg?: string): never {
export function unreachable(): never { export function unreachable(): never {
throw new AssertionError("unreachable"); throw new AssertionError("unreachable");
} }
export function assertNotEOF<T extends {}>(val: T | Deno.EOF): T {
assertNotEquals(val, Deno.EOF);
return val as T;
}

View file

@ -22,9 +22,9 @@ export class TextProtoReader {
/** readLine() reads a single line from the TextProtoReader, /** readLine() reads a single line from the TextProtoReader,
* eliding the final \n or \r\n from the returned string. * eliding the final \n or \r\n from the returned string.
*/ */
async readLine(): Promise<string | Deno.EOF> { async readLine(): Promise<string | null> {
const s = await this.readLineSlice(); const s = await this.readLineSlice();
if (s === Deno.EOF) return Deno.EOF; if (s === null) return null;
return str(s); return str(s);
} }
@ -48,20 +48,20 @@ export class TextProtoReader {
* "Long-Key": {"Even Longer Value"}, * "Long-Key": {"Even Longer Value"},
* } * }
*/ */
async readMIMEHeader(): Promise<Headers | Deno.EOF> { async readMIMEHeader(): Promise<Headers | null> {
const m = new Headers(); const m = new Headers();
let line: Uint8Array | undefined; let line: Uint8Array | undefined;
// The first line cannot start with a leading space. // The first line cannot start with a leading space.
let buf = await this.r.peek(1); let buf = await this.r.peek(1);
if (buf === Deno.EOF) { if (buf === null) {
return Deno.EOF; return null;
} else if (buf[0] == charCode(" ") || buf[0] == charCode("\t")) { } else if (buf[0] == charCode(" ") || buf[0] == charCode("\t")) {
line = (await this.readLineSlice()) as Uint8Array; line = (await this.readLineSlice()) as Uint8Array;
} }
buf = await this.r.peek(1); buf = await this.r.peek(1);
if (buf === Deno.EOF) { if (buf === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} else if (buf[0] == charCode(" ") || buf[0] == charCode("\t")) { } else if (buf[0] == charCode(" ") || buf[0] == charCode("\t")) {
throw new Deno.errors.InvalidData( throw new Deno.errors.InvalidData(
@ -71,7 +71,7 @@ export class TextProtoReader {
while (true) { while (true) {
const kv = await this.readLineSlice(); // readContinuedLineSlice const kv = await this.readLineSlice(); // readContinuedLineSlice
if (kv === Deno.EOF) throw new Deno.errors.UnexpectedEof(); if (kv === null) throw new Deno.errors.UnexpectedEof();
if (kv.byteLength === 0) return m; if (kv.byteLength === 0) return m;
// Key ends at first colon // Key ends at first colon
@ -112,12 +112,12 @@ export class TextProtoReader {
} }
} }
async readLineSlice(): Promise<Uint8Array | Deno.EOF> { async readLineSlice(): Promise<Uint8Array | null> {
// this.closeDot(); // this.closeDot();
let line: Uint8Array | undefined; let line: Uint8Array | undefined;
while (true) { while (true) {
const r = await this.r.readLine(); const r = await this.r.readLine();
if (r === Deno.EOF) return Deno.EOF; if (r === null) return null;
const { line: l, more } = r; const { line: l, more } = r;
// Avoid the copy if the first call produced a full line. // Avoid the copy if the first call produced a full line.

View file

@ -6,12 +6,7 @@
import { BufReader } from "../io/bufio.ts"; import { BufReader } from "../io/bufio.ts";
import { TextProtoReader } from "./mod.ts"; import { TextProtoReader } from "./mod.ts";
import { stringsReader } from "../io/util.ts"; import { stringsReader } from "../io/util.ts";
import { import { assert, assertEquals, assertThrows } from "../testing/asserts.ts";
assert,
assertEquals,
assertThrows,
assertNotEOF,
} from "../testing/asserts.ts";
const { test } = Deno; const { test } = Deno;
function reader(s: string): TextProtoReader { function reader(s: string): TextProtoReader {
@ -31,7 +26,7 @@ test({
test("[textproto] ReadEmpty", async () => { test("[textproto] ReadEmpty", async () => {
const r = reader(""); const r = reader("");
const m = await r.readMIMEHeader(); const m = await r.readMIMEHeader();
assertEquals(m, Deno.EOF); assertEquals(m, null);
}); });
test("[textproto] Reader", async () => { test("[textproto] Reader", async () => {
@ -43,7 +38,7 @@ test("[textproto] Reader", async () => {
assertEquals(s, "line2"); assertEquals(s, "line2");
s = await r.readLine(); s = await r.readLine();
assert(s === Deno.EOF); assert(s === null);
}); });
test({ test({
@ -53,7 +48,8 @@ test({
"my-key: Value 1 \r\nLong-key: Even Longer Value\r\nmy-Key: " + "my-key: Value 1 \r\nLong-key: Even Longer Value\r\nmy-Key: " +
"Value 2\r\n\n"; "Value 2\r\n\n";
const r = reader(input); const r = reader(input);
const m = assertNotEOF(await r.readMIMEHeader()); const m = await r.readMIMEHeader();
assert(m !== null);
assertEquals(m.get("My-Key"), "Value 1, Value 2"); assertEquals(m.get("My-Key"), "Value 1, Value 2");
assertEquals(m.get("Long-key"), "Even Longer Value"); assertEquals(m.get("Long-key"), "Even Longer Value");
}, },
@ -64,7 +60,8 @@ test({
async fn(): Promise<void> { async fn(): Promise<void> {
const input = "Foo: bar\n\n"; const input = "Foo: bar\n\n";
const r = reader(input); const r = reader(input);
const m = assertNotEOF(await r.readMIMEHeader()); const m = await r.readMIMEHeader();
assert(m !== null);
assertEquals(m.get("Foo"), "bar"); assertEquals(m.get("Foo"), "bar");
}, },
}); });
@ -74,7 +71,8 @@ test({
async fn(): Promise<void> { async fn(): Promise<void> {
const input = ": bar\ntest-1: 1\n\n"; const input = ": bar\ntest-1: 1\n\n";
const r = reader(input); const r = reader(input);
const m = assertNotEOF(await r.readMIMEHeader()); const m = await r.readMIMEHeader();
assert(m !== null);
assertEquals(m.get("Test-1"), "1"); assertEquals(m.get("Test-1"), "1");
}, },
}); });
@ -89,7 +87,8 @@ test({
} }
const sdata = data.join(""); const sdata = data.join("");
const r = reader(`Cookie: ${sdata}\r\n\r\n`); const r = reader(`Cookie: ${sdata}\r\n\r\n`);
const m = assertNotEOF(await r.readMIMEHeader()); const m = await r.readMIMEHeader();
assert(m !== null);
assertEquals(m.get("Cookie"), sdata); assertEquals(m.get("Cookie"), sdata);
}, },
}); });
@ -106,7 +105,8 @@ test({
"Audio Mode : None\r\n" + "Audio Mode : None\r\n" +
"Privilege : 127\r\n\r\n"; "Privilege : 127\r\n\r\n";
const r = reader(input); const r = reader(input);
const m = assertNotEOF(await r.readMIMEHeader()); const m = await r.readMIMEHeader();
assert(m !== null);
assertEquals(m.get("Foo"), "bar"); assertEquals(m.get("Foo"), "bar");
assertEquals(m.get("Content-Language"), "en"); assertEquals(m.get("Content-Language"), "en");
// Make sure we drop headers with trailing whitespace // Make sure we drop headers with trailing whitespace
@ -174,7 +174,8 @@ test({
"------WebKitFormBoundaryimeZ2Le9LjohiUiG--\r\n\n", "------WebKitFormBoundaryimeZ2Le9LjohiUiG--\r\n\n",
]; ];
const r = reader(input.join("")); const r = reader(input.join(""));
const m = assertNotEOF(await r.readMIMEHeader()); const m = await r.readMIMEHeader();
assert(m !== null);
assertEquals(m.get("Accept"), "*/*"); assertEquals(m.get("Accept"), "*/*");
assertEquals(m.get("Content-Disposition"), 'form-data; name="test"'); assertEquals(m.get("Content-Disposition"), 'form-data; name="test"');
}, },

View file

@ -102,7 +102,7 @@ const tpr = new TextProtoReader(new BufReader(Deno.stdin));
while (true) { while (true) {
await Deno.stdout.write(encode("> ")); await Deno.stdout.write(encode("> "));
const line = await tpr.readLine(); const line = await tpr.readLine();
if (line === Deno.EOF) { if (line === null) {
break; break;
} }
if (line === "close") { if (line === "close") {

View file

@ -31,7 +31,7 @@ const tpr = new TextProtoReader(new BufReader(Deno.stdin));
while (true) { while (true) {
await Deno.stdout.write(encode("> ")); await Deno.stdout.write(encode("> "));
const line = await tpr.readLine(); const line = await tpr.readLine();
if (line === Deno.EOF) { if (line === null) {
break; break;
} }
if (line === "close") { if (line === "close") {

View file

@ -8,7 +8,7 @@ import { Sha1 } from "../util/sha1.ts";
import { writeResponse } from "../http/io.ts"; import { writeResponse } from "../http/io.ts";
import { TextProtoReader } from "../textproto/mod.ts"; import { TextProtoReader } from "../textproto/mod.ts";
import { Deferred, deferred } from "../util/async.ts"; import { Deferred, deferred } from "../util/async.ts";
import { assertNotEOF } from "../testing/asserts.ts"; import { assert } from "../testing/asserts.ts";
import { concat } from "../bytes/mod.ts"; import { concat } from "../bytes/mod.ts";
import Conn = Deno.Conn; import Conn = Deno.Conn;
import Writer = Deno.Writer; import Writer = Deno.Writer;
@ -149,7 +149,8 @@ export async function writeFrame(
* @throws `Error` Frame is invalid * @throws `Error` Frame is invalid
*/ */
export async function readFrame(buf: BufReader): Promise<WebSocketFrame> { export async function readFrame(buf: BufReader): Promise<WebSocketFrame> {
let b = assertNotEOF(await buf.readByte()); let b = await buf.readByte();
assert(b !== null);
let isLastFrame = false; let isLastFrame = false;
switch (b >>> 4) { switch (b >>> 4) {
case 0b1000: case 0b1000:
@ -163,25 +164,28 @@ export async function readFrame(buf: BufReader): Promise<WebSocketFrame> {
} }
const opcode = b & 0x0f; const opcode = b & 0x0f;
// has_mask & payload // has_mask & payload
b = assertNotEOF(await buf.readByte()); b = await buf.readByte();
assert(b !== null);
const hasMask = b >>> 7; const hasMask = b >>> 7;
let payloadLength = b & 0b01111111; let payloadLength = b & 0b01111111;
if (payloadLength === 126) { if (payloadLength === 126) {
const l = assertNotEOF(await readShort(buf)); const l = await readShort(buf);
assert(l !== null);
payloadLength = l; payloadLength = l;
} else if (payloadLength === 127) { } else if (payloadLength === 127) {
const l = assertNotEOF(await readLong(buf)); const l = await readLong(buf);
assert(l !== null);
payloadLength = Number(l); payloadLength = Number(l);
} }
// mask // mask
let mask: Uint8Array | undefined; let mask: Uint8Array | undefined;
if (hasMask) { if (hasMask) {
mask = new Uint8Array(4); mask = new Uint8Array(4);
assertNotEOF(await buf.readFull(mask)); assert((await buf.readFull(mask)) !== null);
} }
// payload // payload
const payload = new Uint8Array(payloadLength); const payload = new Uint8Array(payloadLength);
assertNotEOF(await buf.readFull(payload)); assert((await buf.readFull(payload)) !== null);
return { return {
isLastFrame, isLastFrame,
opcode, opcode,
@ -479,7 +483,7 @@ export async function handshake(
const tpReader = new TextProtoReader(bufReader); const tpReader = new TextProtoReader(bufReader);
const statusLine = await tpReader.readLine(); const statusLine = await tpReader.readLine();
if (statusLine === Deno.EOF) { if (statusLine === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }
const m = statusLine.match(/^(?<version>\S+) (?<statusCode>\S+) /); const m = statusLine.match(/^(?<version>\S+) (?<statusCode>\S+) /);
@ -497,7 +501,7 @@ export async function handshake(
} }
const responseHeaders = await tpReader.readMIMEHeader(); const responseHeaders = await tpReader.readMIMEHeader();
if (responseHeaders === Deno.EOF) { if (responseHeaders === null) {
throw new Deno.errors.UnexpectedEof(); throw new Deno.errors.UnexpectedEof();
} }

View file

@ -278,7 +278,7 @@ function dummyConn(r: Reader, w: Writer): Conn {
rid: -1, rid: -1,
closeRead: (): void => {}, closeRead: (): void => {},
closeWrite: (): void => {}, closeWrite: (): void => {},
read: (x): Promise<number | Deno.EOF> => r.read(x), read: (x): Promise<number | null> => r.read(x),
write: (x): Promise<number> => w.write(x), write: (x): Promise<number> => w.write(x),
close: (): void => {}, close: (): void => {},
localAddr: { transport: "tcp", hostname: "0.0.0.0", port: 0 }, localAddr: { transport: "tcp", hostname: "0.0.0.0", port: 0 },
@ -335,8 +335,8 @@ test("[ws] createSecKeyHasCorrectLength", () => {
test("[ws] WebSocket should throw `Deno.errors.ConnectionReset` when peer closed connection without close frame", async () => { test("[ws] WebSocket should throw `Deno.errors.ConnectionReset` when peer closed connection without close frame", async () => {
const buf = new Buffer(); const buf = new Buffer();
const eofReader: Deno.Reader = { const eofReader: Deno.Reader = {
read(_: Uint8Array): Promise<number | Deno.EOF> { read(_: Uint8Array): Promise<number | null> {
return Promise.resolve(Deno.EOF); return Promise.resolve(null);
}, },
}; };
const conn = dummyConn(eofReader, buf); const conn = dummyConn(eofReader, buf);
@ -353,8 +353,8 @@ test("[ws] WebSocket should throw `Deno.errors.ConnectionReset` when peer closed
test("[ws] WebSocket shouldn't throw `Deno.errors.UnexpectedEof` on recive()", async () => { test("[ws] WebSocket shouldn't throw `Deno.errors.UnexpectedEof` on recive()", async () => {
const buf = new Buffer(); const buf = new Buffer();
const eofReader: Deno.Reader = { const eofReader: Deno.Reader = {
read(_: Uint8Array): Promise<number | Deno.EOF> { read(_: Uint8Array): Promise<number | null> {
return Promise.resolve(Deno.EOF); return Promise.resolve(null);
}, },
}; };
const conn = dummyConn(eofReader, buf); const conn = dummyConn(eofReader, buf);

View file

@ -13,7 +13,7 @@ async function handle(conn: Deno.Conn): Promise<void> {
try { try {
while (true) { while (true) {
const r = await conn.read(buffer); const r = await conn.read(buffer);
if (r === Deno.EOF) { if (r === null) {
break; break;
} }
await conn.write(response); await conn.write(response);