http: parse the status message in a http response.

This commit is contained in:
Cam Swords 2013-09-23 00:06:58 +10:00 committed by Fedor Indutny
parent a35a2f0192
commit 7ffe2ad616
7 changed files with 102 additions and 1 deletions

View File

@ -1011,6 +1011,12 @@ you can use the `require('querystring').parse` function, or pass
The 3-digit HTTP response status code. E.G. `404`.
### message.statusMessage
**Only valid for response obtained from `http.ClientRequest`.**
The HTTP response status message (reason phrase). E.G. `OK` or `Internal Server Error`.
### message.socket
The `net.Socket` object associated with the connection.

View File

@ -97,7 +97,7 @@ function parserOnHeadersComplete(info) {
} else {
// client only
parser.incoming.statusCode = info.statusCode;
// CHECKME dead code? we're always a request parser
parser.incoming.statusMessage = info.statusMessage;
}
parser.incoming.upgrade = info.upgrade;

View File

@ -64,6 +64,7 @@ function IncomingMessage(socket) {
// response (client) only
this.statusCode = null;
this.statusMessage = null;
this.client = this.socket;
// flag for backwards compatibility grossness.

View File

@ -119,6 +119,7 @@ namespace node {
V(smalloc_p_string, "_smalloc_p") \
V(sni_context_string, "sni_context") \
V(status_code_string, "statusCode") \
V(status_message_string, "statusMessage") \
V(subject_string, "subject") \
V(subjectaltname_string, "subjectaltname") \
V(syscall_string, "syscall") \

View File

@ -181,6 +181,7 @@ class Parser : public BaseObject {
HTTP_CB(on_message_begin) {
num_fields_ = num_values_ = 0;
url_.Reset();
status_message_.Reset();
return 0;
}
@ -191,6 +192,12 @@ class Parser : public BaseObject {
}
HTTP_DATA_CB(on_status) {
status_message_.Update(at, length);
return 0;
}
HTTP_DATA_CB(on_header_field) {
if (num_fields_ == num_values_) {
// start of new field name
@ -259,6 +266,8 @@ class Parser : public BaseObject {
if (parser_.type == HTTP_RESPONSE) {
message_info->Set(env()->status_code_string(),
Integer::New(parser_.status_code, node_isolate));
message_info->Set(env()->status_message_string(),
status_message_.ToString());
}
// VERSION
@ -349,6 +358,7 @@ class Parser : public BaseObject {
void Save() {
url_.Save();
status_message_.Save();
for (int i = 0; i < num_fields_; i++) {
fields_[i].Save();
@ -515,6 +525,7 @@ class Parser : public BaseObject {
void Init(enum http_parser_type type) {
http_parser_init(&parser_, type);
url_.Reset();
status_message_.Reset();
num_fields_ = 0;
num_values_ = 0;
have_flushed_ = false;
@ -526,6 +537,7 @@ class Parser : public BaseObject {
StringPtr fields_[32]; // header fields
StringPtr values_[32]; // header values
StringPtr url_;
StringPtr status_message_;
int num_fields_;
int num_values_;
bool have_flushed_;
@ -540,6 +552,7 @@ class Parser : public BaseObject {
const struct http_parser_settings Parser::settings = {
Parser::on_message_begin,
Parser::on_url,
Parser::on_status,
Parser::on_header_field,
Parser::on_header_value,
Parser::on_headers_complete,

View File

@ -143,6 +143,7 @@ function expectBody(expected) {
assert.equal(info.versionMajor, 1);
assert.equal(info.versionMinor, 1);
assert.equal(info.statusCode, 200);
assert.equal(info.statusMessage, "OK");
});
parser[kOnBody] = mustCall(function(buf, start, len) {
@ -169,6 +170,7 @@ function expectBody(expected) {
assert.equal(info.versionMajor, 1);
assert.equal(info.versionMinor, 0);
assert.equal(info.statusCode, 200);
assert.equal(info.statusMessage, "Connection established");
assert.deepEqual(info.headers || parser.headers, []);
});

View File

@ -0,0 +1,78 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var http = require('http');
var net = require('net');
var testsComplete = 0;
var testCases = [
{ path: "/200", statusMessage: "OK", response: 'HTTP/1.1 200 OK\r\n\r\n' },
{ path: "/500", statusMessage: "Internal Server Error", response: 'HTTP/1.1 500 Internal Server Error\r\n\r\n' },
{ path: "/302", statusMessage: "Moved Temporarily", response: 'HTTP/1.1 302 Moved Temporarily\r\n\r\n' },
{ path: "/missing", statusMessage: "", response: 'HTTP/1.1 200 \r\n\r\n' },
{ path: "/missing-no-space", statusMessage: "", response: 'HTTP/1.1 200\r\n\r\n' }
];
testCases.findByPath = function(path) {
var matching = this.filter(function(testCase) { return testCase.path === path; });
if (matching.length === 0) { throw "failed to find test case with path " + path; }
return matching[0];
};
var server = net.createServer(function(connection) {
connection.on('data', function(data) {
var path = data.toString().match(/GET (.*) HTTP.1.1/)[1];
var testCase = testCases.findByPath(path);
connection.write(testCase.response);
connection.end();
});
});
var runTest = function(testCaseIndex) {
var testCase = testCases[testCaseIndex];
http.get({ port: common.PORT, path: testCase.path }, function(response) {
console.log('client: expected status message: ' + testCase.statusMessage);
console.log('client: actual status message: ' + response.statusMessage);
assert.equal(testCase.statusMessage, response.statusMessage);
response.on('end', function() {
testsComplete++;
if (testCaseIndex + 1 < testCases.length) {
runTest(testCaseIndex + 1);
} else {
server.close();
}
});
response.resume();
});
};
server.listen(common.PORT, function() { runTest(0); });
process.on('exit', function() {
assert.equal(testCases.length, testsComplete);
});