diff --git a/media_types/_util.ts b/media_types/_util.ts index 50d3282991bd..360809c96eec 100644 --- a/media_types/_util.ts +++ b/media_types/_util.ts @@ -131,7 +131,7 @@ function isTokenChar(r: string): boolean { return code > 0x20 && code < 0x7f && !isTSpecial(r); } -function isTSpecial(r: string): boolean { +export function isTSpecial(r: string): boolean { return r[0] ? `()<>@,;:\\"/[]?=`.includes(r[0]) : false; } diff --git a/media_types/_util_test.ts b/media_types/_util_test.ts index afcf81cf1633..4dc34b8b7685 100644 --- a/media_types/_util_test.ts +++ b/media_types/_util_test.ts @@ -1,7 +1,15 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { assertEquals } from "../assert/mod.ts"; -import { consumeMediaParam, consumeToken, consumeValue } from "./_util.ts"; +import { + consumeMediaParam, + consumeToken, + consumeValue, + decode2331Encoding, + isIterator, + isToken, + isTSpecial, +} from "./_util.ts"; Deno.test({ name: "consumeToken()", @@ -22,6 +30,10 @@ Deno.test({ name: "consumeValue()", fn() { const fixtures = [ + ["", "", ""], + [`"\n"foo`, "", `"\n"foo`], + [`"\r"foo`, "", `"\r"foo`], + [`"\\\\;"`, "\\;", ""], ["foo bar", "foo", " bar"], ["bar", "bar", ""], [" bar ", "", " bar "], @@ -47,6 +59,10 @@ Deno.test({ name: "consumeMediaParam()", fn() { const fixtures = [ + ["", "", "", ""], + ["foo=bar", "", "", "foo=bar"], + [";", "", "", ";"], + [";foo", "", "", ";foo"], [" ; foo=bar", "foo", "bar", ""], ["; foo=bar", "foo", "bar", ""], [";foo=bar", "foo", "bar", ""], @@ -69,3 +85,87 @@ Deno.test({ } }, }); + +Deno.test({ + name: "decode2331Encoding()", + fn() { + const fixtures = [ + ["", undefined], + ["foo", undefined], + [`foo'bar'baz`, undefined], + [`us-ascii'en-us'`, undefined], + [`us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A`, "This is ***fun***"], + [`UTF-8''foo-a%cc%88.html`, "foo-ä.html"], + ] as const; + for (const [fixture, expected] of fixtures) { + assertEquals(decode2331Encoding(fixture), expected); + } + }, +}); + +Deno.test({ + name: "isIterator()", + fn() { + const fixtures = [ + [null, false], + [undefined, false], + [{}, false], + ["", true], + [[], true], + [Object.entries({}), true], + ] as const; + for (const [fixture, expected] of fixtures) { + assertEquals(isIterator(fixture), expected); + } + }, +}); + +Deno.test({ + name: "isToken()", + fn() { + const fixtures = [ + ["", false], + [";", false], + ["\\", false], + ["foo", true], + ] as const; + for (const [fixture, expected] of fixtures) { + assertEquals(isToken(fixture), expected); + } + }, +}); + +Deno.test({ + name: "isTSpecial()", + fn() { + const fixtues = [ + ["", false], + [` ()<>@,;:\\"/[]?=`, false], + ["(", true], + [")", true], + ["<", true], + [">", true], + ["@", true], + [",", true], + [";", true], + [":", true], + ["\\", true], + ['"', true], + ["/", true], + ["[", true], + ["]", true], + ["?", true], + ["=", true], + [" ", false], + ["\t", false], + ["\n", false], + ["\r", false], + ["\f", false], + ["\v", false], + ["foo", false], + ] as const; + for (const [fixture, expected] of fixtues) { + assertEquals(isTSpecial(fixture), expected); + } + }, +}); diff --git a/media_types/content_type_test.ts b/media_types/content_type_test.ts index 8eeaed408f35..51df27da4006 100644 --- a/media_types/content_type_test.ts +++ b/media_types/content_type_test.ts @@ -7,10 +7,12 @@ Deno.test({ name: "contentType()", fn() { const fixtures = [ + [" ; charset=UTF-8", undefined], [".json", "application/json; charset=UTF-8"], ["text/html", "text/html; charset=UTF-8"], ["txt", "text/plain; charset=UTF-8"], ["text/plain; charset=ISO-8859-1", "text/plain; charset=ISO-8859-1"], + ["text/plan; charset", undefined], ["foo", undefined], ["file.json", undefined], ["application/foo", "application/foo"], diff --git a/media_types/extensions_by_type_test.ts b/media_types/extensions_by_type_test.ts index 7306ecbd75bb..f5dede5e19de 100644 --- a/media_types/extensions_by_type_test.ts +++ b/media_types/extensions_by_type_test.ts @@ -7,6 +7,7 @@ Deno.test({ name: "extensionsByType()", fn() { const fixtures: [string, string[] | undefined][] = [ + ["text/plain; charset", undefined], ["image/gif", ["gif"]], ["application/javascript", ["js", "mjs"]], ["text/html; charset=UTF-8", ["html", "htm", "shtml"]], diff --git a/media_types/format_media_type_test.ts b/media_types/format_media_type_test.ts index 4ceb97322f25..bdcfcba27984 100644 --- a/media_types/format_media_type_test.ts +++ b/media_types/format_media_type_test.ts @@ -7,6 +7,7 @@ Deno.test({ name: "formatMediaType()", fn() { const fixtures = [ + ["/", undefined, ""], ["noslash", { X: "Y" }, "noslash; x=Y"], ["foo bar/baz", undefined, ""], ["foo/bar baz", undefined, ""], @@ -58,6 +59,7 @@ Deno.test({ ], ["foo/bar", { "0": "'", "9": "'" }, "foo/bar; 0='; 9='"], ["foo", { "bar": "" }, `foo; bar=""`], + ["foo/bar", [], "foo/bar"], ] as const; for (const [type, param, expected] of fixtures) { assertEquals(formatMediaType(type, param), expected); diff --git a/media_types/get_charset_test.ts b/media_types/get_charset_test.ts index 466ea7f3be23..ba8d78a9a877 100644 --- a/media_types/get_charset_test.ts +++ b/media_types/get_charset_test.ts @@ -7,6 +7,8 @@ Deno.test({ name: "getCharset()", fn() { const fixtures = [ + [";", undefined], + ["text/plain; charset", undefined], ["text/plain", "UTF-8"], ["text/html", "UTF-8"], ["application/foo", undefined], diff --git a/media_types/parse_media_type_test.ts b/media_types/parse_media_type_test.ts index 5195578149d8..d76748aec23c 100644 --- a/media_types/parse_media_type_test.ts +++ b/media_types/parse_media_type_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../assert/mod.ts"; +import { assertEquals, assertThrows } from "../assert/mod.ts"; import { parseMediaType } from "./mod.ts"; Deno.test({ @@ -91,3 +91,40 @@ Deno.test({ } }, }); + +Deno.test({ + name: "parseMediaType() throws on invalid media type", + fn() { + const fixtures = [ + `form-data; foo`, + `form-data; foo="bar"; baz`, + ] as const; + for (const fixture of fixtures) { + assertThrows( + () => { + parseMediaType(fixture); + }, + TypeError, + "Invalid media parameter.", + ); + } + }, +}); + +Deno.test({ + name: "parseMediaType() throws on duplicate keys", + fn() { + const fixtures = [ + `form-data; foo="bar"; foo="baz"`, + ] as const; + for (const fixture of fixtures) { + assertThrows( + () => { + parseMediaType(fixture); + }, + TypeError, + "Duplicate key parsed.", + ); + } + }, +});