|
@@ -2,6 +2,7 @@ import 'dart:async';
|
|
|
import 'dart:convert';
|
|
import 'dart:convert';
|
|
|
import 'dart:typed_data';
|
|
import 'dart:typed_data';
|
|
|
|
|
|
|
|
|
|
+import 'package:connectivity_plus/connectivity_plus.dart';
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
|
|
|
|
class SSEParseUtil {
|
|
class SSEParseUtil {
|
|
@@ -32,21 +33,14 @@ class SSETransformer extends StreamTransformerBase<Uint8List, Message> {
|
|
|
@override
|
|
@override
|
|
|
Stream<Message> bind(Stream<Uint8List> stream) {
|
|
Stream<Message> bind(Stream<Uint8List> stream) {
|
|
|
return Stream.eventTransformed(
|
|
return Stream.eventTransformed(
|
|
|
- stream.map((bytes) {
|
|
|
|
|
- try {
|
|
|
|
|
- return utf8.decoder.convert(bytes);
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- debugPrint("$e: $bytes");
|
|
|
|
|
- }
|
|
|
|
|
- return "";
|
|
|
|
|
- }),
|
|
|
|
|
- (sink) => SseEventSink(sink),
|
|
|
|
|
|
|
+ stream.map((uint8List) => List<int>.from(uint8List)),
|
|
|
|
|
+ (sink) => SSESink(sink),
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-class SseEventSink implements EventSink<String> {
|
|
|
|
|
- static const _eventSeparator = "\n\n";
|
|
|
|
|
|
|
+class SSESink implements EventSink<List<int>> {
|
|
|
|
|
+ static final _eventSeparator = utf8.encode("\n\n");
|
|
|
static const _fieldSeparator = "\n";
|
|
static const _fieldSeparator = "\n";
|
|
|
static const _dataPrefix = "data:";
|
|
static const _dataPrefix = "data:";
|
|
|
static const _dataPrefixR = "data: ";
|
|
static const _dataPrefixR = "data: ";
|
|
@@ -61,25 +55,23 @@ class SseEventSink implements EventSink<String> {
|
|
|
|
|
|
|
|
final EventSink<Message> _eventSink;
|
|
final EventSink<Message> _eventSink;
|
|
|
|
|
|
|
|
- String? _id;
|
|
|
|
|
- String? _event;
|
|
|
|
|
- String _data = "";
|
|
|
|
|
- int? _retry;
|
|
|
|
|
-
|
|
|
|
|
- String buffer = "";
|
|
|
|
|
- String completedEvent = "";
|
|
|
|
|
|
|
+ final List<int> _buffer = [];
|
|
|
|
|
|
|
|
- SseEventSink(this._eventSink);
|
|
|
|
|
|
|
+ SSESink(this._eventSink);
|
|
|
|
|
|
|
|
@override
|
|
@override
|
|
|
- void add(String event) {
|
|
|
|
|
- buffer += event;
|
|
|
|
|
|
|
+ void add(List<int> event) {
|
|
|
|
|
+ _buffer.addAll(event);
|
|
|
|
|
|
|
|
- if (buffer.endsWith("\n\n")) {
|
|
|
|
|
- completedEvent = buffer;
|
|
|
|
|
- buffer = "";
|
|
|
|
|
- parseEvent();
|
|
|
|
|
|
|
+ final endIndex = _indexOf(_buffer, _eventSeparator);
|
|
|
|
|
+ if (endIndex == -1) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ final completedEvent = _buffer.sublist(0, endIndex);
|
|
|
|
|
+ _buffer.removeRange(0, endIndex + _eventSeparator.length);
|
|
|
|
|
+
|
|
|
|
|
+ parseEvent(completedEvent);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
@override
|
|
@@ -92,38 +84,62 @@ class SseEventSink implements EventSink<String> {
|
|
|
_eventSink.close();
|
|
_eventSink.close();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- void parseEvent() {
|
|
|
|
|
- completedEvent = completedEvent.substring(0, completedEvent.length - 2);
|
|
|
|
|
- completedEvent.split("\n").forEach((element) {
|
|
|
|
|
- element = element.trim();
|
|
|
|
|
- if (element.isEmpty) return;
|
|
|
|
|
- if (element.startsWith(_commentPrefix) ||
|
|
|
|
|
- element.startsWith(_commentPrefixR)) {
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ int _indexOf(List<int> origin, List<int> target) {
|
|
|
|
|
+ for (var i = 0; i < origin.length - target.length; i++) {
|
|
|
|
|
+ var found = true;
|
|
|
|
|
+ for (var j = 0; j < target.length; j++) {
|
|
|
|
|
+ if (origin[i + j] != target[j]) {
|
|
|
|
|
+ found = false;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (found) {
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void parseEvent(List<int> completedEvent) {
|
|
|
|
|
+ final eventString = utf8.decode(completedEvent);
|
|
|
|
|
+ final fields = eventString.split(_fieldSeparator);
|
|
|
|
|
+
|
|
|
|
|
+ String? id;
|
|
|
|
|
+ String? event;
|
|
|
|
|
+ String data = "";
|
|
|
|
|
+ int? retry;
|
|
|
|
|
+
|
|
|
|
|
+ for (final field in fields) {
|
|
|
|
|
+ final trimmedField = field.trim();
|
|
|
|
|
+ if (trimmedField.isEmpty) {
|
|
|
|
|
+ continue;
|
|
|
}
|
|
}
|
|
|
- if (element.startsWith(_retryPrefixR)) {
|
|
|
|
|
- _retry = int.tryParse(element.substring(_retryPrefixR.length));
|
|
|
|
|
- } else if (element.startsWith(_dataPrefixR)) {
|
|
|
|
|
- _data += element.substring(_dataPrefixR.length);
|
|
|
|
|
- } else if (element.startsWith(_eventPrefixR)) {
|
|
|
|
|
- _event = element.substring(_eventPrefixR.length);
|
|
|
|
|
- } else if (element.startsWith(_idPrefixR)) {
|
|
|
|
|
- _id = element.substring(_idPrefixR.length);
|
|
|
|
|
- } else if (element.startsWith(_idPrefix)) {
|
|
|
|
|
- _id = element.substring(_idPrefix.length);
|
|
|
|
|
- } else if (element.startsWith(_eventPrefix)) {
|
|
|
|
|
- _event = element.substring(_eventPrefix.length);
|
|
|
|
|
- } else if (element.startsWith(_dataPrefix)) {
|
|
|
|
|
- _data += element.substring(_dataPrefix.length);
|
|
|
|
|
- } else if (element.startsWith(_retryPrefix)) {
|
|
|
|
|
- _retry = int.tryParse(element.substring(_retryPrefix.length));
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (trimmedField.startsWith(_commentPrefix) ||
|
|
|
|
|
+ trimmedField.startsWith(_commentPrefixR)) {
|
|
|
|
|
+ continue;
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (trimmedField.startsWith(_retryPrefixR)) {
|
|
|
|
|
+ retry = int.tryParse(trimmedField.substring(_retryPrefixR.length));
|
|
|
|
|
+ } else if (trimmedField.startsWith(_dataPrefixR)) {
|
|
|
|
|
+ data += trimmedField.substring(_dataPrefixR.length);
|
|
|
|
|
+ } else if (trimmedField.startsWith(_eventPrefixR)) {
|
|
|
|
|
+ event = trimmedField.substring(_eventPrefixR.length);
|
|
|
|
|
+ } else if (trimmedField.startsWith(_idPrefixR)) {
|
|
|
|
|
+ id = trimmedField.substring(_idPrefixR.length);
|
|
|
|
|
+ } else if (trimmedField.startsWith(_idPrefix)) {
|
|
|
|
|
+ id = trimmedField.substring(_idPrefix.length);
|
|
|
|
|
+ } else if (trimmedField.startsWith(_eventPrefix)) {
|
|
|
|
|
+ event = trimmedField.substring(_eventPrefix.length);
|
|
|
|
|
+ } else if (trimmedField.startsWith(_dataPrefix)) {
|
|
|
|
|
+ data += trimmedField.substring(_dataPrefix.length);
|
|
|
|
|
+ } else if (trimmedField.startsWith(_retryPrefix)) {
|
|
|
|
|
+ retry = int.tryParse(trimmedField.substring(_retryPrefix.length));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
_eventSink.add(Message(
|
|
_eventSink.add(Message(
|
|
|
- id: _id ?? "", event: _event ?? "", data: _data, retry: _retry));
|
|
|
|
|
- _id = null;
|
|
|
|
|
- _event = null;
|
|
|
|
|
- _data = "";
|
|
|
|
|
- _retry = null;
|
|
|
|
|
|
|
+ id: id ?? "", event: event ?? "", data: data, retry: retry));
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
+}
|