From 7ffe2ad6167e193c7eda46a2db31c153e07d97bd Mon Sep 17 00:00:00 2001 From: Cam Swords Date: Mon, 23 Sep 2013 00:06:58 +1000 Subject: [PATCH] http: parse the status message in a http response. --- doc/api/http.markdown | 6 ++ lib/_http_common.js | 2 +- lib/_http_incoming.js | 1 + src/env.h | 1 + src/node_http_parser.cc | 13 ++++ test/simple/test-http-parser.js | 2 + test/simple/test-http-response-status-message.js | 78 ++++++++++++++++++++++++ 7 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-http-response-status-message.js diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 182711a..d06e78f 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -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. diff --git a/lib/_http_common.js b/lib/_http_common.js index 7d89b86..b599547 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -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; diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index 2f66400..650b054 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -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. diff --git a/src/env.h b/src/env.h index 5cf414b..6db4a08 100644 --- a/src/env.h +++ b/src/env.h @@ -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") \ diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 37840d8..738358b 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -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, diff --git a/test/simple/test-http-parser.js b/test/simple/test-http-parser.js index 7536f77..a7fba0f 100644 --- a/test/simple/test-http-parser.js +++ b/test/simple/test-http-parser.js @@ -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, []); }); diff --git a/test/simple/test-http-response-status-message.js b/test/simple/test-http-response-status-message.js new file mode 100644 index 0000000..3b39110 --- /dev/null +++ b/test/simple/test-http-response-status-message.js @@ -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); +}); -- 2.7.4