mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
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
This commit is contained in:
@@ -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
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
fails. Verification happens at the connection level, *before* the HTTP
|
||||
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`.
|
||||
|
||||
@@ -217,3 +220,4 @@ Global instance of [https.Agent][] for all HTTPS client requests.
|
||||
[https.request()]: #https_https_request_options_callback
|
||||
[tls.connect()]: tls.html#tls_tls_connect_options_callback
|
||||
[tls.createServer()]: tls.html#tls_tls_createserver_options_secureconnectionlistener
|
||||
[SSL_METHODS]: http://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_PROTOCOL_METHODS
|
||||
|
||||
@@ -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.
|
||||
|
||||
- `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
|
||||
['secureConnect'][] event.
|
||||
|
||||
@@ -572,5 +576,6 @@ The numeric representation of the local port.
|
||||
['secureConnect']: #tls_event_secureconnect
|
||||
[secureConnection]: #tls_event_secureconnection
|
||||
[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
|
||||
[SSL_CTX_set_timeout]: http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html
|
||||
|
||||
@@ -54,14 +54,6 @@
|
||||
<a href="http://nodejs.org/api">Official API docs</a> detail the
|
||||
node API.
|
||||
</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>
|
||||
<a href="http://docs.nodejitsu.com/">docs.nodejitsu.com</a>
|
||||
answers many of the common problems people come across.
|
||||
@@ -187,9 +179,11 @@
|
||||
<a href="http://nodejskr.org">OctoberSkyJs</a> Korea Node.js
|
||||
community<br>
|
||||
<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>
|
||||
Node.js in Turkish
|
||||
Node.js in Turkish<br>
|
||||
<a href="http://www.nodebr.com/">NodeBR.com</a>
|
||||
Brazilian community of Node.js
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
106
lib/tls.js
106
lib/tls.js
@@ -250,6 +250,8 @@ function CryptoStream(pair, options) {
|
||||
this._pendingCallback = null;
|
||||
this._doneFlag = false;
|
||||
this._retryAfterPartial = false;
|
||||
this._halfRead = false;
|
||||
this._sslOutCb = null;
|
||||
this._resumingSession = false;
|
||||
this._reading = true;
|
||||
this._destroyed = false;
|
||||
@@ -278,8 +280,14 @@ function onCryptoStreamFinish() {
|
||||
// NOTE: first call checks if client has sent us shutdown,
|
||||
// second call enqueues shutdown into the BIO.
|
||||
if (this.pair.ssl.shutdown() !== 1) {
|
||||
if (this.pair.ssl && this.pair.ssl.error)
|
||||
return this.pair.error();
|
||||
|
||||
this.pair.ssl.shutdown();
|
||||
}
|
||||
|
||||
if (this.pair.ssl && this.pair.ssl.error)
|
||||
return this.pair.error();
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
assert(this._pending === null);
|
||||
|
||||
@@ -337,28 +358,36 @@ CryptoStream.prototype._write = function write(data, encoding, cb) {
|
||||
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
|
||||
this.pair.cleartext.read(0);
|
||||
|
||||
// Cycle encrypted data
|
||||
if (this.pair.encrypted._internallyPendingBytes()) {
|
||||
if (this.pair.encrypted._internallyPendingBytes())
|
||||
this.pair.encrypted.read(0);
|
||||
}
|
||||
|
||||
// Get NPN and Server name when ready
|
||||
this.pair.maybeInitFinished();
|
||||
|
||||
// Whole buffer was written
|
||||
if (written === data.length) {
|
||||
if (this === this.pair.cleartext) {
|
||||
debug('cleartext.write succeed with %d bytes', data.length);
|
||||
} else {
|
||||
debug('encrypted.write succeed with %d bytes', data.length);
|
||||
}
|
||||
|
||||
return cb(null);
|
||||
}
|
||||
if (written !== 0 && written !== -1) {
|
||||
return;
|
||||
} else if (written !== 0 && written !== -1) {
|
||||
assert(!this._retryAfterPartial);
|
||||
this._retryAfterPartial = true;
|
||||
this._write(data.slice(written), encoding, cb);
|
||||
@@ -460,25 +489,42 @@ CryptoStream.prototype._read = function read(size) {
|
||||
this._opposite._done();
|
||||
|
||||
// EOF
|
||||
return this.push(null);
|
||||
this.push(null);
|
||||
} else {
|
||||
// Bail out
|
||||
this.push('');
|
||||
}
|
||||
} else {
|
||||
// Give them requested data
|
||||
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));
|
||||
}
|
||||
|
||||
// Let users know that we've some internal data to read
|
||||
var halfRead = this._internallyPendingBytes() !== 0;
|
||||
|
||||
// Smart check to avoid invoking 'sslOutEnd' in the most of the cases
|
||||
if (this._halfRead !== halfRead) {
|
||||
this._halfRead = halfRead;
|
||||
|
||||
// Notify listeners about internal data end
|
||||
if (this === this.pair.cleartext) {
|
||||
debug('cleartext.sslOutEnd');
|
||||
} else {
|
||||
debug('encrypted.sslOutEnd');
|
||||
}
|
||||
|
||||
// Bail out
|
||||
return this.push('');
|
||||
this.emit('sslOutEnd');
|
||||
}
|
||||
|
||||
// Give them requested data
|
||||
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);
|
||||
});
|
||||
}
|
||||
return this.push(pool.slice(start, start + bytesRead));
|
||||
};
|
||||
|
||||
|
||||
@@ -590,7 +636,7 @@ CryptoStream.prototype.destroySoon = function(err) {
|
||||
if (this.writable)
|
||||
this.end();
|
||||
|
||||
if (this._writableState.finished)
|
||||
if (this._writableState.finished && this._opposite._ended)
|
||||
this.destroy();
|
||||
else
|
||||
this.once('finish', this.destroy);
|
||||
@@ -871,6 +917,8 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
|
||||
/* Let streams know about each other */
|
||||
this.cleartext._opposite = this.encrypted;
|
||||
this.encrypted._opposite = this.cleartext;
|
||||
this.cleartext.init();
|
||||
this.encrypted.init();
|
||||
|
||||
process.nextTick(function() {
|
||||
/* The Connection may be destroyed by an abort call */
|
||||
|
||||
@@ -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;
|
||||
(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")));
|
||||
return rv;
|
||||
|
||||
} else if ((err == SSL_ERROR_SYSCALL) && (ss == kIgnoreSyscall)) {
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
HandleScope scope(node_isolate);
|
||||
BUF_MEM* mem;
|
||||
@@ -1396,17 +1402,26 @@ Handle<Value> Connection::ClearOut(const Arguments& args) {
|
||||
|
||||
if (ss->is_server_) {
|
||||
rv = SSL_accept(ss->ssl_);
|
||||
ss->HandleSSLError("SSL_accept:ClearOut", rv, kZeroIsAnError);
|
||||
ss->HandleSSLError("SSL_accept:ClearOut",
|
||||
rv,
|
||||
kZeroIsAnError,
|
||||
kSyscallError);
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
return scope.Close(Integer::New(bytes_read, node_isolate));
|
||||
@@ -1496,10 +1511,16 @@ Handle<Value> Connection::ClearIn(const Arguments& args) {
|
||||
int rv;
|
||||
if (ss->is_server_) {
|
||||
rv = SSL_accept(ss->ssl_);
|
||||
ss->HandleSSLError("SSL_accept:ClearIn", rv, kZeroIsAnError);
|
||||
ss->HandleSSLError("SSL_accept:ClearIn",
|
||||
rv,
|
||||
kZeroIsAnError,
|
||||
kSyscallError);
|
||||
} else {
|
||||
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));
|
||||
@@ -1509,7 +1530,8 @@ Handle<Value> Connection::ClearIn(const Arguments& args) {
|
||||
|
||||
ss->HandleSSLError("SSL_write:ClearIn",
|
||||
bytes_written,
|
||||
len == 0 ? kZeroIsNotAnError : kZeroIsAnError);
|
||||
len == 0 ? kZeroIsNotAnError : kZeroIsAnError,
|
||||
kSyscallError);
|
||||
ss->SetShutdownFlags();
|
||||
|
||||
return scope.Close(Integer::New(bytes_written, node_isolate));
|
||||
@@ -1749,10 +1771,13 @@ Handle<Value> Connection::Start(const Arguments& args) {
|
||||
int rv;
|
||||
if (ss->is_server_) {
|
||||
rv = SSL_accept(ss->ssl_);
|
||||
ss->HandleSSLError("SSL_accept:Start", rv, kZeroIsAnError);
|
||||
ss->HandleSSLError("SSL_accept:Start", rv, kZeroIsAnError, kSyscallError);
|
||||
} else {
|
||||
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));
|
||||
@@ -1769,7 +1794,7 @@ Handle<Value> Connection::Shutdown(const Arguments& args) {
|
||||
|
||||
if (ss->ssl_ == NULL) return False(node_isolate);
|
||||
int rv = SSL_shutdown(ss->ssl_);
|
||||
ss->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError);
|
||||
ss->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError, kIgnoreSyscall);
|
||||
ss->SetShutdownFlags();
|
||||
|
||||
return scope.Close(Integer::New(rv, node_isolate));
|
||||
|
||||
@@ -219,7 +219,12 @@ class Connection : ObjectWrap {
|
||||
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 SetShutdownFlags();
|
||||
|
||||
75
test/simple/test-tls-client-destroy-soon.js
Normal file
75
test/simple/test-tls-client-destroy-soon.js
Normal file
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user