Browse Source

Merge remote-tracking branch 'origin/v0.10'

Conflicts:
	deps/uv/ChangeLog
	deps/uv/src/unix/stream.c
	deps/uv/src/version.c
	lib/tls.js
v0.11.3-release
Ben Noordhuis 12 years ago
parent
commit
28659aba37
  1. 2
      doc/api/child_process.markdown
  2. 4
      doc/api/https.markdown
  3. 5
      doc/api/tls.markdown
  4. 14
      doc/community/index.html
  5. 102
      lib/tls.js
  6. 45
      src/node_crypto.cc
  7. 7
      src/node_crypto.h
  8. 75
      test/simple/test-tls-client-destroy-soon.js

2
doc/api/child_process.markdown

@ -225,7 +225,7 @@ that some connections will be handled by the parent and some by the child.
For `dgram` servers the workflow is exactly the same. Here you listen on For `dgram` servers the workflow is exactly the same. Here you listen on
a `message` event instead of `connection` and use `server.bind` instead of a `message` event instead of `connection` and use `server.bind` instead of
`server.listen`. `server.listen`. (Currently only supported on UNIX platforms.)
#### Example: sending socket object #### Example: sending socket object

4
doc/api/https.markdown

@ -134,6 +134,9 @@ The following options from [tls.connect()][] can also be specified. However, a
the list of supplied CAs. An `'error'` event is emitted if verification the list of supplied CAs. An `'error'` event is emitted if verification
fails. Verification happens at the connection level, *before* the HTTP fails. Verification happens at the connection level, *before* the HTTP
request is sent. Default `true`. request is sent. Default `true`.
- `secureProtocol`: The SSL method to use, e.g. `SSLv3_method` to force
SSL version 3. The possible values depend on your installation of
OpenSSL and are defined in the constant [SSL_METHODS][].
In order to specify these options, use a custom `Agent`. In order to specify these options, use a custom `Agent`.
@ -217,3 +220,4 @@ Global instance of [https.Agent][] for all HTTPS client requests.
[https.request()]: #https_https_request_options_callback [https.request()]: #https_https_request_options_callback
[tls.connect()]: tls.html#tls_tls_connect_options_callback [tls.connect()]: tls.html#tls_tls_connect_options_callback
[tls.createServer()]: tls.html#tls_tls_createserver_options_secureconnectionlistener [tls.createServer()]: tls.html#tls_tls_createserver_options_secureconnectionlistener
[SSL_METHODS]: http://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_PROTOCOL_METHODS

5
doc/api/tls.markdown

@ -278,6 +278,10 @@ Creates a new client connection to the given `port` and `host` (old API) or
- `servername`: Servername for SNI (Server Name Indication) TLS extension. - `servername`: Servername for SNI (Server Name Indication) TLS extension.
- `secureProtocol`: The SSL method to use, e.g. `SSLv3_method` to force
SSL version 3. The possible values depend on your installation of
OpenSSL and are defined in the constant [SSL_METHODS][].
The `callback` parameter will be added as a listener for the The `callback` parameter will be added as a listener for the
['secureConnect'][] event. ['secureConnect'][] event.
@ -572,5 +576,6 @@ The numeric representation of the local port.
['secureConnect']: #tls_event_secureconnect ['secureConnect']: #tls_event_secureconnect
[secureConnection]: #tls_event_secureconnection [secureConnection]: #tls_event_secureconnection
[Stream]: stream.html#stream_stream [Stream]: stream.html#stream_stream
[SSL_METHODS]: http://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_PROTOCOL_METHODS
[tls.Server]: #tls_class_tls_server [tls.Server]: #tls_class_tls_server
[SSL_CTX_set_timeout]: http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html [SSL_CTX_set_timeout]: http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html

14
doc/community/index.html

@ -54,14 +54,6 @@
<a href="http://nodejs.org/api">Official API docs</a> detail the <a href="http://nodejs.org/api">Official API docs</a> detail the
node API. node API.
</p> </p>
<p>
<a href="http://www.nodemanual.org">Node Manual</a> offers
stylized API docs, as well as tutorials and live code samples
</p>
<p>
<a href="http://www.nodebits.org">Node Bits</a> provides sample
Node.js projects
</p>
<p> <p>
<a href="http://docs.nodejitsu.com/">docs.nodejitsu.com</a> <a href="http://docs.nodejitsu.com/">docs.nodejitsu.com</a>
answers many of the common problems people come across. answers many of the common problems people come across.
@ -187,9 +179,11 @@
<a href="http://nodejskr.org">OctoberSkyJs</a> Korea Node.js <a href="http://nodejskr.org">OctoberSkyJs</a> Korea Node.js
community<br> community<br>
<a href="https://plus.google.com/communities/113346206415381691435">FR . Node.js</a> <a href="https://plus.google.com/communities/113346206415381691435">FR . Node.js</a>
Google+ Community of Node.js French users Google+ Community of Node.js French users<br>
<a href="http://www.nodejstr.com/">Node.js Türkiye</a> <a href="http://www.nodejstr.com/">Node.js Türkiye</a>
Node.js in Turkish Node.js in Turkish<br>
<a href="http://www.nodebr.com/">NodeBR.com</a>
Brazilian community of Node.js
</p> </p>
</div> </div>
</div> </div>

