darthttpstreamhttpclienthttpserver

How to send streamed response to client using Timer.periodic in dart?


I am struggling to send streamed response to the connected client. The code below is the example HttpServer used to respond to the client, the client code is also mentioned using a custom class to extend BaseRequest and the send() method to recieve the streams.

HttpClient

import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> initClient() async {
  final uri = Uri.parse('http://127.0.0.1:8080');
  final request = await CustomRequest(customMethod: 'GET', uri: uri).send();

  request.stream.transform(Utf8Decoder()).listen((str) => print(str));
}

class CustomRequest extends http.BaseRequest {
  final String customMethod;
  final Uri uri;
  CustomRequest({required this.customMethod, required this.uri})
    : super(customMethod, uri);
}

The problem is that the server successfully sends stream events to the client using Duration() with all intervals as 0s in the Timer.periodic() method.

HttpServer

import 'dart:async';
import 'dart:io';

void initServer() async {
  final HttpServer server = await HttpServer.bind('localhost', 8080);
  print('started at http://${server.address.address}:${server.port}');

  await server.forEach((HttpRequest request) async {
    print('Connected to: ${request.connectionInfo!.remoteAddress}');
    Timer.periodic(const Duration(), (timer) {
      print('Ticking... ${timer.tick}');
      request.response.add('Tick#${timer.tick}'.codeUnits);
    });
  });
}

Whereas this does not work like expected, with slight delay between each tick and the client receives no stream events at all until the request.response.close() is called which is not the way I want it to work.

import 'dart:async';
import 'dart:io';

void initServer() async {
  final HttpServer server = await HttpServer.bind('localhost', 8080);
  print('started at http://${server.address.address}:${server.port}');

  await server.forEach((HttpRequest request) async {
    print('Connected to: ${request.connectionInfo!.remoteAddress}');
    Timer.periodic(const Duration(seconds: 1), (timer) {
      print('Ticking... ${timer.tick}');
      request.response.add('Tick#${timer.tick}'.codeUnits);
    });
  });
}

Solution

  • What you are trying to do is not really what we could call "optimal usage" of HTTP which really want to buffer data in larger chunks.

    My guess with the 0 second interval is that you are creating a lot of data rather fast, which does fill up the buffers and results in sending out data.

    But when you set the interval to 1 second, then it takes a very long time for the buffer to get enough data for it makes sense for the HttpServer to send the data.

    What you can do, is disable the send buffer by doing:

      await server.forEach((HttpRequest request) async {
        request.response.bufferOutput = false;
        ...
    

    Gets or sets if the HttpResponse should buffer output.

    Default value is true.

    Note: Disabling buffering of the output can result in very poor performance, when writing many small chunks.

    https://api.dart.dev/dart-io/HttpResponse/bufferOutput.html

    As the documentation also notes, this is really not a good idea if you are going for best possible performance.

    Also, if you have a need for sending events over HTTP using streaming, I would suggest you look into WebSocket which are designed for exactly this.