138 lines
3.7 KiB
JavaScript
138 lines
3.7 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
var nextLine = require('next-line')
|
||
|
|
||
|
// RFC-2068 Start-Line definitions:
|
||
|
// Request-Line: Method SP Request-URI SP HTTP-Version CRLF
|
||
|
// Status-Line: HTTP-Version SP Status-Code SP Reason-Phrase CRLF
|
||
|
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 (data, onlyHeaders) {
|
||
|
return parse(normalize(data), onlyHeaders)
|
||
|
}
|
||
|
|
||
|
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],
|
||
|
url: 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 parseHeaders (str) {
|
||
|
var headers = {}
|
||
|
var next = nextLine(str)
|
||
|
var line = next()
|
||
|
var index, name, value
|
||
|
|
||
|
if (startLine.test(line)) line = next()
|
||
|
|
||
|
while (line) {
|
||
|
// subsequent lines in multi-line headers start with whitespace
|
||
|
if (line[0] === ' ' || line[0] === '\t') {
|
||
|
value += ' ' + line.trim()
|
||
|
line = next()
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if (name) addHeaderLine(name, value, headers)
|
||
|
|
||
|
index = line.indexOf(':')
|
||
|
name = line.substr(0, index)
|
||
|
value = line.substr(index + 1).trim()
|
||
|
|
||
|
line = next()
|
||
|
}
|
||
|
|
||
|
if (name) addHeaderLine(name, value, headers)
|
||
|
|
||
|
return headers
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
var nl = str.indexOf('\r\n')
|
||
|
if (nl === -1) return str
|
||
|
else return str.slice(0, nl)
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
}
|
||
|
}
|
||
|
}
|