-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
150 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,109 +1,152 @@ | ||
'use strict'; | ||
|
||
var ParseError = require('./lib/error') | ||
, ascii = require('./lib/ascii') | ||
, isDelimiter = ascii.isDelimiter | ||
, isTokenChar = ascii.isTokenChar | ||
, isExtended = ascii.isExtended | ||
, isPrint = ascii.isPrint; | ||
|
||
// | ||
// Expose the parser. | ||
// | ||
module.exports = parse; | ||
var util = require('util'); | ||
|
||
var ParseError = require('./lib/error'); | ||
var ascii = require('./lib/ascii'); | ||
|
||
var isDelimiter = ascii.isDelimiter; | ||
var isTokenChar = ascii.isTokenChar; | ||
var isExtended = ascii.isExtended; | ||
var isPrint = ascii.isPrint; | ||
|
||
/** | ||
* Unescape a string. | ||
* | ||
* @param {string} str The string to unescape. | ||
* @returns {string} A new unescaped string. | ||
* @private | ||
*/ | ||
function decode(str) { | ||
return str.replace(/\\(.)/g, '$1'); | ||
} | ||
|
||
/** | ||
* Parse the `Forwarded` header. | ||
* Build an error message when an unexpected character is found. | ||
* | ||
* @param {String} input The field value of the header. | ||
* @returns {Array<Object>} | ||
* @api public | ||
* @param {string} header The header field value. | ||
* @param {number} position The position of the unexpected character. | ||
* @returns {string} The error message. | ||
* @private | ||
*/ | ||
function parse(input) { | ||
var forwarded = {} | ||
, escape = false | ||
, quote = false | ||
, lock = false | ||
, ows = false | ||
, output = [] | ||
, token = '' | ||
, parameter | ||
, code | ||
, ch; | ||
|
||
for (var i = 0; i < input.length; i++) { | ||
code = input.charCodeAt(i); | ||
ch = input.charAt(i); | ||
|
||
if (!parameter) { | ||
if (ows && (code === 0x20/*' '*/|| code === 0x09/*'\t'*/)) { | ||
function unexpectedCharacterMessage(header, position) { | ||
return util.format( | ||
"Unexpected character '%s' at index %d", | ||
header.charAt(position), | ||
position | ||
); | ||
} | ||
|
||
/** | ||
* Parse the `Forwarded` header field value into an array of objects. | ||
* | ||
* @param {string} header The header field value. | ||
* @returns {Object[]} | ||
* @public | ||
*/ | ||
function parse(header) { | ||
var mustUnescape = false; | ||
var isEscaping = false; | ||
var inQuotes = false; | ||
var forwarded = {}; | ||
var output = []; | ||
var start = -1; | ||
var end = -1; | ||
var parameter; | ||
var code; | ||
|
||
for (var i = 0; i < header.length; i++) { | ||
code = header.charCodeAt(i); | ||
|
||
if (parameter === undefined) { | ||
if (start === -1 && (code === 0x20/*' '*/|| code === 0x09/*'\t'*/)) { | ||
continue; | ||
} | ||
if (code === 0x3D/*'='*/&& token) { | ||
parameter = token.toLowerCase(); | ||
token = ''; | ||
} else if (isTokenChar(code)) { | ||
ows = false; | ||
token += ch; | ||
|
||
if (isTokenChar(code)) { | ||
if (start === -1) start = i; | ||
} else if (code === 0x3D/*'='*/ && start !== -1) { | ||
parameter = header.slice(start, i).toLowerCase(); | ||
start = -1; | ||
} else { | ||
throw new ParseError(input, "Unexpected character '"+ ch +"'", i); | ||
throw new ParseError(unexpectedCharacterMessage(header, i), header); | ||
} | ||
} else { | ||
if (escape && (code === 0x09 || isPrint(code) || isExtended(code))) { | ||
escape = false; | ||
token += ch; | ||
if (isEscaping && (code === 0x09 || isPrint(code) || isExtended(code))) { | ||
isEscaping = false; | ||
} else if (isTokenChar(code)) { | ||
if (end !== -1) { | ||
throw new ParseError(unexpectedCharacterMessage(header, i), header); | ||
} | ||
|
||
if (start === -1) start = i; | ||
} else if (isDelimiter(code) || isExtended(code)) { | ||
if (quote) { | ||
if (inQuotes) { | ||
if (code === 0x22/*'"'*/) { | ||
quote = false; | ||
lock = true; | ||
inQuotes = false; | ||
end = i; | ||
} else if (code === 0x5C/*'\'*/) { | ||
escape = true; | ||
if (start === -1) start = i; | ||
isEscaping = mustUnescape = true; | ||
} else if (start === -1) { | ||
start = i; | ||
} | ||
} else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3D) { | ||
inQuotes = true; | ||
} else if ( | ||
(code === 0x2C/*','*/|| code === 0x3B/*';'*/) && | ||
(start !== -1 || end !== -1) | ||
) { | ||
if (start !== -1) { | ||
if (end === -1) end = i; | ||
forwarded[parameter] = mustUnescape | ||
? decode(header.slice(start, end)) | ||
: header.slice(start, end); | ||
} else { | ||
token += ch; | ||
forwarded[parameter] = ''; | ||
} | ||
} else if (code === 0x22 && input.charCodeAt(i - 1) === 0x3D) { | ||
quote = true; | ||
} else if ((code === 0x2C/*','*/|| code === 0x3B/*';'*/) && (token || lock)) { | ||
forwarded[parameter] = token; | ||
|
||
if (code === 0x2C) { | ||
output.push(forwarded); | ||
forwarded = {}; | ||
} | ||
parameter = token = ''; | ||
lock = false; | ||
ows = true; | ||
} else { | ||
throw new ParseError(input, "Unexpected character '"+ ch +"'", i); | ||
} | ||
} else if (isTokenChar(code)) { | ||
if (quote || !lock) { | ||
token += ch; | ||
|
||
parameter = undefined; | ||
start = end = -1; | ||
} else { | ||
throw new ParseError(input, "Unexpected character '"+ ch +"'", i); | ||
throw new ParseError(unexpectedCharacterMessage(header, i), header); | ||
} | ||
} else if (code === 0x20 || code === 0x09) { | ||
if (quote) { | ||
token += ch; | ||
} else if (lock) { | ||
ows = true; | ||
} else if (token) { | ||
lock = ows = true; | ||
if (end !== -1) continue; | ||
|
||
if (inQuotes) { | ||
if (start === -1) start = i; | ||
} else if (start !== -1) { | ||
end = i; | ||
} else { | ||
throw new ParseError(input, "Unexpected character '"+ ch +"'", i); | ||
throw new ParseError(unexpectedCharacterMessage(header, i), header); | ||
} | ||
} else { | ||
throw new ParseError(input, "Unexpected character '"+ ch +"'", i); | ||
throw new ParseError(unexpectedCharacterMessage(header, i), header); | ||
} | ||
} | ||
} | ||
|
||
if (!token && !lock || !parameter || quote || ows) { | ||
throw new ParseError(input, 'Unexpected end of input'); | ||
if (parameter === undefined || inQuotes || start === -1 && end === -1) { | ||
throw new ParseError('Unexpected end of input', header); | ||
} | ||
|
||
forwarded[parameter] = token; | ||
output.push(forwarded); | ||
if (start !== -1) { | ||
if (end === -1) end = i; | ||
forwarded[parameter] = mustUnescape | ||
? decode(header.slice(start, end)) | ||
: header.slice(start, end); | ||
} else { | ||
forwarded[parameter] = ''; | ||
} | ||
|
||
output.push(forwarded); | ||
return output; | ||
} | ||
|
||
module.exports = parse; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters