Skip to content

Commit 316e283

Browse files
committed
Use flat object instead of array-of-arrays for HTTP headers.
E.G. { "Content-Length": 10, "Content-Type": "text/html" } instead of [["Content-Length", 10], ["Content-Type", "text/html"]]. The main reason for this change is object-creation efficiency. This still needs testing and some further changes (like when receiving multiple header lines with the same field-name, they are concatenated with a comma but some headers ("Content-Length") should not be concatenated ; the new header line should replace the old value). Various thoughts on this subject: http://groups.google.com/group/nodejs/browse_thread/thread/9a67bb32706d9efc# http://four.livejournal.com/979640.html http://mail.gnome.org/archives/libsoup-list/2009-March/msg00015.html
1 parent 9c97b1d commit 316e283

11 files changed

Lines changed: 87 additions & 66 deletions

File tree

benchmark/http_simple.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ node.http.createServer(function (req, res) {
4040
var content_length = body.length.toString();
4141

4242
res.sendHeader( status
43-
, [ ["Content-Type", "text/plain"]
44-
, ["Content-Length", content_length]
45-
]
43+
, { "Content-Type": "text/plain"
44+
, "Content-Length": content_length
45+
}
4646
);
4747
res.sendBody(body);
4848

benchmark/static_http_server.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ for (var i = 0; i < bytes; i++) {
1212
}
1313

1414
var server = node.http.createServer(function (req, res) {
15-
res.sendHeader(200, [
16-
["Content-Type", "text/plain"],
17-
["Content-Length", body.length]
18-
]);
15+
res.sendHeader(200, {
16+
"Content-Type": "text/plain",
17+
"Content-Length": body.length
18+
});
1919
res.sendBody(body);
2020
res.finish();
2121
})

src/http.js

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ function IncomingMessage (connection) {
117117

118118
this.connection = connection;
119119
this.httpVersion = null;
120-
this.headers = [];
120+
this.headers = {};
121121

122122
// request (server) only
123123
this.uri = "";
@@ -142,6 +142,15 @@ IncomingMessage.prototype.resume = function () {
142142
this.connection.readResume();
143143
};
144144

145+
IncomingMessage.prototype._addHeaderLine = function (field, value) {
146+
if (field in this.headers) {
147+
// TODO Certain headers like 'Content-Type' should not be concatinated.
148+
// See https://www.google.com/reader/view/?tab=my#overview-page
149+
this.headers[field] += ", " + value;
150+
} else {
151+
this.headers[field] = value;
152+
}
153+
};
145154

146155
function OutgoingMessage () {
147156
node.EventEmitter.call(this);
@@ -162,22 +171,26 @@ OutgoingMessage.prototype.send = function (data, encoding) {
162171
this.output.push(data);
163172
};
164173

165-
OutgoingMessage.prototype.sendHeaderLines = function (first_line, header_lines) {
174+
OutgoingMessage.prototype.sendHeaderLines = function (first_line, headers) {
166175
var sent_connection_header = false;
167176
var sent_content_length_header = false;
168177
var sent_transfer_encoding_header = false;
169178

170-
header_lines = header_lines || [];
171-
172179
// first_line in the case of request is: "GET /index.html HTTP/1.1\r\n"
173180
// in the case of response it is: "HTTP/1.1 200 OK\r\n"
174-
var header = first_line;
175-
176-
for (var i = 0; i < header_lines.length; i++) {
177-
var field = header_lines[i][0];
178-
var value = header_lines[i][1];
181+
var message_header = first_line;
182+
var field, value;
183+
for (var i in headers) {
184+
if (headers instanceof Array) {
185+
field = headers[i][0];
186+
value = headers[i][1];
187+
} else {
188+
if (!headers.hasOwnProperty(i)) continue;
189+
field = i;
190+
value = headers[i];
191+
}
179192

180-
header += field + ": " + value + CRLF;
193+
message_header += field + ": " + value + CRLF;
181194

182195
if (connection_expression.exec(field)) {
183196
sent_connection_header = true;
@@ -196,23 +209,23 @@ OutgoingMessage.prototype.sendHeaderLines = function (first_line, header_lines)
196209
// keep-alive logic
197210
if (sent_connection_header == false) {
198211
if (this.should_keep_alive) {
199-
header += "Connection: keep-alive\r\n";
212+
message_header += "Connection: keep-alive\r\n";
200213
} else {
201214
this.closeOnFinish = true;
202-
header += "Connection: close\r\n";
215+
message_header += "Connection: close\r\n";
203216
}
204217
}
205218

206219
if (sent_content_length_header == false && sent_transfer_encoding_header == false) {
207220
if (this.use_chunked_encoding_by_default) {
208-
header += "Transfer-Encoding: chunked\r\n";
221+
message_header += "Transfer-Encoding: chunked\r\n";
209222
this.chunked_encoding = true;
210223
}
211224
}
212225

213-
header += CRLF;
226+
message_header += CRLF;
214227

215-
this.send(header);
228+
this.send(message_header);
216229
// wait until the first body chunk, or finish(), is sent to flush.
217230
};
218231

@@ -255,7 +268,7 @@ ServerResponse.prototype.sendHeader = function (statusCode, headers) {
255268
};
256269

257270

258-
function ClientRequest (method, uri, header_lines) {
271+
function ClientRequest (method, uri, headers) {
259272
OutgoingMessage.call(this);
260273

261274
this.should_keep_alive = false;
@@ -266,7 +279,7 @@ function ClientRequest (method, uri, header_lines) {
266279
}
267280
this.closeOnFinish = true;
268281

269-
this.sendHeaderLines(method + " " + uri + " HTTP/1.1\r\n", header_lines);
282+
this.sendHeaderLines(method + " " + uri + " HTTP/1.1\r\n", headers);
270283
}
271284
node.inherits(ClientRequest, OutgoingMessage);
272285

@@ -282,7 +295,7 @@ function createIncomingMessageStream (connection, incoming_listener) {
282295
stream.addListener("incoming", incoming_listener);
283296

284297
var incoming;
285-
var last_header_was_a_value = false;
298+
var field = null, value = null;
286299

287300
connection.addListener("message_begin", function () {
288301
incoming = new IncomingMessage(connection);
@@ -294,25 +307,31 @@ function createIncomingMessageStream (connection, incoming_listener) {
294307
});
295308

296309
connection.addListener("header_field", function (data) {
297-
if (incoming.headers.length > 0 && last_header_was_a_value == false) {
298-
incoming.headers[incoming.headers.length-1][0] += data;
310+
if (value) {
311+
incoming._addHeaderLine(field, value);
312+
field = null;
313+
value = null;
314+
}
315+
if (field) {
316+
field += data;
299317
} else {
300-
incoming.headers.push([data]);
318+
field = data;
301319
}
302-
last_header_was_a_value = false;
303320
});
304321

305322
connection.addListener("header_value", function (data) {
306-
var last_pair = incoming.headers[incoming.headers.length-1];
307-
if (last_pair.length == 1) {
308-
last_pair[1] = data;
309-
} else {
310-
last_pair[1] += data;
311-
}
312-
last_header_was_a_value = true;
323+
if (value) {
324+
value += data;
325+
} else {
326+
value = data;
327+
}
313328
});
314329

315330
connection.addListener("headers_complete", function (info) {
331+
if (field && value) {
332+
incoming._addHeaderLine(field, value);
333+
}
334+
316335
incoming.httpVersion = info.httpVersion;
317336

318337
if (info.method) {

test/mjsunit/disabled/test-http-stress.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ var request_count = 1000;
55
var response_body = '{"ok": true}';
66

77
var server = node.http.createServer(function(req, res) {
8-
res.sendHeader(200, [['Content-Type', 'text/javascript']]);
8+
res.sendHeader(200, {'Content-Type': 'text/javascript'});
99
res.sendBody(response_body);
1010
res.finish();
1111
});

test/mjsunit/test-http-client-race.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ var body2_s = "22222";
66

77
var server = node.http.createServer(function (req, res) {
88
var body = req.uri.path === "/1" ? body1_s : body2_s;
9-
res.sendHeader(200, [
10-
["Content-Type", "text/plain"],
11-
["Content-Length", body.length]
12-
]);
9+
res.sendHeader(200, { "Content-Type": "text/plain"
10+
, "Content-Length": body.length
11+
});
1312
res.sendBody(body);
1413
res.finish();
1514
});

test/mjsunit/test-http-client-upload.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var server = node.http.createServer(function(req, res) {
1717
req.addListener("complete", function () {
1818
server_req_complete = true;
1919
puts("request complete from server");
20-
res.sendHeader(200, [['Content-Type', 'text/plain']]);
20+
res.sendHeader(200, {'Content-Type': 'text/plain'});
2121
res.sendBody('hello\n');
2222
res.finish();
2323
});

test/mjsunit/test-http-proxy.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ var BACKEND_PORT = 8870;
55

66
var backend = node.http.createServer(function (req, res) {
77
// node.debug("backend");
8-
res.sendHeader(200, [["content-type", "text/plain"]]);
8+
res.sendHeader(200, {"content-type": "text/plain"});
99
res.sendBody("hello world\n");
1010
res.finish();
1111
});
@@ -14,7 +14,7 @@ backend.listen(BACKEND_PORT);
1414

1515
var proxy_client = node.http.createClient(BACKEND_PORT);
1616
var proxy = node.http.createServer(function (req, res) {
17-
// node.debug("proxy req");
17+
node.debug("proxy req headers: " + JSON.stringify(req.headers));
1818
var proxy_req = proxy_client.get(req.uri.path);
1919
proxy_req.finish(function(proxy_res) {
2020
res.sendHeader(proxy_res.statusCode, proxy_res.headers);

test/mjsunit/test-http-server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function onLoad() {
2626
}
2727

2828
setTimeout(function () {
29-
res.sendHeader(200, [["Content-Type", "text/plain"]]);
29+
res.sendHeader(200, {"Content-Type": "text/plain"});
3030
res.sendBody(req.uri.path);
3131
res.finish();
3232
}, 1);

test/mjsunit/test-http.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ function onLoad () {
1111
if (responses_sent == 0) {
1212
assertEquals("GET", req.method);
1313
assertEquals("/hello", req.uri.path);
14+
15+
p(req.headers);
16+
assertTrue("Accept" in req.headers);
17+
assertEquals("*/*", req.headers["Accept"]);
18+
19+
assertTrue("Foo" in req.headers);
20+
assertEquals("bar", req.headers["Foo"]);
1421
}
1522

1623
if (responses_sent == 1) {
@@ -20,7 +27,7 @@ function onLoad () {
2027
}
2128

2229
req.addListener("complete", function () {
23-
res.sendHeader(200, [["Content-Type", "text/plain"]]);
30+
res.sendHeader(200, {"Content-Type": "text/plain"});
2431
res.sendBody("The path was " + req.uri.path);
2532
res.finish();
2633
responses_sent += 1;
@@ -30,7 +37,7 @@ function onLoad () {
3037
}).listen(PORT);
3138

3239
var client = node.http.createClient(PORT);
33-
var req = client.get("/hello");
40+
var req = client.get("/hello", {"Accept": "*/*", "Foo": "bar"});
3441
req.finish(function (res) {
3542
assertEquals(200, res.statusCode);
3643
responses_recvd += 1;

website/api.txt

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ World" after waiting two seconds:
1818
----------------------------------------
1919
node.http.createServer(function (request, response) {
2020
setTimeout(function () {
21-
response.sendHeader(200, [["Content-Type", "text/plain"]]);
21+
response.sendHeader(200, {"Content-Type": "text/plain"});
2222
response.sendBody("Hello World");
2323
response.finish();
2424
}, 2000);
@@ -575,15 +575,14 @@ In particular, large, possibly chunk-encoded, messages. The interface is
575575
careful to never buffer entire requests or responses--the
576576
user is able to stream data.
577577

578-
HTTP message headers are represented by an array of 2-element
579-
arrays like this
578+
HTTP message headers are represented by an object like this
580579

581580
----------------------------------------
582-
[ ["Content-Length", "123"]
583-
, ["Content-Type", "text/plain"]
584-
, ["Connection", "keep-alive"]
585-
, ["Accept", "*/*"]
586-
]
581+
{ "Content-Length": "123"
582+
, "Content-Type": "text/plain"
583+
, "Connection": "keep-alive"
584+
, "Accept": "*/*"
585+
}
587586
----------------------------------------
588587

589588
In order to support the full spectrum of possible HTTP applications, Node's
@@ -693,7 +692,6 @@ in the actual HTTP Request.
693692

694693

695694
+request.headers+ ::
696-
The request headers expressed as an array of 2-element arrays.
697695
Read only.
698696

699697

@@ -727,17 +725,16 @@ passed as the second parameter to the +"request"+ event.
727725
+response.sendHeader(statusCode, headers)+ ::
728726

729727
Sends a response header to the request. The status code is a 3-digit HTTP
730-
status code, like +404+. The second argument, +headers+, should be an array
731-
of 2-element arrays, representing the response headers.
728+
status code, like +404+. The second argument, +headers+ are the response headers.
732729
+
733730
Example:
734731
+
735732
----------------------------------------
736733
var body = "hello world";
737-
response.sendHeader(200, [
738-
["Content-Length", body.length],
739-
["Content-Type", "text/plain"]
740-
]);
734+
response.sendHeader(200, {
735+
"Content-Length": body.length,
736+
"Content-Type": "text/plain"
737+
});
741738
----------------------------------------
742739
+
743740
This method must only be called once on a message and it must
@@ -799,8 +796,7 @@ connection is not established until a request is issued.
799796
Issues a request; if necessary establishes connection. Returns a +node.http.ClientRequest+ instance.
800797
+
801798
+request_headers+ is optional.
802-
+request_headers+ should be an array of 2-element
803-
arrays. Additional request headers might be added internally
799+
Additional request headers might be added internally
804800
by Node. Returns a +ClientRequest+ object.
805801
+
806802
Do remember to include the +Content-Length+ header if you
@@ -894,7 +890,7 @@ After emitted no other events will be emitted on the response.
894890
+"1.1"+ or +"1.0"+.
895891

896892
+response.headers+ ::
897-
The response headers. An Array of 2-element arrays.
893+
The response headers.
898894

899895
+response.setBodyEncoding(encoding)+ ::
900896
Set the encoding for the response body. Either +"utf8"+ or +"raw"+.

0 commit comments

Comments
 (0)