The standard says headers are case insensitive.
Ruby and node both force lower case headers.
We are using an outside server program that expects headers 'AuthToken' to be case sensitive, using .NET framework, and apparently both don't follow standards. We need headers to be up case in this instance.
At the time of writing, the following setHeader
was copied from
the _http_outgoing page of node's core lib
var http = require('http');
http.OutgoingMessage.prototype.setHeader = function(name, value) {
if (arguments.length < 2) {
throw new Error('`name` and `value` are required for setHeader().');
}
if (this._header) {
throw new Error('Can\'t set headers after they are sent.');
}
// NO LOWER CASE
var key = name//.toLowerCase();
this._headers = this._headers || {};
this._headerNames = this._headerNames || {};
this._headers[key] = value;
this._headerNames[key] = name;
// Since we're re-defining the method, we can't use this part anymore
//if (automaticHeaders[key]) {
// this._removedHeader[key] = false;
//}
};
Commented out part for lowercase
So.. if you get this problem. require http and override this method with the version you're currently using.
It should then work properly. You could do a similar thing of overriding a method in ruby, but it won't be a quick and easy
Then this will work:
require('request')
request({url: 'http://myurl.com', headers: {UpperCaseWorks: 'Yay'}})
EDIT: here's for the newer version of node
OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
if (this._header) {
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'set');
}
validateHeader(name, value);
if (!this[outHeadersKey])
this[outHeadersKey] = {};
// no more lower case
const key = name//.toLowerCase();
this[outHeadersKey][key] = [name, value];
switch (key.length) {
case 10:
if (key === 'connection')
this._removedConnection = false;
break;
case 14:
if (key === 'content-length')
this._removedContLen = false;
break;
case 17:
if (key === 'transfer-encoding')
this._removedTE = false;
break;
}
};
Looks like it calls this local method, which'll need to be defined as well
function validateHeader(name, value) {
let err;
if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) {
err = new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Header name', name);
} else if (value === undefined) {
err = new errors.TypeError('ERR_HTTP_INVALID_HEADER_VALUE', value, name);
} else if (checkInvalidHeaderChar(value)) {
debug('Header "%s" contains invalid characters', name);
err = new errors.TypeError('ERR_INVALID_CHAR', 'header content', name);
}
if (err !== undefined) {
Error.captureStackTrace(err, validateHeader);
throw err;
}
}
And this
const { outHeadersKey } = require('internal/http');
Anyway, check your version of node for what you are overriding
Piggybacking on Funkodebat's answer, here's my solution for Node 16:
const http = require('http');
// https://github.com/nodejs/node/blob/v16.x/lib/_http_outgoing.js#L574-L587
const { validateHeaderName, validateHeaderValue } = http;
http.OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
if (this._header) {
throw new Error('Cannot set headers after they are sent to the client');
}
validateHeaderName(name);
validateHeaderValue(name, value);
// Extra logic to find kOutHeaders symbol in `this`
const kOutHeaders = Object.getOwnPropertySymbols(this).find(
(sym) => sym.toString() === 'Symbol(kOutHeaders)'
);
let headers = this[kOutHeaders];
if (headers === null) this[kOutHeaders] = headers = Object.create(null);
headers[name] = [name, value]; // toLowerCase removed from here
return this;
};
By looking at the source of NodeJS library on github, you do not need to override the OutgoingMessage.prototype.setHeader
Instead of passing the headers as an Object, you should send them as an Array. Here is a working example :
const http = require('http');
const postData = JSON.stringify({
'msg': 'Hello World!'
});
const options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
// use an Array instead of Object to avoid lowercase transformation
headers: [
['Host' ,'localhost' ],
['X-CustomHeaderFancy' , 'valueForFancyHeader'],
['Content-Type', 'application/json'],
['Content-Length', Buffer.byteLength(postData)]
}
};
const req = http.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
// Write data to request body
req.write(postData);
req.end();
inside the source code of https://github.com/nodejs/node/blob/v16.x/lib/_http_client.js#L249 there is a test to know if the headers are an array, if it is the case, then it bypass the lowercase transformation. I do not know why it is not documented ? It's a very useful feature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With