Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to Receive SSE Events when running as a Flutter Web app on Chrome Web #337

Open
ponderMuse opened this issue Oct 28, 2019 · 20 comments

Comments

@ponderMuse
Copy link

Have a Flutter App that is able to receive SSE text/event-stream events when running on mobile but it is not receiving the same SSE text/event-stream events when running on the chrome web.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  http.Client _client;

  MyApp() : super() {
	print("Ctor called");
	subscribe();
  }

  @override
  Widget build(BuildContext context) {
	print("building..");
	return MaterialApp(
	  title: 'Flutter SSE',
	  home: Scaffold(
		appBar: AppBar(
		  title: Text('Receive SSE Events'),
		),
		body: Center(
		  child: Text('Ready for events..'),
		),
	  ),
	);
  }

  subscribe() async {
	print("Subscribing..");
	try {
	  _client = http.Client();

	  var request = new http.Request("GET", Uri.parse("http://192.168.1.11:8080/myserver/events"));
	  request.headers["Cache-Control"] = "no-cache";
	  request.headers["Accept"] = "text/event-stream";

	  Future<http.StreamedResponse> response = _client.send(request);
	  print("Subscribed!");

	  response.asStream().listen((streamedResponse) {
		print("Received streamedResponse.statusCode:${streamedResponse.statusCode}");
		streamedResponse.stream.listen((data) {
		  print("Received data:$data");
		});
	  });
	} catch(e) {
	  print("Caught $e");
	}
  }

  unsubscribe() {
	_client.close();
  }
}

On the server-side, I can see that the flutter app always subscribes okay as I can see the Sink being added. And the server-side always dispatches the events to the Sink but the flutter app only receives the events when running as a mobile app and NOT when running as a Chrome web app.

C:\Users\pondeMuse\flutter\bin\flutter.bat doctor --verbose
[√] Flutter (Channel master, v1.10.15-pre.188, on Microsoft Windows [Version 10.0.16299.1087], locale en-GB)
• Flutter version 1.10.15-pre.188 at C:\Users\pondeMuse\flutter
• Framework revision 584ee10 (2 days ago), 2019-10-21 07:49:28 -0700
• Engine revision 8aefcd8575
• Dart version 2.6.0 (build 2.6.0-dev.8.0 a61c775db8)

[√] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
• Android SDK at C:\Users\pondeMuse\AppData\Local\Android\sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-29, build-tools 29.0.2
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)
• All Android licenses accepted.

[√] Chrome - develop for the web
• Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[√] Android Studio (version 3.5)
• Android Studio at C:\Program Files\Android\Android Studio
• Flutter plugin version 40.2.2
• Dart plugin version 191.8593
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)

[!] IntelliJ IDEA Community Edition (version 2019.2)
• IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.2.5
X Flutter plugin not installed; this adds Flutter specific functionality.
• Dart plugin version 192.7402
• For information about installing plugins, see
https://flutter.dev/intellij-setup/#installing-the-plugins

[√] Connected device (2 available)
• Chrome • chrome • web-javascript • Google Chrome 77.0.3865.120
• Web Server • web-server • web-javascript • Flutter Tools

! Doctor found issues in 1 category.

@ponderMuse
Copy link
Author

I keep reading that there could be issues associated with CORS when running on a web browser but as far as I can see, there are no issues with CORS. CORS on the server-side has been configured to accept all origins (i.e. "*"), configured to accept "GET" and "OPTIONS" methods, and configured to accept all headers (i.e. "*").

Debugging in Chrome browser shows that the response back is a 200 and can see the following response headers:

Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: text/event-stream;charset=UTF-8
Expires: 0
Pragma: no-cache
transfer-encoding: chunked
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block

Unfortunately, I don't know enough about the http protocol so I don't know if any of the response headers above should be reason for concern as to why an event stream cannot be received on a web browser?

As a side note, the flutter/dart code was originally written as angulardart/dart code and that code was able to receive streamed events fine when running on Chrome web browser (Therefore assuing that CORS was configured correctly).

