Skip to content

Commit 993d4cd

Browse files
authored
Convert Headers to ES2015 and implement Iterable interface (node-fetch#180)
Closes node-fetch#127, node-fetch#174.
1 parent 2874af4 commit 993d4cd

File tree

5 files changed

+276
-139
lines changed

5 files changed

+276
-139
lines changed

src/headers.js

Lines changed: 185 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -5,137 +5,211 @@
55
* Headers class offers convenient helpers
66
*/
77

8-
module.exports = Headers;
9-
10-
/**
11-
* Headers class
12-
*
13-
* @param Object headers Response headers
14-
* @return Void
15-
*/
16-
function Headers(headers) {
8+
export const MAP = Symbol('map');
9+
10+
export default class Headers {
11+
/**
12+
* Headers class
13+
*
14+
* @param Object headers Response headers
15+
* @return Void
16+
*/
17+
constructor(headers) {
18+
this[MAP] = {};
19+
20+
// Headers
21+
if (headers instanceof Headers) {
22+
headers = headers.raw();
23+
}
1724

18-
var self = this;
19-
this._headers = {};
25+
// plain object
26+
for (const prop in headers) {
27+
if (!headers.hasOwnProperty(prop)) {
28+
continue;
29+
}
30+
31+
if (typeof headers[prop] === 'string') {
32+
this.set(prop, headers[prop]);
33+
} else if (typeof headers[prop] === 'number' && !isNaN(headers[prop])) {
34+
this.set(prop, headers[prop].toString());
35+
} else if (headers[prop] instanceof Array) {
36+
headers[prop].forEach(item => {
37+
this.append(prop, item.toString());
38+
});
39+
}
40+
}
41+
}
2042

21-
// Headers
22-
if (headers instanceof Headers) {
23-
headers = headers.raw();
43+
/**
44+
* Return first header value given name
45+
*
46+
* @param String name Header name
47+
* @return Mixed
48+
*/
49+
get(name) {
50+
const list = this[MAP][name.toLowerCase()];
51+
return list ? list[0] : null;
2452
}
2553

26-
// plain object
27-
for (var prop in headers) {
28-
if (!headers.hasOwnProperty(prop)) {
29-
continue;
54+
/**
55+
* Return all header values given name
56+
*
57+
* @param String name Header name
58+
* @return Array
59+
*/
60+
getAll(name) {
61+
if (!this.has(name)) {
62+
return [];
3063
}
3164

32-
if (typeof headers[prop] === 'string') {
33-
this.set(prop, headers[prop]);
34-
35-
} else if (typeof headers[prop] === 'number' && !isNaN(headers[prop])) {
36-
this.set(prop, headers[prop].toString());
65+
return this[MAP][name.toLowerCase()];
66+
}
3767

38-
} else if (headers[prop] instanceof Array) {
39-
headers[prop].forEach(function(item) {
40-
self.append(prop, item.toString());
68+
/**
69+
* Iterate over all headers
70+
*
71+
* @param Function callback Executed for each item with parameters (value, name, thisArg)
72+
* @param Boolean thisArg `this` context for callback function
73+
* @return Void
74+
*/
75+
forEach(callback, thisArg) {
76+
Object.getOwnPropertyNames(this[MAP]).forEach(name => {
77+
this[MAP][name].forEach(value => {
78+
callback.call(thisArg, value, name, this);
4179
});
80+
});
81+
}
82+
83+
/**
84+
* Overwrite header values given name
85+
*
86+
* @param String name Header name
87+
* @param String value Header value
88+
* @return Void
89+
*/
90+
set(name, value) {
91+
this[MAP][name.toLowerCase()] = [value];
92+
}
93+
94+
/**
95+
* Append a value onto existing header
96+
*
97+
* @param String name Header name
98+
* @param String value Header value
99+
* @return Void
100+
*/
101+
append(name, value) {
102+
if (!this.has(name)) {
103+
this.set(name, value);
104+
return;
42105
}
106+
107+
this[MAP][name.toLowerCase()].push(value);
43108
}
44109

45-
}
110+
/**
111+
* Check for header name existence
112+
*
113+
* @param String name Header name
114+
* @return Boolean
115+
*/
116+
has(name) {
117+
return this[MAP].hasOwnProperty(name.toLowerCase());
118+
}
46119

47-
/**
48-
* Return first header value given name
49-
*
50-
* @param String name Header name
51-
* @return Mixed
52-
*/
53-
Headers.prototype.get = function(name) {
54-
var list = this._headers[name.toLowerCase()];
55-
return list ? list[0] : null;
56-
};
120+
/**
121+
* Delete all header values given name
122+
*
123+
* @param String name Header name
124+
* @return Void
125+
*/
126+
delete(name) {
127+
delete this[MAP][name.toLowerCase()];
128+
};
129+
130+
/**
131+
* Return raw headers (non-spec api)
132+
*
133+
* @return Object
134+
*/
135+
raw() {
136+
return this[MAP];
137+
}
57138

58-
/**
59-
* Return all header values given name
60-
*
61-
* @param String name Header name
62-
* @return Array
63-
*/
64-
Headers.prototype.getAll = function(name) {
65-
if (!this.has(name)) {
66-
return [];
139+
/**
140+
* Get an iterator on keys.
141+
*
142+
* @return Iterator
143+
*/
144+
keys() {
145+
const keys = [];
146+
this.forEach((_, name) => keys.push(name));
147+
return new Iterator(keys);
67148
}
68149

69-
return this._headers[name.toLowerCase()];
70-
};
150+
/**
151+
* Get an iterator on values.
152+
*
153+
* @return Iterator
154+
*/
155+
values() {
156+
const values = [];
157+
this.forEach(value => values.push(value));
158+
return new Iterator(values);
159+
}
71160

72-
/**
73-
* Iterate over all headers
74-
*
75-
* @param Function callback Executed for each item with parameters (value, name, thisArg)
76-
* @param Boolean thisArg `this` context for callback function
77-
* @return Void
78-
*/
79-
Headers.prototype.forEach = function(callback, thisArg) {
80-
Object.getOwnPropertyNames(this._headers).forEach(function(name) {
81-
this._headers[name].forEach(function(value) {
82-
callback.call(thisArg, value, name, this)
83-
}, this)
84-
}, this)
85-
}
161+
/**
162+
* Get an iterator on entries.
163+
*
164+
* @return Iterator
165+
*/
166+
entries() {
167+
const entries = [];
168+
this.forEach((value, name) => entries.push([name, value]));
169+
return new Iterator(entries);
170+
}
86171

87-
/**
88-
* Overwrite header values given name
89-
*
90-
* @param String name Header name
91-
* @param String value Header value
92-
* @return Void
93-
*/
94-
Headers.prototype.set = function(name, value) {
95-
this._headers[name.toLowerCase()] = [value];
96-
};
172+
/**
173+
* Get an iterator on entries.
174+
*
175+
* This is the default iterator of the Headers object.
176+
*
177+
* @return Iterator
178+
*/
179+
[Symbol.iterator]() {
180+
return this.entries();
181+
}
97182

98-
/**
99-
* Append a value onto existing header
100-
*
101-
* @param String name Header name
102-
* @param String value Header value
103-
* @return Void
104-
*/
105-
Headers.prototype.append = function(name, value) {
106-
if (!this.has(name)) {
107-
this.set(name, value);
108-
return;
183+
/**
184+
* Tag used by `Object.prototype.toString()`.
185+
*/
186+
get [Symbol.toStringTag]() {
187+
return 'Headers';
109188
}
189+
}
110190

111-
this._headers[name.toLowerCase()].push(value);
112-
};
191+
const ITEMS = Symbol('items');
192+
class Iterator {
193+
constructor(items) {
194+
this[ITEMS] = items;
195+
}
113196

114-
/**
115-
* Check for header name existence
116-
*
117-
* @param String name Header name
118-
* @return Boolean
119-
*/
120-
Headers.prototype.has = function(name) {
121-
return this._headers.hasOwnProperty(name.toLowerCase());
122-
};
197+
next() {
198+
if (!this[ITEMS].length) {
199+
return {
200+
value: undefined,
201+
done: true
202+
};
203+
}
123204

124-
/**
125-
* Delete all header values given name
126-
*
127-
* @param String name Header name
128-
* @return Void
129-
*/
130-
Headers.prototype['delete'] = function(name) {
131-
delete this._headers[name.toLowerCase()];
132-
};
205+
return {
206+
value: this[ITEMS].shift(),
207+
done: false
208+
};
133209

134-
/**
135-
* Return raw headers (non-spec api)
136-
*
137-
* @return Object
138-
*/
139-
Headers.prototype.raw = function() {
140-
return this._headers;
141-
};
210+
}
211+
212+
[Symbol.iterator]() {
213+
return this;
214+
}
215+
}

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ var stream = require('stream');
1414

1515
var Body = require('./body');
1616
var Response = require('./response');
17-
var Headers = require('./headers');
17+
import Headers from './headers';
1818
var Request = require('./request');
1919
var FetchError = require('./fetch-error');
2020

src/request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
var parse_url = require('url').parse;
9-
var Headers = require('./headers');
9+
import Headers from './headers';
1010
var Body = require('./body');
1111

1212
module.exports = Request;

src/response.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
var http = require('http');
9-
var Headers = require('./headers');
9+
import Headers from './headers';
1010
var Body = require('./body');
1111

1212
module.exports = Response;

0 commit comments

Comments
 (0)