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
48 changes: 9 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
[![Build Status][ci-image]][ci-url]
[![Coverage Status][coveralls-image]][coveralls-url]

Create and parse HTTP Content-Type header according to RFC 7231
Create and parse HTTP `Content-Type` header.

## Installation

```sh
$ npm install content-type
npm install content-type
```

## API
Expand All @@ -26,38 +26,12 @@ const contentType = require("content-type");
const obj = contentType.parse("image/svg+xml; charset=utf-8");
```

Parse a `Content-Type` header. This will return an object with the following
properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`):
Parse a `Content-Type` header. This will return an object with the following properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`):

- `type`: The media type (the type and subtype, always lower case).
Example: `'image/svg+xml'`
- `type`: The media type. Example: `'image/svg+xml'`.
- `parameters`: An optional object of the parameters in the media type (parameter name is always lower case). Example: `{charset: 'utf-8'}`.

- `parameters`: An object of the parameters in the media type (name of parameter
always lower case). Example: `{charset: 'utf-8'}`

Throws a `TypeError` if the string is missing or invalid.

### contentType.parse(req)

```js
const obj = contentType.parse(req);
```

Parse the `Content-Type` header from the given `req`. Short-cut for
`contentType.parse(req.headers['content-type'])`.

Throws a `TypeError` if the `Content-Type` header is missing or invalid.

### contentType.parse(res)

```js
const obj = contentType.parse(res);
```

Parse the `Content-Type` header set on the given `res`. Short-cut for
`contentType.parse(res.getHeader('content-type'))`.

Throws a `TypeError` if the `Content-Type` header is missing or invalid.
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.

### contentType.format(obj)

Expand All @@ -68,14 +42,10 @@ const str = contentType.format({
});
```

Format an object into a `Content-Type` header. This will return a string of the
content type for the given object with the following properties (examples are
shown that produce the string `'image/svg+xml; charset=utf-8'`):

- `type`: The media type (will be lower-cased). Example: `'image/svg+xml'`
Format an object into a `Content-Type` header. This will return a string of the content type for the given object with the following properties (examples are shown that produce the string `'image/svg+xml; charset=utf-8'`):

- `parameters`: An object of the parameters in the media type (name of the
parameter will be lower-cased). Example: `{charset: 'utf-8'}`
- `type`: The media type (will be lower-cased). Example: `'image/svg+xml'`.
- `parameters`: An optional object of the parameters in the media type. Example: `{charset: 'utf-8'}`.

Throws a `TypeError` if the object contains an invalid type or parameter names.

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"http",
"req",
"res",
"rfc7231"
"rfc7231",
"rfc9110"
],
"repository": "jshttp/content-type",
"funding": {
Expand All @@ -24,6 +25,7 @@
"dist/"
],
"scripts": {
"bench": "vitest bench",
"build": "ts-scripts build",
"format": "ts-scripts format",
"lint": "ts-scripts lint",
Expand Down
26 changes: 5 additions & 21 deletions src/format.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,42 +41,26 @@ describe("format(obj)", function () {
type: "text/html",
parameters: { charset: "utf-8", foo: "bar", bar: "baz" },
});
assert.strictEqual(str, "text/html; bar=baz; charset=utf-8; foo=bar");
});

it("should require argument", function () {
assert.throws(
format.bind(null, undefined as any),
/argument obj is required/,
);
});

it("should reject non-objects", function () {
assert.throws(format.bind(null, 7 as any), /argument obj is required/);
});
Comment thread
blakeembrey marked this conversation as resolved.

it("should require type", function () {
const obj = {};
assert.throws(format.bind(null, obj as any), /invalid type/);
assert.strictEqual(str, "text/html; charset=utf-8; foo=bar; bar=baz");
});

it("should reject invalid type", function () {
const obj = { type: "text/" };
assert.throws(format.bind(null, obj), /invalid type/);
assert.throws(format.bind(null, obj), /Invalid type: text\//);
});

it("should reject invalid type with LWS", function () {
const obj = { type: " text/html" };
assert.throws(format.bind(null, obj), /invalid type/);
assert.throws(format.bind(null, obj), /Invalid type: text\/html/);
});

it("should reject invalid parameter name", function () {
const obj = { type: "image/svg", parameters: { "foo/": "bar" } };
assert.throws(format.bind(null, obj), /invalid parameter name/);
assert.throws(format.bind(null, obj), /Invalid parameter name: foo\//);
});

it("should reject invalid parameter value", function () {
const obj = { type: "image/svg", parameters: { foo: "bar\u0000" } };
assert.throws(format.bind(null, obj), /invalid parameter value/);
assert.throws(format.bind(null, obj), /Invalid parameter value: bar\u0000/);
});
});
71 changes: 71 additions & 0 deletions src/index.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { bench, describe } from "vitest";
import { format, parse } from "./index";

describe("parse", () => {
const BASIC_HEADER = "text/html";
const PARAMS_HEADER = "application/json; charset=utf-8; foo=bar; version=1";
const QUOTED_HEADER =
'text/plain; filename="report\\"-2026.csv"; foo=bar; version=1';
const OWS_HEADER =
"application/json; \t charset = utf-8 ;\t foo = bar ; version = 1";
const MANY_PARAMS_HEADER =
"application/json; a=1; b=2; c=3; d=4; e=5; f=6; g=7; h=8; i=9; j=10";
const ESCAPED_HEAVY_HEADER =
'text/plain; filename="quarterly\\\\report\\"-2026.csv"; path="C:\\\\temp\\\\files"; note="a\\\\b\\\\c\\\\d"; version=1';

bench("basic", () => {
parse(BASIC_HEADER);
});

bench("simple parameters", () => {
parse(PARAMS_HEADER);
});

bench("quoted and escaped parameters", () => {
parse(QUOTED_HEADER);
});

bench("OWS-heavy parameters", () => {
parse(OWS_HEADER);
});

bench("many parameters", () => {
parse(MANY_PARAMS_HEADER);
});

bench("escape-heavy quoted parameters", () => {
parse(ESCAPED_HEAVY_HEADER);
});
});

describe("format", () => {
const BASIC_OBJECT = { type: "text/html" };
const PARAMS_OBJECT = {
type: "application/json",
parameters: {
charset: "utf-8",
profile: "urn:example:v1",
version: "1",
},
};
const QUOTED_OBJECT = {
type: "text/plain",
parameters: {
filename: 'report"-2026.csv',
foo: "test=bar",
q: "0.9",
},
};

bench("basic", () => {
format(BASIC_OBJECT);
});

bench("simple parameters", () => {
format(PARAMS_OBJECT);
});

bench("quoted and escaped parameters", () => {
format(QUOTED_OBJECT);
});
});
Loading
Loading