102
lib/tls.js

@ -250,6 +250,8 @@ function CryptoStream(pair, options) {
this._pendingCallback = null; this._pendingCallback = null;
this._doneFlag = false; this._doneFlag = false;
this._retryAfterPartial = false; this._retryAfterPartial = false;
this._halfRead = false;
this._sslOutCb = null;
this._resumingSession = false; this._resumingSession = false;
this._reading = true; this._reading = true;
this._destroyed = false; this._destroyed = false;
@ -278,8 +280,14 @@ function onCryptoStreamFinish() {
// NOTE: first call checks if client has sent us shutdown, // NOTE: first call checks if client has sent us shutdown,
// second call enqueues shutdown into the BIO. // second call enqueues shutdown into the BIO.
if (this.pair.ssl.shutdown() !== 1) { if (this.pair.ssl.shutdown() !== 1) {
if (this.pair.ssl && this.pair.ssl.error)
return this.pair.error();
this.pair.ssl.shutdown(); this.pair.ssl.shutdown();
} }
if (this.pair.ssl && this.pair.ssl.error)
return this.pair.error();
} }
} else { } else {
debug('encrypted.onfinish'); debug('encrypted.onfinish');
@ -309,6 +317,19 @@ function onCryptoStreamEnd() {
} }
// NOTE: Called once `this._opposite` is set.
CryptoStream.prototype.init = function init() {
var self = this;
this._opposite.on('sslOutEnd', function() {
if (self._sslOutCb) {
var cb = self._sslOutCb;
self._sslOutCb = null;
cb(null);
}
});
};
CryptoStream.prototype._write = function write(data, encoding, cb) { CryptoStream.prototype._write = function write(data, encoding, cb) {
assert(this._pending === null); assert(this._pending === null);
@ -337,28 +358,36 @@ CryptoStream.prototype._write = function write(data, encoding, cb) {
return cb(this.pair.error(true)); return cb(this.pair.error(true));
} }
// Whole buffer was written
if (written === data.length) {
if (this === this.pair.cleartext) {
debug('cleartext.write succeed with ' + written + ' bytes');
} else {
debug('encrypted.write succeed with ' + written + ' bytes');
}
// Invoke callback only when all data read from opposite stream
if (this._opposite._halfRead) {
assert(this._sslOutCb === null);
this._sslOutCb = cb;
} else {
cb(null);
}
}
// Force SSL_read call to cycle some states/data inside OpenSSL // Force SSL_read call to cycle some states/data inside OpenSSL
this.pair.cleartext.read(0); this.pair.cleartext.read(0);
// Cycle encrypted data // Cycle encrypted data
if (this.pair.encrypted._internallyPendingBytes()) { if (this.pair.encrypted._internallyPendingBytes())
this.pair.encrypted.read(0); this.pair.encrypted.read(0);
}
// Get NPN and Server name when ready // Get NPN and Server name when ready
this.pair.maybeInitFinished(); this.pair.maybeInitFinished();
// Whole buffer was written
if (written === data.length) { if (written === data.length) {
if (this === this.pair.cleartext) { return;
debug('cleartext.write succeed with %d bytes', data.length); } else if (written !== 0 && written !== -1) {
} else {
debug('encrypted.write succeed with %d bytes', data.length);
}
return cb(null);
}
if (written !== 0 && written !== -1) {
assert(!this._retryAfterPartial); assert(!this._retryAfterPartial);
this._retryAfterPartial = true; this._retryAfterPartial = true;
this._write(data.slice(written), encoding, cb); this._write(data.slice(written), encoding, cb);
@ -460,25 +489,42 @@ CryptoStream.prototype._read = function read(size) {
this._opposite._done(); this._opposite._done();
// EOF // EOF
return this.push(null); this.push(null);
} else {
// Bail out
this.push('');
} }
} else {
// Bail out // Give them requested data
return this.push(''); if (this.ondata) {
var self = this;
this.ondata(pool, start, start + bytesRead);
// Consume data automatically
// simple/test-https-drain fails without it
process.nextTick(function() {
self.read(bytesRead);
});
}
this.push(pool.slice(start, start + bytesRead));
} }
// Give them requested data // Let users know that we've some internal data to read
if (this.ondata) { var halfRead = this._internallyPendingBytes() !== 0;
var self = this;
this.ondata(pool, start, start + bytesRead);
// Consume data automatically // Smart check to avoid invoking 'sslOutEnd' in the most of the cases
// simple/test-https-drain fails without it if (this._halfRead !== halfRead) {
process.nextTick(function() { this._halfRead = halfRead;
self.read(bytesRead);
}); // Notify listeners about internal data end
if (this === this.pair.cleartext) {
debug('cleartext.sslOutEnd');
} else {
debug('encrypted.sslOutEnd');
}
this.emit('sslOutEnd');
} }
return this.push(pool.slice(start, start + bytesRead));
}; };
@ -590,7 +636,7 @@ CryptoStream.prototype.destroySoon = function(err) {
if (this.writable) if (this.writable)
this.end(); this.end();
if (this._writableState.finished) if (this._writableState.finished && this._opposite._ended)
this.destroy(); this.destroy();
else else
this.once('finish', this.destroy); this.once('finish', this.destroy);
@ -871,6 +917,8 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
/* Let streams know about each other */ /* Let streams know about each other */
this.cleartext._opposite = this.encrypted; this.cleartext._opposite = this.encrypted;
this.encrypted._opposite = this.cleartext; this.encrypted._opposite = this.cleartext;
this.cleartext.init();
this.encrypted.init();
process.nextTick(function() { process.nextTick(function() {
/* The Connection may be destroyed by an abort call */ /* The Connection may be destroyed by an abort call */

45
src/node_crypto.cc

@ -934,7 +934,10 @@ int Connection::HandleBIOError(BIO *bio, const char* func, int rv) {
} }
int Connection::HandleSSLError(const char* func, int rv, ZeroStatus zs) { int Connection::HandleSSLError(const char* func,
int rv,
ZeroStatus zs,
SyscallStatus ss) {
ClearErrorOnReturn clear_error_on_return; ClearErrorOnReturn clear_error_on_return;
(void) &clear_error_on_return; // Silence unused variable warning. (void) &clear_error_on_return; // Silence unused variable warning.
@ -959,6 +962,9 @@ int Connection::HandleSSLError(const char* func, int rv, ZeroStatus zs) {
Exception::Error(String::New("ZERO_RETURN"))); Exception::Error(String::New("ZERO_RETURN")));
return rv; return rv;
} else if ((err == SSL_ERROR_SYSCALL) && (ss == kIgnoreSyscall)) {
return 0;
} else { } else {
HandleScope scope(node_isolate); HandleScope scope(node_isolate);
BUF_MEM* mem; BUF_MEM* mem;
@ -1396,17 +1402,26 @@ Handle<Value> Connection::ClearOut(const Arguments& args) {
if (ss->is_server_) { if (ss->is_server_) {
rv = SSL_accept(ss->ssl_); rv = SSL_accept(ss->ssl_);
ss->HandleSSLError("SSL_accept:ClearOut", rv, kZeroIsAnError); ss->HandleSSLError("SSL_accept:ClearOut",
rv,
kZeroIsAnError,
kSyscallError);
} else { } else {
rv = SSL_connect(ss->ssl_); rv = SSL_connect(ss->ssl_);
ss->HandleSSLError("SSL_connect:ClearOut", rv, kZeroIsAnError); ss->HandleSSLError("SSL_connect:ClearOut",
rv,
kZeroIsAnError,
kSyscallError);
} }
if (rv < 0) return scope.Close(Integer::New(rv, node_isolate)); if (rv < 0) return scope.Close(Integer::New(rv, node_isolate));
} }
int bytes_read = SSL_read(ss->ssl_, buffer_data + off, len); int bytes_read = SSL_read(ss->ssl_, buffer_data + off, len);
ss->HandleSSLError("SSL_read:ClearOut", bytes_read, kZeroIsNotAnError); ss->HandleSSLError("SSL_read:ClearOut",
bytes_read,
kZeroIsNotAnError,
kSyscallError);
ss->SetShutdownFlags(); ss->SetShutdownFlags();
return scope.Close(Integer::New(bytes_read, node_isolate)); return scope.Close(Integer::New(bytes_read, node_isolate));
@ -1496,10 +1511,16 @@ Handle<Value> Connection::ClearIn(const Arguments& args) {
int rv; int rv;
if (ss->is_server_) { if (ss->is_server_) {
rv = SSL_accept(ss->ssl_); rv = SSL_accept(ss->ssl_);
ss->HandleSSLError("SSL_accept:ClearIn", rv, kZeroIsAnError); ss->HandleSSLError("SSL_accept:ClearIn",
rv,
kZeroIsAnError,
kSyscallError);
} else { } else {
rv = SSL_connect(ss->ssl_); rv = SSL_connect(ss->ssl_);
ss->HandleSSLError("SSL_connect:ClearIn", rv, kZeroIsAnError); ss->HandleSSLError("SSL_connect:ClearIn",
rv,
kZeroIsAnError,
kSyscallError);
} }
if (rv < 0) return scope.Close(Integer::New(rv, node_isolate)); if (rv < 0) return scope.Close(Integer::New(rv, node_isolate));
@ -1509,7 +1530,8 @@ Handle<Value> Connection::ClearIn(const Arguments& args) {
ss->HandleSSLError("SSL_write:ClearIn", ss->HandleSSLError("SSL_write:ClearIn",
bytes_written, bytes_written,
len == 0 ? kZeroIsNotAnError : kZeroIsAnError); len == 0 ? kZeroIsNotAnError : kZeroIsAnError,
kSyscallError);
ss->SetShutdownFlags(); ss->SetShutdownFlags();
return scope.Close(Integer::New(bytes_written, node_isolate)); return scope.Close(Integer::New(bytes_written, node_isolate));
@ -1749,10 +1771,13 @@ Handle<Value> Connection::Start(const Arguments& args) {
int rv; int rv;
if (ss->is_server_) { if (ss->is_server_) {
rv = SSL_accept(ss->ssl_); rv = SSL_accept(ss->ssl_);
ss->HandleSSLError("SSL_accept:Start", rv, kZeroIsAnError); ss->HandleSSLError("SSL_accept:Start", rv, kZeroIsAnError, kSyscallError);
} else { } else {
rv = SSL_connect(ss->ssl_); rv = SSL_connect(ss->ssl_);
ss->HandleSSLError("SSL_connect:Start", rv, kZeroIsAnError); ss->HandleSSLError("SSL_connect:Start",
rv,
kZeroIsAnError,
kSyscallError);
} }
return scope.Close(Integer::New(rv, node_isolate)); return scope.Close(Integer::New(rv, node_isolate));
@ -1769,7 +1794,7 @@ Handle<Value> Connection::Shutdown(const Arguments& args) {
if (ss->ssl_ == NULL) return False(node_isolate); if (ss->ssl_ == NULL) return False(node_isolate);
int rv = SSL_shutdown(ss->ssl_); int rv = SSL_shutdown(ss->ssl_);
ss->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError); ss->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError, kIgnoreSyscall);
ss->SetShutdownFlags(); ss->SetShutdownFlags();
return scope.Close(Integer::New(rv, node_isolate)); return scope.Close(Integer::New(rv, node_isolate));

7
src/node_crypto.h

@ -219,7 +219,12 @@ class Connection : ObjectWrap {
kZeroIsAnError kZeroIsAnError
}; };
int HandleSSLError(const char* func, int rv, ZeroStatus zs); enum SyscallStatus {
kIgnoreSyscall,
kSyscallError
};
int HandleSSLError(const char* func, int rv, ZeroStatus zs, SyscallStatus ss);
void ClearError(); void ClearError();
void SetShutdownFlags(); void SetShutdownFlags();

75
test/simple/test-tls-client-destroy-soon.js

@ -0,0 +1,75 @@
// 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.
// Create an ssl server. First connection, validate that not resume.
// Cache session and close connection. Use session on second connection.
// ASSERT resumption.
if (!process.versions.openssl) {
console.error('Skipping because node compiled without OpenSSL.');
process.exit(0);
}
var common = require('../common');
var assert = require('assert');
var tls = require('tls');
var fs = require('fs');
var options = {
key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'),
cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem')
};
var big = new Buffer(2 * 1024 * 1024);
var connections = 0;
var bytesRead = 0;
big.fill('Y');
// create server
var server = tls.createServer(options, function(socket) {
socket.end(big);
socket.destroySoon();
connections++;
});
// start listening
server.listen(common.PORT, function() {
var client = tls.connect({
port: common.PORT,
rejectUnauthorized: false
}, function() {
client.on('readable', function() {
var d = client.read();
if (d)
bytesRead += d.length;
});
client.on('end', function() {
server.close();
});
});
});
process.on('exit', function() {
assert.equal(1, connections);
assert.equal(big.length, bytesRead);
});
Loading…
Cancel
Save