http-headers
Advanced tools
Comparing version 1.2.0 to 2.0.0
112
index.js
'use strict' | ||
var strip = require('strip-lines') | ||
var nextLine = require('next-line') | ||
@@ -10,16 +9,35 @@ | ||
var startLine = /^[A-Z_]+(\/\d\.\d)? / | ||
var requestLine = /^([A-Z_]+) (.+) [A-Z]+\/(\d)\.(\d)$/ | ||
var statusLine = /^[A-Z]+\/(\d)\.(\d) (\d{3}) (.*)$/ | ||
module.exports = function (str) { | ||
return parse(normalize(str)) | ||
module.exports = function (data, onlyHeaders) { | ||
return parse(normalize(data), onlyHeaders) | ||
} | ||
function normalize (str) { | ||
if (str && str._header) str = str._header // extra headers from http.ServerResponse object | ||
if (!str || typeof str.toString !== 'function') return '' | ||
str = str.toString().trim() | ||
if (startLine.test(str)) str = strip(str, 1) | ||
return str | ||
function parse (str, onlyHeaders) { | ||
var line = firstLine(str) | ||
var match | ||
if (onlyHeaders && startLine.test(line)) { | ||
return parseHeaders(str) | ||
} else if ((match = line.match(requestLine)) !== null) { | ||
return { | ||
method: match[1], | ||
path: match[2], | ||
version: { major: parseInt(match[3], 10), minor: parseInt(match[4], 10) }, | ||
headers: parseHeaders(str) | ||
} | ||
} else if ((match = line.match(statusLine)) !== null) { | ||
return { | ||
version: { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) }, | ||
statusCode: parseInt(match[3], 10), | ||
statusMessage: match[4], | ||
headers: parseHeaders(str) | ||
} | ||
} else { | ||
return parseHeaders(str) | ||
} | ||
} | ||
function parse (str) { | ||
function parseHeaders (str) { | ||
var headers = {} | ||
@@ -30,2 +48,4 @@ var next = nextLine(str) | ||
if (startLine.test(line)) line = next() | ||
while (line) { | ||
@@ -39,3 +59,3 @@ // subsequent lines in multi-line headers start with whitespace | ||
if (name) addMessageHeader() | ||
if (name) addHeaderLine(name, value, headers) | ||
@@ -49,11 +69,71 @@ index = line.indexOf(':') | ||
if (name) addMessageHeader() | ||
if (name) addHeaderLine(name, value, headers) | ||
return headers | ||
} | ||
function addMessageHeader () { | ||
name = name.toLowerCase() | ||
if (name in headers) headers[name] += ', ' + value | ||
else headers[name] = value | ||
function normalize (str) { | ||
if (str && str._header) str = str._header // extra headers from http.ServerResponse object | ||
if (!str || typeof str.toString !== 'function') return '' | ||
return str.toString().trim() | ||
} | ||
function firstLine (str) { | ||
return str.slice(0, str.indexOf('\r\n')) | ||
} | ||
// The following function is lifted from: | ||
// https://github.com/nodejs/node/blob/f1294f5bfd7f02bce8029818be9c92de59749137/lib/_http_incoming.js#L116-L170 | ||
// | ||
// Add the given (field, value) pair to the message | ||
// | ||
// Per RFC2616, section 4.2 it is acceptable to join multiple instances of the | ||
// same header with a ', ' if the header in question supports specification of | ||
// multiple values this way. If not, we declare the first instance the winner | ||
// and drop the second. Extended header fields (those beginning with 'x-') are | ||
// always joined. | ||
function addHeaderLine (field, value, dest) { | ||
field = field.toLowerCase() | ||
switch (field) { | ||
// Array headers: | ||
case 'set-cookie': | ||
if (dest[field] !== undefined) { | ||
dest[field].push(value) | ||
} else { | ||
dest[field] = [value] | ||
} | ||
break | ||
// list is taken from: | ||
// https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp | ||
case 'content-type': | ||
case 'content-length': | ||
case 'user-agent': | ||
case 'referer': | ||
case 'host': | ||
case 'authorization': | ||
case 'proxy-authorization': | ||
case 'if-modified-since': | ||
case 'if-unmodified-since': | ||
case 'from': | ||
case 'location': | ||
case 'max-forwards': | ||
case 'retry-after': | ||
case 'etag': | ||
case 'last-modified': | ||
case 'server': | ||
case 'age': | ||
case 'expires': | ||
// drop duplicates | ||
if (dest[field] === undefined) dest[field] = value | ||
break | ||
default: | ||
// make comma-separated list | ||
if (typeof dest[field] === 'string') { | ||
dest[field] += ', ' + value | ||
} else { | ||
dest[field] = value | ||
} | ||
} | ||
} |
{ | ||
"name": "http-headers", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"description": "Parse http headers", | ||
@@ -14,4 +14,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"next-line": "^1.0.0", | ||
"strip-lines": "^1.0.1" | ||
"next-line": "^1.0.0" | ||
}, | ||
@@ -39,5 +38,5 @@ "devDependencies": { | ||
"coordinates": [ | ||
55.68773059999999, | ||
12.5956059 | ||
55.6876679, | ||
12.5955582 | ||
] | ||
} |
127
README.md
@@ -6,7 +6,7 @@ # http-headers | ||
Extract and parse headers from an HTTP request or reponse. | ||
Parse the start-line and headers from an HTTP request or reponse. | ||
Converts: | ||
``` | ||
```http | ||
HTTP/1.1 200 OK | ||
@@ -23,5 +23,12 @@ Date: Tue, 10 Jun 2014 07:19:27 GMT | ||
```js | ||
{ date: 'Tue, 10 Jun 2014 07:19:27 GMT', | ||
connection: 'keep-alive', | ||
'transfer-encoding': 'chunked' } | ||
{ | ||
version: { major: 1, minor: 1 }, | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
headers: { | ||
date: 'Tue, 10 Jun 2014 07:19:27 GMT', | ||
connection: 'keep-alive', | ||
'transfer-Encoding': 'chunked' | ||
} | ||
} | ||
``` | ||
@@ -31,3 +38,2 @@ | ||
- Auto-detects and ignores HTTP start-line if present | ||
- Auto-detects and ignores body if present | ||
@@ -38,3 +44,3 @@ - Fully [RFC 2068](http://www.rfc-base.org/txt/rfc-2068.txt) compliant | ||
- Support multi-line headers (lines will be joined with a space) | ||
- Support repeating headers (values will be joined with `, `) | ||
- Support repeating headers | ||
@@ -44,3 +50,3 @@ ## Installation | ||
``` | ||
npm install http-headers | ||
npm install http-headers --save | ||
``` | ||
@@ -62,3 +68,3 @@ | ||
// parse incoming data as an HTTP request and extra HTTP headers | ||
console.log('Request headers:', httpHeaders(data)) | ||
console.log(httpHeaders(data)) | ||
}) | ||
@@ -79,3 +85,3 @@ }).listen(8080) | ||
res.end('Hello World') | ||
console.log('Response headers:', httpHeaders(res)) | ||
console.log(httpHeaders(res)) | ||
}).listen(8080) | ||
@@ -102,11 +108,106 @@ ``` | ||
```js | ||
httpHeaders(data[, onlyHeaders]) | ||
``` | ||
httpHeaders([ string | buffer | http.ServerReponse ]) | ||
Arguments: | ||
- `data` - A string, buffer or instance of `http.ServerReponse` | ||
- `onlyHeaders` - An optional boolean. If `true`, only the headers | ||
object will be returned. Defaults to `false` | ||
### Request example | ||
If given a request as input: | ||
```http | ||
GET /foo HTTP/1.1 | ||
Date: Tue, 10 Jun 2014 07:19:27 GMT | ||
Connection: keep-alive | ||
Transfer-Encoding: chunked | ||
Hello World | ||
``` | ||
The module returns a JavaScript object with each element representing a | ||
parsed header. All header names are lowercased. | ||
Returns: | ||
```js | ||
{ | ||
method: 'GET', | ||
path: '/foo', | ||
version: { major: 1, minor: 1 }, | ||
headers: { | ||
date: 'Tue, 10 Jun 2014 07:19:27 GMT', | ||
connection: 'keep-alive', | ||
'transfer-Encoding': 'chunked' | ||
} | ||
} | ||
``` | ||
### Response example | ||
If given a request as input: | ||
```http | ||
HTTP/1.1 200 OK | ||
Date: Tue, 10 Jun 2014 07:19:27 GMT | ||
Connection: keep-alive | ||
Transfer-Encoding: chunked | ||
Hello World | ||
``` | ||
Returns: | ||
```js | ||
{ | ||
version: { major: 1, minor: 1 }, | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
headers: { | ||
date: 'Tue, 10 Jun 2014 07:19:27 GMT', | ||
connection: 'keep-alive', | ||
'transfer-Encoding': 'chunked' | ||
} | ||
} | ||
``` | ||
### `onlyHeaders` example | ||
If the optional second argument is set to `true`, only headers are | ||
returned no matter the type of input: | ||
```js | ||
{ | ||
date: 'Tue, 10 Jun 2014 07:19:27 GMT', | ||
connection: 'keep-alive', | ||
'transfer-Encoding': 'chunked' | ||
} | ||
``` | ||
### No Start-Line | ||
If the `data` given does not contain an HTTP Start-Line, only the | ||
headers are returned, even if the `onlyHeaders` argument is `false`: | ||
```http | ||
Date: Tue, 10 Jun 2014 07:19:27 GMT | ||
Connection: keep-alive | ||
Transfer-Encoding: chunked | ||
Hello World | ||
``` | ||
Returns: | ||
```js | ||
{ | ||
date: 'Tue, 10 Jun 2014 07:19:27 GMT', | ||
connection: 'keep-alive', | ||
'transfer-Encoding': 'chunked' | ||
} | ||
``` | ||
## License | ||
MIT |
65
test.js
@@ -12,2 +12,5 @@ 'use strict' | ||
'Transfer-Encoding: chunked\r\n' + | ||
'Age: foo\r\n' + | ||
'Age: bar\r\n' + | ||
'Set-Cookie: cookie\r\n' + | ||
'X-List: A\r\n' + | ||
@@ -21,12 +24,27 @@ 'X-Multi-Line-Header: Foo\r\n' + | ||
var result = { | ||
var headerResult = { | ||
date: 'Tue, 10 Jun 2014 07:29:20 GMT', | ||
connection: 'keep-alive', | ||
'transfer-encoding': 'chunked', | ||
age: 'foo', | ||
'set-cookie': ['cookie'], | ||
'x-list': 'A, B', | ||
'x-multi-line-header': 'Foo Bar' | ||
} | ||
var responseResult = { | ||
version: { major: 1, minor: 1 }, | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
headers: headerResult | ||
} | ||
var requestResult = { | ||
method: 'GET', | ||
path: '/foo', | ||
version: { major: 1, minor: 1 }, | ||
headers: headerResult | ||
} | ||
test('no argument', function (t) { | ||
t.deepEqual(httpHeaders(), {}) | ||
t.deepEqual(httpHeaders(undefined, true), {}) | ||
t.end() | ||
@@ -37,2 +55,3 @@ }) | ||
t.deepEqual(httpHeaders(''), {}) | ||
t.deepEqual(httpHeaders('', true), {}) | ||
t.end() | ||
@@ -43,2 +62,3 @@ }) | ||
t.deepEqual(httpHeaders({}), {}) | ||
t.deepEqual(httpHeaders({}, true), {}) | ||
t.end() | ||
@@ -49,2 +69,3 @@ }) | ||
t.deepEqual(httpHeaders(new Buffer('')), {}) | ||
t.deepEqual(httpHeaders(new Buffer(''), true), {}) | ||
t.end() | ||
@@ -54,6 +75,10 @@ }) | ||
test('start-line + header', function (t) { | ||
t.deepEqual(httpHeaders(requestLine + msgHeaders), result) | ||
t.deepEqual(httpHeaders(statusLine + msgHeaders), result) | ||
t.deepEqual(httpHeaders(new Buffer(requestLine + msgHeaders)), result) | ||
t.deepEqual(httpHeaders(new Buffer(statusLine + msgHeaders)), result) | ||
t.deepEqual(httpHeaders(requestLine + msgHeaders), requestResult) | ||
t.deepEqual(httpHeaders(statusLine + msgHeaders), responseResult) | ||
t.deepEqual(httpHeaders(new Buffer(requestLine + msgHeaders)), requestResult) | ||
t.deepEqual(httpHeaders(new Buffer(statusLine + msgHeaders)), responseResult) | ||
t.deepEqual(httpHeaders(requestLine + msgHeaders, true), headerResult) | ||
t.deepEqual(httpHeaders(statusLine + msgHeaders, true), headerResult) | ||
t.deepEqual(httpHeaders(new Buffer(requestLine + msgHeaders), true), headerResult) | ||
t.deepEqual(httpHeaders(new Buffer(statusLine + msgHeaders), true), headerResult) | ||
t.end() | ||
@@ -63,4 +88,6 @@ }) | ||
test('headers only', function (t) { | ||
t.deepEqual(httpHeaders(msgHeaders), result) | ||
t.deepEqual(httpHeaders(new Buffer(msgHeaders)), result) | ||
t.deepEqual(httpHeaders(msgHeaders), headerResult) | ||
t.deepEqual(httpHeaders(new Buffer(msgHeaders)), headerResult) | ||
t.deepEqual(httpHeaders(msgHeaders, true), headerResult) | ||
t.deepEqual(httpHeaders(new Buffer(msgHeaders), true), headerResult) | ||
t.end() | ||
@@ -70,6 +97,10 @@ }) | ||
test('full http response', function (t) { | ||
t.deepEqual(httpHeaders(requestMsg), result) | ||
t.deepEqual(httpHeaders(responseMsg), result) | ||
t.deepEqual(httpHeaders(new Buffer(requestMsg)), result) | ||
t.deepEqual(httpHeaders(new Buffer(responseMsg)), result) | ||
t.deepEqual(httpHeaders(requestMsg), requestResult) | ||
t.deepEqual(httpHeaders(responseMsg), responseResult) | ||
t.deepEqual(httpHeaders(new Buffer(requestMsg)), requestResult) | ||
t.deepEqual(httpHeaders(new Buffer(responseMsg)), responseResult) | ||
t.deepEqual(httpHeaders(requestMsg, true), headerResult) | ||
t.deepEqual(httpHeaders(responseMsg, true), headerResult) | ||
t.deepEqual(httpHeaders(new Buffer(requestMsg), true), headerResult) | ||
t.deepEqual(httpHeaders(new Buffer(responseMsg), true), headerResult) | ||
t.end() | ||
@@ -82,2 +113,3 @@ }) | ||
t.deepEqual(httpHeaders(res), {}) | ||
t.deepEqual(httpHeaders(res, true), {}) | ||
t.end() | ||
@@ -88,2 +120,3 @@ }) | ||
t.deepEqual(httpHeaders({ _header: undefined }), {}) | ||
t.deepEqual(httpHeaders({ _header: undefined }, true), {}) | ||
t.end() | ||
@@ -94,2 +127,3 @@ }) | ||
t.deepEqual(httpHeaders({ _header: '' }), {}) | ||
t.deepEqual(httpHeaders({ _header: '' }, true), {}) | ||
t.end() | ||
@@ -99,5 +133,12 @@ }) | ||
t.test('normal _header property', function (t) { | ||
t.deepEqual(httpHeaders({ _header: statusLine + msgHeaders }), result) | ||
t.deepEqual(httpHeaders({ _header: statusLine + msgHeaders }), responseResult) | ||
t.deepEqual(httpHeaders({ _header: statusLine + msgHeaders }, true), headerResult) | ||
t.end() | ||
}) | ||
}) | ||
test('set-cookie', function (t) { | ||
t.deepEqual(httpHeaders('Set-Cookie: foo'), { 'set-cookie': ['foo'] }) | ||
t.deepEqual(httpHeaders('Set-Cookie: foo\r\nSet-Cookie: bar'), { 'set-cookie': ['foo', 'bar'] }) | ||
t.end() | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14078
1
234
206
- Removedstrip-lines@^1.0.1
- Removedstrip-lines@1.0.1(transitive)