Description
I think the Dart HttpClient
opens two TLS connections to a server and that causes a problem for me.
I'm working with self-signed certificates and have a required client certificate.
Here's a minimal Dart client:
import 'dart:io';
void main() async {
final key = File('app.key').readAsBytesSync();
final cert = File('app.cert').readAsBytesSync();
final sc = SecurityContext(withTrustedRoots: false)
..usePrivateKeyBytes(key)
..useCertificateChainBytes(cert)
..setAlpnProtocols(['http/1.1'], false);
final client = HttpClient(context: sc);
client.badCertificateCallback = (cert, host, port) {
print('do we talk to ${cert.subject} at $host:$port? Yes!');
return true;
};
final request = await client.openUrl('GET', Uri.parse('https://localhost:8443/'));
request.persistentConnection = false;
final response = await request.close();
print(response.statusCode);
await response.drain();
client.close();
}
Here's a minimal Node.js server:
const https = require('https');
const fs = require('fs');
const options = {
enableTrace: true,
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert'),
requestCert: true,
rejectUnauthorized: false,
};
const server = https.createServer(options, (req, res) => {
console.log(req.socket.getPeerCertificate().fingerprint);
res.writeHead(200);
res.end('Welcome!\n');
});
server.on('clientError', (err, socket) => {
console.error(err);
});
server.listen(8443);
I run the server with NODE_DEBUG=tls,https node server.js
.
Here's the trace for curl
:
TLS 2513: net.Server.on(connection): new TLSSocket
TLS 2513: server _init handle? true
TLS 2513: server initRead handle? true buffered? 0
TLS 2513: server onhandshakestart
TLS 2513: server onhandshakedone
TLS 2513: server _finishInit handle? true alpn http/1.1 servername localhost
TLS 2513: server emit secureConnection
84:E3:E6:E1:B6:59:FF:3E:D5:E9:F8:28:81:46:E3:5C:AB:58:1D:2E
And here's the trace when running the Dart client:
TLS 2527: net.Server.on(connection): new TLSSocket
TLS 2527: server _init handle? true
TLS 2527: server initRead handle? true buffered? 0
TLS 2527: net.Server.on(connection): new TLSSocket
TLS 2527: server _init handle? true
TLS 2527: server initRead handle? true buffered? 0
TLS 2527: server onhandshakestart
Error: socket hang up
at connResetException (node:internal/errors:692:14)
at TLSSocket.onSocketClose (node:_tls_wrap:1082:23)
at TLSSocket.emit (node:events:539:35)
at node:net:709:12
at Socket.done (node:_tls_wrap:582:7)
at Object.onceWrapper (node:events:642:26)
at Socket.emit (node:events:527:28)
at TCP.<anonymous> (node:net:709:12) {
code: 'ECONNRESET'
}
TLS 2527: server onhandshakestart
TLS 2527: server onhandshakedone
TLS 2527: server _finishInit handle? true alpn http/1.1 servername localhost
TLS 2527: server emit secureConnection
84:E3:E6:E1:B6:59:FF:3E:D5:E9:F8:28:81:46:E3:5C:AB:58:1D:2E
Notice the ECONNRESET
error and notice how new TLSSocket
appears twice.
It looks like Dart is establishing two connections to the server, then retracting on of them.
I think this is a problem, because the "real" server I'm talking to, will stop talking to my client on the first error. The Node.js server is more forgiving. It will eventually respond with "welcome" and talk to Dart.
Here's my Dart version. I'm working on macOS:
$ dart --version
Dart SDK version: 2.17.0-222.0.dev (dev) (Fri Mar 18 12:54:18 2022 -0700) on "macos_x64"