diff options
author | haturau <135221985+haturatu@users.noreply.github.com> | 2024-11-20 01:20:47 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-20 01:20:47 +0900 |
commit | 85719a67e59c7aa45bead26e4942d7df8b1b42d4 (patch) | |
tree | face0aecaac53e93ce2f23b53c48859bcf1a36ec /tests/specs/run/tls_connecttls/textproto.ts | |
parent | 67697bc2e4a62a9670699fd18ad0dd8efc5bd955 (diff) | |
parent | 186b52731c6bb326c4d32905c5e732d082e83465 (diff) |
Merge branch 'denoland:main' into main
Diffstat (limited to 'tests/specs/run/tls_connecttls/textproto.ts')
-rw-r--r-- | tests/specs/run/tls_connecttls/textproto.ts | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/tests/specs/run/tls_connecttls/textproto.ts b/tests/specs/run/tls_connecttls/textproto.ts new file mode 100644 index 000000000..9e0f5f5f0 --- /dev/null +++ b/tests/specs/run/tls_connecttls/textproto.ts @@ -0,0 +1,170 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/** **Deprecated**. Use `TextLineStream` from `std/steams` for line-by-line text reading instead. + * + * A reader for dealing with low level text based protocols. + * + * Based on + * [net/textproto](https://github.com/golang/go/tree/master/src/net/textproto). + * + * @deprecated (will be removed after 0.159.0) Use `TextLineStream` from `std/steams` for line-by-line text reading instead. + * @module + */ + +import type { BufReader, ReadLineResult } from "@std/io/buf-reader"; +import { concat } from "@std/bytes/concat"; + +// Constants created for DRY +const CHAR_SPACE: number = " ".charCodeAt(0); +const CHAR_TAB: number = "\t".charCodeAt(0); +const CHAR_COLON: number = ":".charCodeAt(0); + +const WHITESPACES: Array<number> = [CHAR_SPACE, CHAR_TAB]; + +const decoder = new TextDecoder(); + +// FROM https://github.com/denoland/deno/blob/b34628a26ab0187a827aa4ebe256e23178e25d39/cli/js/web/headers.ts#L9 +const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/g; + +function str(buf: Uint8Array | null | undefined): string { + return !buf ? "" : decoder.decode(buf); +} + +/** + * @deprecated (will be removed after 0.159.0) Use `TextLineStream` from `std/steams` for line-by-line text reading instead. + */ +export class TextProtoReader { + constructor(readonly r: BufReader) {} + + /** readLine() reads a single line from the TextProtoReader, + * eliding the final \n or \r\n from the returned string. + */ + async readLine(): Promise<string | null> { + const s = await this.readLineSlice(); + return s === null ? null : str(s); + } + + /** ReadMimeHeader reads a MIME-style header from r. + * The header is a sequence of possibly continued Key: Value lines + * ending in a blank line. + * The returned map m maps CanonicalMIMEHeaderKey(key) to a + * sequence of values in the same order encountered in the input. + * + * For example, consider this input: + * + * My-Key: Value 1 + * Long-Key: Even + * Longer Value + * My-Key: Value 2 + * + * Given that input, ReadMIMEHeader returns the map: + * + * map[string][]string{ + * "My-Key": {"Value 1", "Value 2"}, + * "Long-Key": {"Even Longer Value"}, + * } + */ + async readMimeHeader(): Promise<Headers | null> { + const m = new Headers(); + let line: Uint8Array | undefined; + + // The first line cannot start with a leading space. + let buf = await this.r.peek(1); + if (buf === null) { + return null; + } else if (WHITESPACES.includes(buf[0])) { + line = (await this.readLineSlice()) as Uint8Array; + } + + buf = await this.r.peek(1); + if (buf === null) { + throw new Deno.errors.UnexpectedEof(); + } else if (WHITESPACES.includes(buf[0])) { + throw new Deno.errors.InvalidData( + `malformed MIME header initial line: ${str(line)}`, + ); + } + + while (true) { + const kv = await this.readLineSlice(); // readContinuedLineSlice + if (kv === null) throw new Deno.errors.UnexpectedEof(); + if (kv.byteLength === 0) return m; + + // Key ends at first colon + let i = kv.indexOf(CHAR_COLON); + if (i < 0) { + throw new Deno.errors.InvalidData( + `malformed MIME header line: ${str(kv)}`, + ); + } + + //let key = canonicalMIMEHeaderKey(kv.subarray(0, endKey)); + const key = str(kv.subarray(0, i)); + + // As per RFC 7230 field-name is a token, + // tokens consist of one or more chars. + // We could throw `Deno.errors.InvalidData` here, + // but better to be liberal in what we + // accept, so if we get an empty key, skip it. + if (key == "") { + continue; + } + + // Skip initial spaces in value. + i++; // skip colon + while ( + i < kv.byteLength && + (WHITESPACES.includes(kv[i])) + ) { + i++; + } + const value = str(kv.subarray(i)).replace( + invalidHeaderCharRegex, + encodeURI, + ); + + // In case of invalid header we swallow the error + // example: "Audio Mode" => invalid due to space in the key + try { + m.append(key, value); + } catch { + // Pass + } + } + } + + async readLineSlice(): Promise<Uint8Array | null> { + let line = new Uint8Array(0); + let r: ReadLineResult | null = null; + + do { + r = await this.r.readLine(); + // TODO(ry): + // This skipSpace() is definitely misplaced, but I don't know where it + // comes from nor how to fix it. + + //TODO(SmashingQuasar): Kept skipSpace to preserve behavior but it should be looked into to check if it makes sense when this is used. + + if (r !== null && this.skipSpace(r.line) !== 0) { + line = concat([line, r.line]); + } + } while (r !== null && r.more); + + return r === null ? null : line; + } + + skipSpace(l: Uint8Array): number { + let n = 0; + + for (const val of l) { + if (!WHITESPACES.includes(val)) { + n++; + } + } + + return n; + } +} |