Skip to content

Does HttpClient open two TLS connection instead of just one? #48652

Closed
@sma

Description

@sma

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"

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-core-librarySDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries.closed-duplicateClosed in favor of an existing reportlibrary-io

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions