From 5b1a535cd89624a021b4bd183cfaaccef5b63dd9 Mon Sep 17 00:00:00 2001 From: Rhys Jones Date: Mon, 30 Nov 2009 16:51:20 +0100 Subject: [PATCH] Add HTTP client TLS support --- lib/http.js | 25 ++++++++ src/node_net.cc | 13 ----- src/node_net.h | 13 +++++ test/mjsunit/test-http-tls.js | 107 ++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 13 deletions(-) create mode 100644 test/mjsunit/test-http-tls.js diff --git a/lib/http.js b/lib/http.js index caeff0f2c7..609a3bcdba 100644 --- a/lib/http.js +++ b/lib/http.js @@ -505,6 +505,7 @@ function connectionListener (connection) { exports.createClient = function (port, host) { var client = new process.http.Client(); + var secure_credentials={ secure : false }; var requests = []; @@ -513,6 +514,13 @@ exports.createClient = function (port, host) { if (client.readyState == "closed") { //sys.debug("HTTP CLIENT request flush. reconnect. readyState = " + client.readyState); client.connect(port, host); // reconnect + if (secure_credentials.secure) { + client.tcpSetSecure(secure_credentials.format_type, + secure_credentials.ca_certs, + secure_credentials.crl_list, + secure_credentials.private_key, + secure_credentials.certificate); + } return; } //sys.debug("client flush readyState = " + client.readyState); @@ -521,6 +529,16 @@ exports.createClient = function (port, host) { requests.push(req); }; + client.tcpSetSecure = client.setSecure; + client.setSecure = function(format_type, ca_certs, crl_list, private_key, certificate) { + secure_credentials.secure = true; + secure_credentials.format_type = format_type; + secure_credentials.ca_certs = ca_certs; + secure_credentials.crl_list = crl_list; + secure_credentials.private_key = private_key; + secure_credentials.certificate = certificate; + } + client.addListener("connect", function () { client.resetParser(); requests[0].flush(); @@ -543,6 +561,13 @@ exports.createClient = function (port, host) { if (requests.length > 0 && client.readyState != "opening") { //sys.debug("HTTP CLIENT: reconnecting readyState = " + client.readyState); client.connect(port, host); // reconnect + if (secure_credentials.secure) { + client.tcpSetSecure(secure_credentials.format_type, + secure_credentials.ca_certs, + secure_credentials.crl_list, + secure_credentials.private_key, + secure_credentials.certificate); + } } }); diff --git a/src/node_net.cc b/src/node_net.cc index 9966bf4407..1387589917 100644 --- a/src/node_net.cc +++ b/src/node_net.cc @@ -599,19 +599,6 @@ void Connection::OnClose() { }; Emit("close", 2, argv); - - #if EVCOM_HAVE_GNUTLS - if (secure_) { - if (stream_.session) { - gnutls_deinit(stream_.session); - stream_.session = NULL; - } - if (!stream_.server && credentials) { - gnutls_certificate_free_credentials(credentials); - credentials = NULL; - } - } - #endif } void Connection::OnConnect() { diff --git a/src/node_net.h b/src/node_net.h index cde3f2bae0..6eccec9aff 100644 --- a/src/node_net.h +++ b/src/node_net.h @@ -134,6 +134,19 @@ class Connection : public EventEmitter { assert(connection->stream_.recvfd < 0); assert(connection->stream_.sendfd < 0); + #if EVCOM_HAVE_GNUTLS + if (connection->secure_) { + if (connection->stream_.session) { + gnutls_deinit(connection->stream_.session); + connection->stream_.session = NULL; + } + if (!connection->stream_.server && connection->credentials) { + gnutls_certificate_free_credentials(connection->credentials); + connection->credentials = NULL; + } + } + #endif + connection->OnClose(); assert(connection->attached_); diff --git a/test/mjsunit/test-http-tls.js b/test/mjsunit/test-http-tls.js new file mode 100644 index 0000000000..dd85c150ee --- /dev/null +++ b/test/mjsunit/test-http-tls.js @@ -0,0 +1,107 @@ +process.mixin(require("./common")); +http = require("http"); +PORT = 8888; + +HOST = "localhost"; + +var have_tls; +try { + var dummy_server = http.createServer(); + dummy_server.setSecure(); + have_tls=true; +} catch (e) { + have_tls=false; + puts("Not compiled with TLS support."); + process.exit(); +} + + +var responses_sent = 0; +var responses_recvd = 0; +var body0 = ""; +var body1 = ""; +var caPem = posix.cat(fixturesDir+"/test_ca.pem").wait(); +var certPem = posix.cat(fixturesDir+"/test_cert.pem").wait(); +var keyPem = posix.cat(fixturesDir+"/test_key.pem").wait(); + + +var http_server=http.createServer(function (req, res) { + var verified = req.connection.verifyPeer(); + var peerDN = req.connection.getPeerCertificate("DNstring"); + assertEquals(verified, 1); + assertEquals(peerDN, "C=UK,ST=Acknack Ltd,L=Rhys Jones,O=node.js," + + "OU=Test TLS Certificate,CN=localhost"); + + if (responses_sent == 0) { + assertEquals("GET", req.method); + assertEquals("/hello", req.uri.path); + + p(req.headers); + assertTrue("accept" in req.headers); + assertEquals("*/*", req.headers["accept"]); + + assertTrue("foo" in req.headers); + assertEquals("bar", req.headers["foo"]); + } + + if (responses_sent == 1) { + assertEquals("POST", req.method); + assertEquals("/world", req.uri.path); + this.close(); + } + + req.addListener("complete", function () { + res.sendHeader(200, {"Content-Type": "text/plain"}); + res.sendBody("The path was " + req.uri.path); + res.finish(); + responses_sent += 1; + }); + + //assertEquals("127.0.0.1", res.connection.remoteAddress); +}); +http_server.setSecure("X509_PEM", caPem, 0, keyPem, certPem); +http_server.listen(PORT); + +var client = http.createClient(PORT, HOST); +client.setSecure("x509_PEM", caPem, 0, keyPem, certPem); +var req = client.get("/hello", {"Accept": "*/*", "Foo": "bar"}); +req.finish(function (res) { + var verified = res.connection.verifyPeer(); + var peerDN = res.connection.getPeerCertificate("DNstring"); + assertEquals(verified, 1); + assertEquals(peerDN, "C=UK,ST=Acknack Ltd,L=Rhys Jones,O=node.js," + + "OU=Test TLS Certificate,CN=localhost"); + assertEquals(200, res.statusCode); + responses_recvd += 1; + res.setBodyEncoding("ascii"); + res.addListener("body", function (chunk) { body0 += chunk; }); + debug("Got /hello response"); +}); + +setTimeout(function () { + req = client.post("/world"); + req.finish(function (res) { + var verified = res.connection.verifyPeer(); + var peerDN = res.connection.getPeerCertificate("DNstring"); + assertEquals(verified, 1); + assertEquals(peerDN, "C=UK,ST=Acknack Ltd,L=Rhys Jones,O=node.js," + + "OU=Test TLS Certificate,CN=localhost"); + assertEquals(200, res.statusCode); + responses_recvd += 1; + res.setBodyEncoding("utf8"); + res.addListener("body", function (chunk) { body1 += chunk; }); + debug("Got /world response"); + }); +}, 1); + +process.addListener("exit", function () { + debug("responses_recvd: " + responses_recvd); + assertEquals(2, responses_recvd); + + debug("responses_sent: " + responses_sent); + assertEquals(2, responses_sent); + + assertEquals("The path was /hello", body0); + assertEquals("The path was /world", body1); +}); +