dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.06k stars 1.56k forks source link

It is not possible to detect a client disconnect using `HttpServer` #54014

Open moham96 opened 10 months ago

moham96 commented 10 months ago

Hi, Currently i can see no way to detect that a client disconnected when using HttpServer, the response streaming methods addStream and pipe return a future that finishes when the streamed response is finished, but it doesn't return or throw an exception when the client disconnect before the server sends the full response.

Here is an example to show the problem:

import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';

final rand = Random();
Uint8List randomString(int length) {
  final result = Uint8List(length);
  for (var i = 0; i < length; i++) {
    result[i] = 48 + rand.nextInt(122 - 48);
  }
  return result;
}

StreamController<List<int>> streamController = StreamController<List<int>>();
Future<void> requestProcessor(HttpRequest request) async {
  var startTime = DateTime.now();
  print('[$startTime] got request');
  streamController.sink.done.then((value) => print("Stream sink is done"));
  Future.delayed(Duration(milliseconds: 200), () {
    // we intentionally don't close the stream here to keep the connection open
    print('Sending random bytes');
    streamController.add(randomString(10240));
  });
  await streamController.stream.pipe(request
      .response); // the future doesn't finish when the client closed the connection
  // await request.response.addStream(streamController.stream); // here also
  print('client closed the connection? ');

  var endTime = DateTime.now();
  print(
      '[$endTime] request ended, took ${endTime.difference(startTime).inSeconds} seconds');
}

void startServer() async {
  var server = await HttpServer.bind(InternetAddress.anyIPv4, 1000);
  server.listen(
    requestProcessor,
    onDone: () => print('listen done'),
    onError: (Object e) => print("listen error"),
  );
}

To test use something like curl:

curl  -v  --output - 127.0.0.1:1000

Then after you receive the response kill the curl command with ctrl+c which should simulate a client disconnect before receiving the full response. you can see that there is nothing that signals that the client has disconnected prematurely.

Regards

moham96 commented 5 months ago

Any updates on this? is this possible?