sse_parse_util.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:typed_data';
  4. import 'package:connectivity_plus/connectivity_plus.dart';
  5. import 'package:flutter/cupertino.dart';
  6. class SSEParseUtil {
  7. static Stream<Message> parse(Stream<Uint8List> stream) {
  8. return stream.transform(SSETransformer());
  9. }
  10. }
  11. class Message {
  12. final String id;
  13. final String event;
  14. final String data;
  15. final int? retry;
  16. Message(
  17. {required this.id,
  18. required this.event,
  19. required this.data,
  20. required this.retry});
  21. @override
  22. String toString() {
  23. return 'Message{id: $id, event: $event, data: $data, retry: $retry}';
  24. }
  25. }
  26. class SSETransformer extends StreamTransformerBase<Uint8List, Message> {
  27. @override
  28. Stream<Message> bind(Stream<Uint8List> stream) {
  29. return Stream.eventTransformed(
  30. stream.map((uint8List) => List<int>.from(uint8List)),
  31. (sink) => SSESink(sink),
  32. );
  33. }
  34. }
  35. class SSESink implements EventSink<List<int>> {
  36. static final _eventSeparator = utf8.encode("\n\n");
  37. static const _fieldSeparator = "\n";
  38. static const _dataPrefix = "data:";
  39. static const _dataPrefixR = "data: ";
  40. static const _idPrefix = "id:";
  41. static const _idPrefixR = "id: ";
  42. static const _eventPrefix = "event:";
  43. static const _eventPrefixR = "event: ";
  44. static const _retryPrefix = "retry:";
  45. static const _retryPrefixR = "retry: ";
  46. static const _commentPrefix = ":";
  47. static const _commentPrefixR = ": ";
  48. final EventSink<Message> _eventSink;
  49. final List<int> _buffer = [];
  50. SSESink(this._eventSink);
  51. @override
  52. void add(List<int> event) {
  53. _buffer.addAll(event);
  54. final endIndex = _indexOf(_buffer, _eventSeparator);
  55. if (endIndex == -1) {
  56. return;
  57. }
  58. final completedEvent = _buffer.sublist(0, endIndex);
  59. _buffer.removeRange(0, endIndex + _eventSeparator.length);
  60. parseEvent(completedEvent);
  61. }
  62. @override
  63. void addError(Object error, [StackTrace? stackTrace]) {
  64. _eventSink.addError(error, stackTrace);
  65. }
  66. @override
  67. void close() {
  68. _eventSink.close();
  69. }
  70. int _indexOf(List<int> origin, List<int> target) {
  71. for (var i = 0; i < origin.length - target.length; i++) {
  72. var found = true;
  73. for (var j = 0; j < target.length; j++) {
  74. if (origin[i + j] != target[j]) {
  75. found = false;
  76. break;
  77. }
  78. }
  79. if (found) {
  80. return i;
  81. }
  82. }
  83. return -1;
  84. }
  85. void parseEvent(List<int> completedEvent) {
  86. final eventString = utf8.decode(completedEvent);
  87. final fields = eventString.split(_fieldSeparator);
  88. String? id;
  89. String? event;
  90. String data = "";
  91. int? retry;
  92. for (final field in fields) {
  93. final trimmedField = field.trim();
  94. if (trimmedField.isEmpty) {
  95. continue;
  96. }
  97. if (trimmedField.startsWith(_commentPrefix) ||
  98. trimmedField.startsWith(_commentPrefixR)) {
  99. continue;
  100. }
  101. if (trimmedField.startsWith(_retryPrefixR)) {
  102. retry = int.tryParse(trimmedField.substring(_retryPrefixR.length));
  103. } else if (trimmedField.startsWith(_dataPrefixR)) {
  104. data += trimmedField.substring(_dataPrefixR.length);
  105. } else if (trimmedField.startsWith(_eventPrefixR)) {
  106. event = trimmedField.substring(_eventPrefixR.length);
  107. } else if (trimmedField.startsWith(_idPrefixR)) {
  108. id = trimmedField.substring(_idPrefixR.length);
  109. } else if (trimmedField.startsWith(_idPrefix)) {
  110. id = trimmedField.substring(_idPrefix.length);
  111. } else if (trimmedField.startsWith(_eventPrefix)) {
  112. event = trimmedField.substring(_eventPrefix.length);
  113. } else if (trimmedField.startsWith(_dataPrefix)) {
  114. data += trimmedField.substring(_dataPrefix.length);
  115. } else if (trimmedField.startsWith(_retryPrefix)) {
  116. retry = int.tryParse(trimmedField.substring(_retryPrefix.length));
  117. }
  118. }
  119. _eventSink.add(Message(
  120. id: id ?? "", event: event ?? "", data: data, retry: retry));
  121. }
  122. }