Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS - https - TypeError [ERR_INVALID_HTTP_TOKEN]: Header name must be a valid HTTP token ["Accept​"]

I have a NodeJS code that uses https module as: https.request(options, (res) => {...... where options is an object like

const options = {
    hostname: SERVICE_HOSTNAME,
    path: BASE_PATH,
    method: 'GET',
    headers: {
        'Content-Type': 'application/json',
        'Accept​': 'application/json',
        ApplicationId,
        Authorization
    }
  };

My problems start when I add Accept header. I getting an error:

TypeError [ERR_INVALID_HTTP_TOKEN]: Header name must be a valid HTTP token ["Accept​"]
    at ClientRequest.setHeader (_http_outgoing.js:472:3)
    at new ClientRequest (_http_client.js:203:14)
    at Object.request (https.js:289:10)

How Accept header would be invalid?

like image 534
bensiu Avatar asked May 27 '20 13:05

bensiu


3 Answers

Now this is crazy, but you have an extra character in your "Accept" string. If you run

console.log('Accept​'.charCodeAt(6)) // prints 8203

(You have to run it with your copy of "Accept" from your question, which I assume is copy-pasted from elsewhere)

Unicode 8203 is zero width space, hence it's not visible to us humans :)


By the way, initially i've discovered the character by copy-pasting into Node REPL and noticing a "square" in console output after getting the error.

I've then tried copy-pasting your code into my IntelliJ editor, and was pleasantly surprised to see this warning:

enter image description here

like image 122
hlfrmn Avatar answered Nov 11 '22 02:11

hlfrmn


hlfrmn already found this tricky error and gave the correct answer, just in case anyone else is interested:

Starting from version 14.3.0 Node's http module will perform validateHeaderName and validateHeaderValue on each Key-Value pair you specify in your request options headers.

So if you do

const {validateHeaderName} = require('http');

try {
    const options = {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
            'Accept​': 'application/json',
        }
    };
    for (const header in options.headers) {
        validateHeaderName(header);
    }

} catch (err) {
    console.log(err);
}

the mentioned error will be printed:

TypeError [ERR_INVALID_HTTP_TOKEN]: Header name must be a valid HTTP token ["Accept​"] will be printed from the catch block.
like image 8
eol Avatar answered Nov 11 '22 02:11

eol


I didn't eat because of your question T_T

As @hlfrmn said, you have a rare char in your Accept word!!

I reproduced the error:

const https = require('https');

//copied from origin question
var copiedOptions = {
  headers: {
    'Content-Type': 'application/json',
    "Accept​": 'application/json'
  }
}

var writedOptions = {
  headers: {
    'Content-Type': 'application/json',
    "Accept": 'application/json'
  }
}

https.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY',writedOptions, (resp) => {
  let data = '';

  // A chunk of data has been recieved.
  resp.on('data', (chunk) => {
    data += chunk;
  });

  // The whole response has been received. Print out the result.
  resp.on('end', () => {
    console.log(JSON.parse(data).explanation);
  });

}).on("error", (err) => {
  console.log("Error: " + err.message);
});

If you try with copiedOptions, you will get the same error.


Since Node v10.20.1

Searching in https://github.com/nodejs/node/tree/master and the stackTrace: _http_outgoing.js:472

at ClientRequest.setHeader (_http_outgoing.js:472:3)
  • function setHeader https://github.com/nodejs/node/blob/v10.20.1/lib/_http_outgoing.js#L472
  • function validateHeaderName https://github.com/nodejs/node/blob/v10.20.1/lib/_http_outgoing.js#L446
  • function checkIsHttpToken https://github.com/nodejs/node/blob/master/lib/_http_common.js#L210

I founded this:

const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
/**
 * Verifies that the given val is a valid HTTP token
 * per the rules defined in RFC 7230
 * See https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6
 */
function checkIsHttpToken(val) {
  return tokenRegExp.test(val);
}

That regex ensure that header name only has allowed tokens or chars according to https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6

Maybe a earlier validation in nodejs core could help:

You have a not allowed char in your header name!

like image 1
JRichardsz Avatar answered Nov 11 '22 01:11

JRichardsz