diff --git a/doc/api/http.markdown b/doc/api/http.markdown index e270238fdb..cc99da5399 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -483,13 +483,59 @@ Options: ### Event: 'upgrade' -`function (request, socket, head)` +`function (response, socket, head)` + +Emitted each time a server responds to a request with an upgrade. If this +event isn't being listened for, clients receiving an upgrade header will have +their connections closed. + +A client server pair that show you how to listen for the `upgrade` event using `http.getAgent`: -Emitted each time a server responds to a request with an upgrade. If this event -isn't being listened for, clients receiving an upgrade header will have their -connections closed. + var http = require('http'); + var net = require('net'); + + // Create an HTTP server + var srv = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('okay'); + }); + srv.on('upgrade', function(req, socket, upgradeHead) { + socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + + 'Upgrade: WebSocket\r\n' + + 'Connection: Upgrade\r\n' + + '\r\n\r\n'); + + socket.ondata = function(data, start, end) { + socket.write(data.toString('utf8', start, end), 'utf8'); // echo back + }; + }); + + // now that server is running + srv.listen(1337, '127.0.0.1', function() { + + // make a request + var agent = http.getAgent('127.0.0.1', 1337); + + var options = { + agent: agent, + port: 1337, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket' + } + }; + + var req = http.request(options); + req.end(); + + agent.on('upgrade', function(res, socket, upgradeHead) { + console.log('got upgraded!'); + socket.end(); + process.exit(0); + }); + }); -See the description of the [upgrade event](http.html#event_upgrade_) for `http.Server` for further details. ### Event: 'continue' diff --git a/lib/crypto.js b/lib/crypto.js index 14d26a409c..d7f403ed10 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -37,7 +37,7 @@ try { } -function Credentials(secureProtocol, flags) { +function Credentials(secureProtocol, flags, context) { if (!(this instanceof Credentials)) { return new Credentials(secureProtocol); } @@ -46,24 +46,32 @@ function Credentials(secureProtocol, flags) { throw new Error('node.js not compiled with openssl crypto support.'); } - this.context = new SecureContext(); - - if (secureProtocol) { - this.context.init(secureProtocol); + if (context) { + this.context = context; } else { - this.context.init(); - } + this.context = new SecureContext(); - if(flags) this.context.setOptions(flags); + if (secureProtocol) { + this.context.init(secureProtocol); + } else { + this.context.init(); + } + } + if (flags) this.context.setOptions(flags); } exports.Credentials = Credentials; -exports.createCredentials = function(options) { +exports.createCredentials = function(options, context) { if (!options) options = {}; - var c = new Credentials(options.secureProtocol, options.secureOptions); + + var c = new Credentials(options.secureProtocol, + options.secureOptions, + context); + + if (context) return c; if (options.key) c.context.setKey(options.key); diff --git a/lib/tls.js b/lib/tls.js index e20ee987c9..49c1ce84f8 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -244,9 +244,23 @@ CryptoStream.prototype._done = function() { }; -CryptoStream.prototype.fd = -1; -CryptoStream.prototype.__defineGetter__('readyState', - net.Socket.prototype.__lookupGetter__('readyState')); +// readyState is deprecated. Don't use it. +Object.defineProperty(CryptoStream.prototype, 'readyState', { + get: function() { + if (this._connecting) { + return 'opening'; + } else if (this.readable && this.writable) { + return 'open'; + } else if (this.readable && !this.writable) { + return 'readOnly'; + } else if (!this.readable && this.writable) { + return 'writeOnly'; + } else { + return 'closed'; + } + } +}); + // Move decrypted, clear data out into the application. // From the user's perspective this occurs as a 'data' event @@ -750,18 +764,24 @@ function Server(/* [options], listener */) { var self = this; + // Handle option defaults: + this.setOptions(options); + + var sharedCreds = crypto.createCredentials({ + key: self.key, + cert: self.cert, + ca: self.ca, + ciphers: self.ciphers, + secureProtocol: self.secureProtocol, + secureOptions: self.secureOptions, + crl: self.crl + }); + + sharedCreds.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA'); + // constructor call net.Server.call(this, function(socket) { - var creds = crypto.createCredentials({ - key: self.key, - cert: self.cert, - ca: self.ca, - ciphers: self.ciphers, - secureProtocol: self.secureProtocol, - secureOptions: self.secureOptions, - crl: self.crl - }); - creds.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA'); + var creds = crypto.createCredentials(null, sharedCreds.context); var pair = new SecurePair(creds, true, diff --git a/src/node_buffer.cc b/src/node_buffer.cc index f8d1a30879..c9fdd0c284 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -702,6 +702,11 @@ Handle Buffer::ByteLength(const Arguments &args) { Handle Buffer::MakeFastBuffer(const Arguments &args) { HandleScope scope; + if (!Buffer::HasInstance(args[0])) { + return ThrowException(Exception::TypeError(String::New( + "First argument must be a Buffer"))); + } + Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); Local fast_buffer = args[1]->ToObject();; uint32_t offset = args[2]->Uint32Value(); diff --git a/src/platform_sunos.cc b/src/platform_sunos.cc index 3011c3e2bb..3a1cedbb10 100644 --- a/src/platform_sunos.cc +++ b/src/platform_sunos.cc @@ -168,7 +168,7 @@ int Platform::GetCPUInfo(Local *cpus) { *cpus = Array::New(); lookup_instance = 0; - while (ksp = kstat_lookup(kc, "cpu_info", lookup_instance, NULL)){ + while (ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL)){ cpuinfo = Object::New(); if (kstat_read(kc, ksp, NULL) == -1) { @@ -183,9 +183,9 @@ int Platform::GetCPUInfo(Local *cpus) { cpuinfo->Set(String::New("error"), String::New(strerror(errno))); (*cpus)->Set(lookup_instance, cpuinfo); } else { - knp = (kstat_named_t *) kstat_data_lookup(ksp, "clock_MHz"); + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clock_MHz"); cpuinfo->Set(String::New("speed"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, "brand"); + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"brand"); cpuinfo->Set(String::New("model"), data_named(knp)); (*cpus)->Set(lookup_instance, cpuinfo); } @@ -194,7 +194,7 @@ int Platform::GetCPUInfo(Local *cpus) { } lookup_instance = 0; - while (ksp = kstat_lookup(kc, "cpu", lookup_instance, "sys")){ + while (ksp = kstat_lookup(kc, (char *)"cpu", lookup_instance, (char *)"sys")){ cpuinfo = (*cpus)->Get(lookup_instance)->ToObject(); cputimes = Object::New(); @@ -202,13 +202,13 @@ int Platform::GetCPUInfo(Local *cpus) { cputimes->Set(String::New("error"), String::New(strerror(errno))); cpuinfo->Set(String::New("times"), cpuinfo); } else { - knp = (kstat_named_t *) kstat_data_lookup(ksp, "cpu_ticks_kernel"); + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_kernel"); cputimes->Set(String::New("system"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, "cpu_ticks_user"); + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_user"); cputimes->Set(String::New("user"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, "cpu_ticks_idle"); + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_idle"); cputimes->Set(String::New("idle"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, "intr"); + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"intr"); cputimes->Set(String::New("irq"), data_named(knp)); cpuinfo->Set(String::New("times"), cputimes); @@ -224,12 +224,37 @@ int Platform::GetCPUInfo(Local *cpus) { double Platform::GetFreeMemory() { - return 0.0; + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + ulong_t freemem; + + if((kc = kstat_open()) == NULL) + throw "could not open kstat"; + + ksp = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_pages"); + + if(kstat_read(kc, ksp, NULL) == -1){ + throw "could not read kstat"; + } + else { + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"freemem"); + freemem = knp->value.ul; + } + + kstat_close(kc); + + return static_cast(freemem)*pagesize; } double Platform::GetTotalMemory() { - return 0.0; + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + double pages = static_cast(sysconf(_SC_PHYS_PAGES)); + + return pagesize*pages; } double Platform::GetUptimeImpl() { @@ -243,12 +268,12 @@ double Platform::GetUptimeImpl() { if ((kc = kstat_open()) == NULL) throw "could not open kstat"; - ksp = kstat_lookup(kc, "unix", 0, "system_misc"); + ksp = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc"); if (kstat_read(kc, ksp, NULL) == -1) { throw "unable to read kstat"; } else { - knp = (kstat_named_t *) kstat_data_lookup(ksp, "clk_intr"); + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clk_intr"); clk_intr = knp->value.ul; } diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index 189141ca9f..7561e5c341 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -577,3 +577,9 @@ assert.equal(0xee, b[0]); assert.equal(0xad, b[1]); assert.equal(0xbe, b[2]); assert.equal(0xef, b[3]); + + +// This should not segfault the program. +assert.throws(function() { + new Buffer('"pong"', 0, 6, 8031, '127.0.0.1') +}); diff --git a/test/simple/test-http-upgrade-agent.js b/test/simple/test-http-upgrade-agent.js new file mode 100644 index 0000000000..9f80590309 --- /dev/null +++ b/test/simple/test-http-upgrade-agent.js @@ -0,0 +1,93 @@ +// 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. + +// Verify that the 'upgrade' header causes an 'upgrade' event to be emitted to +// the HTTP client. This test uses a raw TCP server to better control server +// behavior. + +var common = require('../common'); +var assert = require('assert'); + +var http = require('http'); +var net = require('net'); + +// Create a TCP server +var srv = net.createServer(function(c) { + var data = ''; + c.addListener('data', function(d) { + data += d.toString('utf8'); + + c.write('HTTP/1.1 101\r\n'); + c.write('hello: world\r\n'); + c.write('connection: upgrade\r\n'); + c.write('upgrade: websocket\r\n'); + c.write('\r\n'); + c.write('nurtzo'); + }); + + c.addListener('end', function() { + c.end(); + }); +}); + +var gotUpgrade = false; + +srv.listen(common.PORT, '127.0.0.1', function() { + + var agent = http.getAgent({ + host: '127.0.0.1', + port: common.PORT + }); + assert.ok(agent); + + var options = { + port: common.PORT, + host: '127.0.0.1', + headers: { + 'upgrade': 'websocket' + } + }; + + var req = http.request(options); + req.end(); + + agent.on('upgrade', function(res, socket, upgradeHead) { + // XXX: This test isn't fantastic, as it assumes that the entire response + // from the server will arrive in a single data callback + assert.equal(upgradeHead, 'nurtzo'); + + console.log(res.headers); + var expectedHeaders = { 'hello': 'world', + 'connection': 'upgrade', + 'upgrade': 'websocket' }; + assert.deepEqual(expectedHeaders, res.headers); + + socket.end(); + srv.close(); + + gotUpgrade = true; + }); + +}); + +process.addListener('exit', function() { + assert.ok(gotUpgrade); +}); diff --git a/test/simple/test-tls-set-encoding.js b/test/simple/test-tls-set-encoding.js index 6634110fee..2956eec0e2 100644 --- a/test/simple/test-tls-set-encoding.js +++ b/test/simple/test-tls-set-encoding.js @@ -54,6 +54,11 @@ server.listen(common.PORT, function() { client.on('close', function() { + // readyState is deprecated but we want to make + // sure this isn't triggering an assert in lib/net.js + // See issue #1069. + assert.equal('closed', client.readyState); + assert.equal(buffer, message); console.log(message); server.close();