Any help or pointers on this issue would be greatly appreciated. Even a simple "Yes" receiving streamed events is possible in current version of flutter web on Chrome so you must be doing something wrong on the server side. Or "No" this does not work in current version of flutter web on Chrome but eventually it will/never will, etc.

@ponderMuse ponderMuse changed the title Unable to Receive SSE Events when runninf as a Flutter Web app on Chrome Web Unable to Receive SSE Events when running as a Flutter Web app on Chrome Web Oct 30, 2019
@sebastienharinck
Copy link

Hi @ponderMuse,

Did you find a solution ? I have succeeded on SSE with Flutter - Android. But I am stuck like you with the Web version.

All help is welcome !
Thanks

@ponderMuse
Copy link
Author

Hello @sebastienharinck ,

I've only just read your post. Please see the comments from my other post: Not Able to Receive SSE Events.

Basically, at the time of that post, receiving SSE events was possible in a Flutter Web app using package:sse

What package are you using?

@sebastienharinck
Copy link

Hello @ponderMuse,

Thanks for the link !

I tried package:sse and package:eventsource. I am able to build an app that works on mobile OR web browser. But never both with the same code..

Note : I am pretty new in flutter and dart so maybe I did something wrong. At that moment, are you able to build a flutter app that works for mobile and web browser ? If yes, which package do you use ? And do you still need a conditional imports as your previous post mentions ?

Thanks a lot

@jamiewest
Copy link
Contributor

I ended up writing my own to work on web and vm, package:sse_client. It by no means complete but it may provide a path to accomplish what your looking for.

@tomasdev
Copy link

I took a look at @jamiewest package, pretty simple implementation, perhaps it's worth having something alike within the http flutter package.

@hivesey
Copy link

hivesey commented Oct 20, 2020

@jamiewest @tomasdev Used package:sse_client but the client gets only the 1st server request not henceforth. Can you please share an example of the implementation? Thanks.

@paxcodes
Copy link

@jamiewest Yes, an example to use the package would be lovely. And what do you do in cases of having an httpOnly cookie? IIUC, The [BrowserClient] that received the httpOnly cookie should be the same client used to request for an SSE connection.

@jd-debug
Copy link

Any updates on this issue?

@katjas
Copy link

katjas commented Mar 3, 2022

Hi there,

I wonder if it is planned to solve this issue for future version?

We are facing the same problem to not receive streamed data one by one (in browser only), but can't really use the package sse - according to the documentation, a client using that package has to be paired with a server of this package, while we are connecting to a java based server.

Thanks a lot
Katja

@jamiewest
Copy link
Contributor

package:sse

Sorry I haven't done much with this, I used an implementation of this in my signalr_core package. Good luck, hopefully we can get some better options in this space.

@srix55
Copy link

srix55 commented Sep 26, 2022

I stumbled upon this. While http works fine on android & ios, on web, it gets the whole chunk of data only upon server close of stream... not when the event is sent by the server.

@lvxduck
Copy link

lvxduck commented May 26, 2023

Hi there,

The packages package:sse_client and package:sse only supports GET requests. As a workaround, I tried using HttpRequest (an XMLHttpRequest for Dart), and it works.

Here's an example of how I implemented code that uses POST requests.

Stream<String> connect(
  String path, {
  Map<String, dynamic>? headers,
  required String body,
}) {
  int progress = 0;
  final httpRequest = HttpRequest();
  final streamController = StreamController<String>();
  httpRequest.open('POST', 'your url');
  headers?.forEach((key, value) {
    httpRequest.setRequestHeader(key, value);
  });
  httpRequest.addEventListener('progress', (event) {
    final data = httpRequest.responseText!.substring(progress);
    progress += data.length;
    streamController.add(data);
  });
  httpRequest.addEventListener('loadend', (event) {
    httpRequest.abort();
    streamController.close();
  });
  httpRequest.addEventListener('error', (event) {
    streamController.addError(
      httpRequest.responseText ?? httpRequest.status ?? 'err',
    );
  });
  httpRequest.send(body);
  return streamController.stream;
}

@duypxd
Copy link

duypxd commented May 26, 2023

Hi there,

The packages package:sse_client and package:sse only supports GET requests. As a workaround, I tried using HttpRequest (an XMLHttpRequest for Dart), and it works.

