Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed & Imporoved AxiosHeaders class #5224

Merged
merged 4 commits into from Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions index.d.ts
Expand Up @@ -21,8 +21,7 @@ type AxiosHeaderTester = (matcher?: AxiosHeaderMatcher) => boolean;

export class AxiosHeaders {
constructor(
headers?: RawAxiosHeaders | AxiosHeaders,
defaultHeaders?: RawAxiosHeaders | AxiosHeaders
headers?: RawAxiosHeaders | AxiosHeaders
);

set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;
Expand All @@ -39,12 +38,16 @@ export class AxiosHeaders {

normalize(format: boolean): AxiosHeaders;

concat(...targets: Array<AxiosHeaders | RawAxiosHeaders | string>): AxiosHeaders;

toJSON(asStrings?: boolean): RawAxiosHeaders;

static from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders;

static accessor(header: string | string[]): AxiosHeaders;

static concat(...targets: Array<AxiosHeaders | RawAxiosHeaders | string>): AxiosHeaders;

setContentType: AxiosHeaderSetter;
getContentType: AxiosHeaderGetter;
hasContentType: AxiosHeaderTester;
Expand Down
4 changes: 2 additions & 2 deletions lib/adapters/http.js
Expand Up @@ -203,7 +203,7 @@ export default function httpAdapter(config) {
data: convertedData,
status: 200,
statusText: 'OK',
headers: {},
headers: new AxiosHeaders(),
config
});
}
Expand Down Expand Up @@ -588,4 +588,4 @@ export default function httpAdapter(config) {
});
}

export const __setProxy = setProxy;
export const __setProxy = setProxy;
1 change: 1 addition & 0 deletions lib/axios.js
Expand Up @@ -75,5 +75,6 @@ axios.AxiosHeaders = AxiosHeaders;
axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);

axios.default = axios;

// this module should only have a default export
export default axios
18 changes: 10 additions & 8 deletions lib/core/Axios.js
Expand Up @@ -47,7 +47,7 @@ class Axios {

config = mergeConfig(this.defaults, config);

const {transitional, paramsSerializer} = config;
const {transitional, paramsSerializer, headers} = config;

if (transitional !== undefined) {
validator.assertOptions(transitional, {
Expand All @@ -67,20 +67,22 @@ class Axios {
// Set config.method
config.method = (config.method || this.defaults.method || 'get').toLowerCase();

let contextHeaders;

// Flatten headers
const defaultHeaders = config.headers && utils.merge(
config.headers.common,
config.headers[config.method]
contextHeaders = headers && utils.merge(
headers.common,
headers[config.method]
);

defaultHeaders && utils.forEach(
contextHeaders && utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
(method) => {
delete headers[method];
}
);

config.headers = new AxiosHeaders(config.headers, defaultHeaders);
config.headers = AxiosHeaders.concat(contextHeaders, headers);

// filter out skipped interceptors
const requestInterceptorChain = [];
Expand Down
158 changes: 82 additions & 76 deletions lib/core/AxiosHeaders.js
Expand Up @@ -4,7 +4,6 @@ import utils from '../utils.js';
import parseHeaders from '../helpers/parseHeaders.js';

const $internals = Symbol('internals');
const $defaults = Symbol('defaults');

function normalizeHeader(header) {
return header && String(header).trim().toLowerCase();
Expand All @@ -30,6 +29,10 @@ function parseTokens(str) {
return tokens;
}

function isValidHeaderName(str) {
return /^[-_a-zA-Z]+$/.test(str.trim());
}

function matchHeaderValue(context, value, header, filter) {
if (utils.isFunction(filter)) {
return filter.call(this, value, header);
Expand Down Expand Up @@ -66,27 +69,12 @@ function buildAccessors(obj, header) {
});
}

function findKey(obj, key) {
key = key.toLowerCase();
const keys = Object.keys(obj);
let i = keys.length;
let _key;
while (i-- > 0) {
_key = keys[i];
if (key === _key.toLowerCase()) {
return _key;
}
class AxiosHeaders {
constructor(headers) {
headers && this.set(headers);
}
return null;
}

function AxiosHeaders(headers, defaults) {
headers && this.set(headers);
this[$defaults] = defaults || null;
}

Object.assign(AxiosHeaders.prototype, {
set: function(header, valueOrRewrite, rewrite) {
set(header, valueOrRewrite, rewrite) {
const self = this;

function setHeader(_value, _header, _rewrite) {
Expand All @@ -96,77 +84,78 @@ Object.assign(AxiosHeaders.prototype, {
throw new Error('header name must be a non-empty string');
}

const key = findKey(self, lHeader);
const key = utils.findKey(self, lHeader);

if (key && _rewrite !== true && (self[key] === false || _rewrite === false)) {
return;
if(!key || self[key] === undefined || _rewrite === true || (_rewrite === undefined && self[key] !== false)) {
self[key || _header] = normalizeValue(_value);
}

self[key || _header] = normalizeValue(_value);
}

if (utils.isPlainObject(header)) {
utils.forEach(header, (_value, _header) => {
setHeader(_value, _header, valueOrRewrite);
});
const setHeaders = (headers, _rewrite) =>
utils.forEach(headers, (_value, _header) => setHeader(_value, _header, _rewrite));

if (utils.isPlainObject(header) || header instanceof this.constructor) {
setHeaders(header, valueOrRewrite)
} else if(utils.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) {
setHeaders(parseHeaders(header), valueOrRewrite);
} else {
setHeader(valueOrRewrite, header, rewrite);
header != null && setHeader(valueOrRewrite, header, rewrite);
}

return this;
},
}

get: function(header, parser) {
get(header, parser) {
header = normalizeHeader(header);

if (!header) return undefined;
if (header) {
const key = utils.findKey(this, header);

const key = findKey(this, header);
if (key) {
const value = this[key];

if (key) {
const value = this[key];
if (!parser) {
return value;
}

if (!parser) {
return value;
}
if (parser === true) {
return parseTokens(value);
}

if (parser === true) {
return parseTokens(value);
}
if (utils.isFunction(parser)) {
return parser.call(this, value, key);
}

if (utils.isFunction(parser)) {
return parser.call(this, value, key);
}
if (utils.isRegExp(parser)) {
return parser.exec(value);
}

if (utils.isRegExp(parser)) {
return parser.exec(value);
throw new TypeError('parser must be boolean|regexp|function');
}

throw new TypeError('parser must be boolean|regexp|function');
}
},
}

has: function(header, matcher) {
has(header, matcher) {
header = normalizeHeader(header);

if (header) {
const key = findKey(this, header);
const key = utils.findKey(this, header);

return !!(key && (!matcher || matchHeaderValue(this, this[key], key, matcher)));
}

return false;
},
}

delete: function(header, matcher) {
delete(header, matcher) {
const self = this;
let deleted = false;

function deleteHeader(_header) {
_header = normalizeHeader(_header);

if (_header) {
const key = findKey(self, _header);
const key = utils.findKey(self, _header);

if (key && (!matcher || matchHeaderValue(self, self[key], key, matcher))) {
delete self[key];
Expand All @@ -183,18 +172,18 @@ Object.assign(AxiosHeaders.prototype, {
}

return deleted;
},
}

clear: function() {
clear() {
return Object.keys(this).forEach(this.delete.bind(this));
},
}

normalize: function(format) {
normalize(format) {
const self = this;
const headers = {};

utils.forEach(this, (value, header) => {
const key = findKey(headers, header);
const key = utils.findKey(headers, header);

if (key) {
self[key] = normalizeValue(value);
Expand All @@ -214,30 +203,47 @@ Object.assign(AxiosHeaders.prototype, {
});

return this;
},
}

concat(...targets) {
return this.constructor.concat(this, ...targets);
}

toJSON: function(asStrings) {
toJSON(asStrings) {
const obj = Object.create(null);

utils.forEach(Object.assign({}, this[$defaults] || null, this),
(value, header) => {
if (value == null || value === false) return;
obj[header] = asStrings && utils.isArray(value) ? value.join(', ') : value;
});
utils.forEach(this, (value, header) => {
value != null && value !== false && (obj[header] = asStrings && utils.isArray(value) ? value.join(', ') : value);
});

return obj;
}
});

Object.assign(AxiosHeaders, {
from: function(thing) {
if (utils.isString(thing)) {
return new this(parseHeaders(thing));
}
[Symbol.iterator]() {
return Object.entries(this.toJSON())[Symbol.iterator]();
}

toString() {
return Object.entries(this.toJSON()).map(([header, value]) => header + ': ' + value).join('\n');
}

get [Symbol.toStringTag]() {
return 'AxiosHeaders';
}

static from(thing) {
return thing instanceof this ? thing : new this(thing);
},
}

accessor: function(header) {
static concat(first, ...targets) {
const computed = new this(first);

targets.forEach((target) => computed.set(target));

return computed;
}

static accessor(header) {
const internals = this[$internals] = (this[$internals] = {
accessors: {}
});
Expand All @@ -258,7 +264,7 @@ Object.assign(AxiosHeaders, {

return this;
}
});
}

AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent']);

Expand Down
4 changes: 4 additions & 0 deletions lib/core/dispatchRequest.js
Expand Up @@ -41,6 +41,10 @@ export default function dispatchRequest(config) {
config.transformRequest
);

if (['post', 'put', 'patch'].indexOf(config.method) !== -1) {
config.headers.setContentType('application/x-www-form-urlencoded', false);
}

const adapter = config.adapter || defaults.adapter;

return adapter(config).then(function onAdapterResolution(response) {
Expand Down