Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ npm install content-type
const contentType = require("content-type");
```

### contentType.parse(string)
### contentType.parse(string, options?)

```js
const obj = contentType.parse("image/svg+xml; charset=utf-8");
Expand All @@ -33,6 +33,10 @@ Parse a `Content-Type` header. This will return an object with the following pro

The parser is lenient, but will throw a `TypeError` when unable to parse a parameter due to ambiguity. E.g. `foo="` where the quote is unterminated.

#### Options

- `parameters` (default: `true`): Set to `false` to skip parsing parameters, only returns `type`.

### contentType.format(obj)

```js
Expand Down
4 changes: 4 additions & 0 deletions src/index.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ describe("parse", () => {
parse(PARAMS_HEADER);
});

bench("simple parameters (options.parameters = false)", () => {
parse(PARAMS_HEADER, { parameters: false });
});

bench("quoted and escaped parameters", () => {
parse(QUOTED_HEADER);
});
Expand Down
28 changes: 19 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,27 @@ export function format(obj: ContentType): string {
return result;
}

/**
* Options for parsing a `Content-Type` header.
*/
export interface ParseOptions {
parameters?: boolean;
}

/**
* Parse a `Content-Type` header.
*/
export function parse(header: string): ContentType {
export function parse(header: string, options?: ParseOptions): ContentType {
const len = header.length;
const semiIndex = header.indexOf(";");
const end = semiIndex !== -1 ? semiIndex : len;
const valueStart = skipOWS(header, 0, end);
const valueEnd = trailingOWS(header, valueStart, end);
const type = header.slice(valueStart, valueEnd).toLowerCase();

if (semiIndex === -1) return { type };
if (semiIndex === -1 || options?.parameters === false) return { type };

const parameters = parseParameters(header, end, len);
const parameters = parseParameters(header, end + 1, len);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some readability tweaks in case we want to expose parseParameters later. No change in overall performance by making this change now.

return { type, parameters };
}

Expand All @@ -80,21 +87,24 @@ function parseParameters(
const parameters: Record<string, string> = Object.create(null);

parameter: while (index < len) {
index = skipOWS(header, index + 1, len);
index = skipOWS(header, index, len);

const keyStart = index;

while (index < len) {
const char = header[index];
if (char === ";") continue parameter;
if (char === ";") {
index++;
continue parameter;
}

if (char === "=") {
const keyEnd = trailingOWS(header, keyStart, index);
const key = header.slice(keyStart, keyEnd).toLowerCase();

index = skipOWS(header, index + 1, len);

if (header[index] === '"') {
if (index < len && header[index] === '"') {
index++;

let value = "";
Expand All @@ -103,12 +113,11 @@ function parseParameters(
if (char === '"') {
index = skipOWS(header, index, len);
if (index < len && header[index] !== ";") {
throw new TypeError(
`Unexpected characters after parameter at index ${index}`,
);
throw new TypeError(`Unexpected character at index ${index}`);
}

parameters[key] = value;
index++;
continue parameter;
}

Expand All @@ -131,6 +140,7 @@ function parseParameters(

const valueEnd = trailingOWS(header, valueStart, index);
parameters[key] = header.slice(valueStart, valueEnd);
index++;
continue parameter;
}

Expand Down
11 changes: 10 additions & 1 deletion src/parse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ describe("parse(string)", function () {
it("should error on non-OWS after closing quote", function () {
assert.throws(
parse.bind(null, 'text/plain; foo="bar"baz'),
/Unexpected characters after parameter at index 21/,
/Unexpected character at index 21/,
);
});

Expand All @@ -213,4 +213,13 @@ describe("parse(string)", function () {
},
});
});

it("should skip parsing parameters when options.parameters is false", function () {
const type = parse("text/html; charset=utf-8; foo=bar", {
parameters: false,
});
assert.deepEqual(type, {
type: "text/html",
});
});
});
Loading