Here's an example of how I implemented code that uses POST requests.

Stream<String> connect(
  String path, {
  Map<String, dynamic>? headers,
  required String body,
}) {
  int progress = 0;
  final httpRequest = HttpRequest();
  final streamController = StreamController<String>();
  httpRequest.open('POST', 'your url');
  headers?.forEach((key, value) {
    httpRequest.setRequestHeader(key, value);
  });
  httpRequest.addEventListener('progress', (event) {
    final data = httpRequest.responseText!.substring(progress);
    progress += data.length;
    streamController.add(data);
  });
  httpRequest.addEventListener('loadend', (event) {
    httpRequest.abort();
    streamController.close();
  });
  httpRequest.addEventListener('error', (event) {
    streamController.addError(
      httpRequest.responseText ?? httpRequest.status ?? 'err',
    );
  });
  httpRequest.send(body);
  return streamController.stream;
}

Thank you so much! Work well with me...donate you 1000$

@AmirKeshavarz
Copy link

Thank you so much @lvxduck
Your code is so useful for me at the moment.
But I want to run my program in both Android and web. So I found that I must prepare 2 codes for 2 different builds.
So it would be appreciated if someone can provide with a solution applicable in both platforms.

@mikemoore13
Copy link

other way i created a client that worked on the web for this pluggin
https://pub.dev/packages/eventsource

see here stevenroose/dart-eventsource#31

@Josecodesalot
Copy link

OpenAI uses SSE, its critical that this works for Flutter web an LLM Entrepreneurs.

For now, the best thing to do is to create the client in JS for Flutter Web.

@davidmigloz
Copy link

@Josecodesalot you can use package:fetch_client to support streaming on web for now (until BrowserClient is migrated to the Fetch API). That is how package:openai_dart client supports streaming in all the platforms.

@timobaehr
Copy link

@Josecodesalot you can use package:fetch_client to support streaming on web for now (until BrowserClient is migrated to the Fetch API). That is how package:openai_dart client supports streaming in all the platforms.

@davidmigloz suggested fetch_client at web until this issue is resolved. For those who have not already worked with conditional imports, here is the code:

sse_stream.dart: Get stream on Android/iOS/Mac/Windows:

import 'package:http/http.dart';

Future<ByteStream> getStream(Request request) async {
  final client = Client();
  StreamedResponse response = await client.send(request);
  return response.stream;
}

sse_stream_web.dart: Get stream on web:

import 'package:fetch_client/fetch_client.dart';
import 'package:http/http.dart';

Future<ByteStream> getStream(Request request) async {
  final FetchClient fetchClient = FetchClient(mode: RequestMode.cors);
  final FetchResponse response = await fetchClient.send(request);
  return response.stream;
}

Conditional import:

import 'path/to/sse_stream.dart' if (dart.library.js) 'path/to/sse_client_web.dart';

@marinosabijan
Copy link

God like! thanks

@Josecodesalot you can use package:fetch_client to support streaming on web for now (until BrowserClient is migrated to the Fetch API). That is how package:openai_dart client supports streaming in all the platforms.

@Josecodesalot you can use package:fetch_client to support streaming on web for now (until BrowserClient is migrated to the Fetch API). That is how package:openai_dart client supports streaming in all the platforms.

@davidmigloz suggested fetch_client at web until this issue is resolved. For those who have not already worked with conditional imports, here is the code:

sse_stream.dart: Get stream on Android/iOS/Mac/Windows:

import 'package:http/http.dart';

Future<ByteStream> getStream(Request request) async {
  final client = Client();
  StreamedResponse response = await client.send(request);
  return response.stream;
}

sse_stream_web.dart: Get stream on web:

import 'package:fetch_client/fetch_client.dart';
import 'package:http/http.dart';

Future<ByteStream> getStream(Request request) async {
  final FetchClient fetchClient = FetchClient(mode: RequestMode.cors);
  final FetchResponse response = await fetchClient.send(request);
  return response.stream;
}

Conditional import:

import 'path/to/sse_stream.dart' if (dart.library.js) 'path/to/sse_client_web.dart';

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests