diff --git a/.gitignore b/.gitignore index 3c8a157..78f1ae6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,7 @@ .dart_tool/ .packages +.tmp/ + # Conventional directory for build output. build/ diff --git a/02e74f10e0327ad868d138f2b4fdd6f0 b/02e74f10e0327ad868d138f2b4fdd6f0 new file mode 100644 index 0000000..a5c750f --- /dev/null +++ b/02e74f10e0327ad868d138f2b4fdd6f0 @@ -0,0 +1 @@ +27 \ No newline at end of file diff --git a/0604473ff30c614d2045ebee1b9e110e b/0604473ff30c614d2045ebee1b9e110e new file mode 100644 index 0000000..e762e26 Binary files /dev/null and b/0604473ff30c614d2045ebee1b9e110e differ diff --git a/bin/tcp_server.dart b/bin/tcp_server.dart index c5f9c2f..85770f7 100644 --- a/bin/tcp_server.dart +++ b/bin/tcp_server.dart @@ -1,155 +1,160 @@ /* * @Author : Linloir * @Date : 2022-10-06 15:44:16 - * @LastEditTime : 2022-10-08 23:57:37 + * @LastEditTime : 2022-10-09 18:00:40 * @Description : */ -import 'dart:convert'; import 'dart:io'; import 'package:tcp_server/database.dart'; import 'package:tcp_server/requesthandler.dart'; import 'package:tcp_server/tcpcontroller/controller.dart'; +import 'package:tcp_server/tcpcontroller/payload/message.dart'; import 'package:tcp_server/tcpcontroller/request.dart'; import 'package:tcp_server/tcpcontroller/response.dart'; void main(List arguments) async { + //Create tmp folder + await Directory('${Directory.current.path}/.tmp').create(); await DataBaseHelper().initialize(); - var tokenMap = {}; - var socketMap = >{}; + var tokenMap = {}; + var controllerMap = >{}; var listenSocket = await ServerSocket.bind('127.0.0.1', 20706); listenSocket.listen( (socket) { var controller = TCPController(socket: socket); - controller.stream.listen((request) async { + controller.inStream.listen((request) async { + print('[L] ${request.toJSON}'); if(request.tokenID == null) { - if(socketMap[socket] == null) { - socketMap[socket] = (() async => (await DataBaseHelper().createToken()))(); + if(controllerMap[controller] == null) { + controllerMap[controller] = (() async => (await DataBaseHelper().createToken()))(); } - request.tokenID = await socketMap[socket]; + request.tokenID = await controllerMap[controller]; var tokenResponse = TCPResponse( - type: RequestType.token, + type: ResponseType.token, status: ResponseStatus.ok, body: { "tokenid": request.tokenID } ); - await socket.addStream(tokenResponse.stream); + controller.outStream.add(tokenResponse); } - tokenMap[request.tokenID!] = tokenMap[request.tokenID!] ?? socket; + tokenMap[request.tokenID!] = tokenMap[request.tokenID!] ?? controller; switch(request.requestType) { case RequestType.checkState: { var response = await onCheckState(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.register: { var response = await onRegister(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.login: { var response = await onLogin(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.logout: { var response = await onLogout(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.profile: { var response = await onFetchProfile(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.modifyProfile: { var response = await onModifyProfile(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.modifyPassword: { var response = await onModifyPassword(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.sendMessage: { //Forword Message - var message = request.body['message'] as Map; + var message = Message.fromJSONObject(request.body); await DataBaseHelper().setFetchHistoryFor( tokenID: request.tokenID, - newTimeStamp: message['timestamp'] as int + newTimeStamp: message.timestamp ); - var originUserID = message['userid'] as int; + var originUserID = message.senderID; var onlineDevices = await DataBaseHelper().fetchTokenIDsViaUserID(userID: originUserID); for(var device in onlineDevices) { if(device == request.tokenID) { continue; } - var targetSocket = tokenMap[device]; - targetSocket?.write(jsonEncode({ - 'response': 'FORWARDMSG', - 'body': { - "message": message - } - })); + var targetController = tokenMap[device]; + var forwardResponse = TCPResponse( + type: ResponseType.forwardMessage, + status: ResponseStatus.ok, + body: message.jsonObject + ); + targetController?.outStream.add(forwardResponse); //Update Fetch Histories await DataBaseHelper().setFetchHistoryFor( tokenID: device, - newTimeStamp: message['timestamp'] as int + newTimeStamp: message.timestamp ); } - var targetUserID = message['targetid'] as int; + var targetUserID = message.receiverID; var targetDevices = await DataBaseHelper().fetchTokenIDsViaUserID(userID: targetUserID); for(var device in targetDevices) { //Forward to socket - var targetSocket = tokenMap[device]; - targetSocket?.write(jsonEncode({ - 'response': 'FORWARDMSG', - 'body': { - "message": message - } - })); + var targetController = tokenMap[device]; + var forwardResponse = TCPResponse( + type: ResponseType.forwardMessage, + status: ResponseStatus.ok, + body: message.jsonObject + ); + targetController?.outStream.add(forwardResponse); //Update Fetch Histories await DataBaseHelper().setFetchHistoryFor( tokenID: device, - newTimeStamp: message['timestamp'] as int + newTimeStamp: message.timestamp ); } var response = await onSendMessage(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.fetchMessage: { var response = await onFetchMessage(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.fetchFile: { var response = await onFetchFile(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.searchUser: { var response = await onSearchUser(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.fetchContact: { var response = await onFetchContact(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } case RequestType.unknown: { var response = await onUnknownRequest(request, socket); - await socket.addStream(response.stream); + controller.outStream.add(response); break; } default: { print('[E] Drop out of switch case'); } } + //Clear temp file + request.payload?.delete(); }); }, ); diff --git a/lib/database.dart b/lib/database.dart index 72c21ae..721d846 100644 --- a/lib/database.dart +++ b/lib/database.dart @@ -1,7 +1,7 @@ /* * @Author : Linloir * @Date : 2022-10-06 16:15:01 - * @LastEditTime : 2022-10-08 23:54:36 + * @LastEditTime : 2022-10-09 17:59:13 * @Description : */ @@ -467,7 +467,8 @@ class DataBaseHelper { { 'msgmd5': msg.md5encoded, 'filemd5': fileMd5 - } + }, + conflictAlgorithm: ConflictAlgorithm.replace ); } } diff --git a/lib/requesthandler.dart b/lib/requesthandler.dart index 2774f9c..9f1587b 100644 --- a/lib/requesthandler.dart +++ b/lib/requesthandler.dart @@ -1,7 +1,7 @@ /* * @Author : Linloir * @Date : 2022-10-08 20:52:48 - * @LastEditTime : 2022-10-08 23:39:59 + * @LastEditTime : 2022-10-09 13:39:02 * @Description : */ @@ -18,13 +18,13 @@ Future onCheckState(TCPRequest request, Socket socket) async { try { var userInfo = await DataBaseHelper().checkLoginState(tokenID: request.tokenID); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: userInfo.jsonObject ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -39,13 +39,13 @@ Future onRegister(TCPRequest request, Socket socket) async { tokenID: request.tokenID ); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: newUserInfo.jsonObject ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -59,13 +59,13 @@ Future onLogin(TCPRequest request, Socket socket) async { tokenID: request.tokenID ); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: userInfo.jsonObject ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -76,12 +76,12 @@ Future onLogout(TCPRequest request, Socket socket) async { try { await DataBaseHelper().logOut(tokenID: request.tokenID); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -92,13 +92,13 @@ Future onFetchProfile(TCPRequest request, Socket socket) async { try { var userInfo = await DataBaseHelper().fetchUserInfoViaToken(tokenID: request.tokenID); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: userInfo.jsonObject ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -112,12 +112,12 @@ Future onModifyPassword(TCPRequest request, Socket socket) async { tokenID: request.tokenID ); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -131,13 +131,13 @@ Future onModifyProfile(TCPRequest request, Socket socket) async { tokenID: request.tokenID ); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: newUserInfo.jsonObject ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -153,17 +153,18 @@ Future onSendMessage(TCPRequest request, Socket socket) async { fileMd5: message.fileMd5 ); } - await DataBaseHelper().storeMessage( - msg: message, - fileMd5: message.fileMd5 - ); + //Store message + await DataBaseHelper().storeMessage( + msg: message, + fileMd5: message.fileMd5 + ); return TCPResponse( - type: RequestType.sendMessage, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -174,15 +175,15 @@ Future onFetchMessage(TCPRequest request, Socket socket) async { try { var messages = await DataBaseHelper().fetchMessagesFor(tokenID: request.tokenID); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: { - 'messages': messages.map((e) => e.jsonObject) + 'messages': messages.map((e) => e.jsonObject).toList() } ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -194,13 +195,13 @@ Future onFetchFile(TCPRequest request, Socket socket) async { var filePath = await DataBaseHelper().fetchFilePath(msgMd5: request.body['msgmd5'] as String); var file = File(filePath); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, payload: file ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -211,13 +212,13 @@ Future onSearchUser(TCPRequest request, Socket socket) async { try { var userInfo = await DataBaseHelper().fetchUserInfoViaUsername(username: request.body['username'] as String); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: userInfo.jsonObject ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -228,12 +229,12 @@ Future onAddContact(TCPRequest request, Socket socket) async { try { await DataBaseHelper().addContact(tokenID: request.tokenID, userID: request.body['userid'] as int); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -246,7 +247,7 @@ Future onFetchContact(TCPRequest request, Socket socket) async { var pendingContacts = await DataBaseHelper().fetchPendingContacts(tokenID: request.tokenID); var requestingContacts = await DataBaseHelper().fetchRequestingContacts(tokenID: request.tokenID); return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.ok, body: { "contacts": contacts.map((e) => e.jsonObject), @@ -256,7 +257,7 @@ Future onFetchContact(TCPRequest request, Socket socket) async { ); } on Exception catch (exception) { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: exception.toString() ); @@ -265,7 +266,7 @@ Future onFetchContact(TCPRequest request, Socket socket) async { Future onUnknownRequest(TCPRequest request, Socket socket) async { return TCPResponse( - type: request.requestType, + type: ResponseType.fromRequestType(request.requestType), status: ResponseStatus.err, errInfo: 'Unkown request' ); diff --git a/lib/tcpcontroller/controller.dart b/lib/tcpcontroller/controller.dart index 642666d..5037e2a 100644 --- a/lib/tcpcontroller/controller.dart +++ b/lib/tcpcontroller/controller.dart @@ -1,7 +1,7 @@ /* * @Author : Linloir * @Date : 2022-10-08 15:10:04 - * @LastEditTime : 2022-10-08 23:11:24 + * @LastEditTime : 2022-10-09 17:55:26 * @Description : */ @@ -10,12 +10,15 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:tcp_server/tcpcontroller/request.dart'; +import 'package:tcp_server/tcpcontroller/response.dart'; class TCPController { final Socket socket; //Stores the incoming bytes of the TCP connection temporarily - final Uint8List buffer = Uint8List(0); + final List buffer = []; + //Stores the fetched require temporarily + List _requestBytes = []; //Byte length for json object int requestLength = 0; @@ -23,19 +26,26 @@ class TCPController { int payloadLength = 0; //Construct a stream which emits events on intact requests - StreamController _requestStreamController = StreamController()..close(); + StreamController> _requestStreamController = StreamController()..close(); //Construct a payload stream which forward the incoming byte into temp file - StreamController _payloadStreamController = StreamController()..close(); + StreamController> _payloadStreamController = StreamController()..close(); //Provide a request stream for caller functions to listen on - final StreamController _streamController = StreamController(); - Stream get stream => _streamController.stream; + final StreamController _inStreamController = StreamController(); + Stream get inStream => _inStreamController.stream; + + //Provide a post stream for caller functions to push to + final StreamController _outStreamController = StreamController(); + StreamSink get outStream => _outStreamController.sink; TCPController({ required this.socket }) { socket.listen(socketHandler); + _outStreamController.stream.listen((response) async { + await socket.addStream(response.stream); + }); } //Listen to the incoming stream and emits event whenever there is a intact request @@ -44,30 +54,35 @@ class TCPController { buffer.addAll(fetchedData); //Consume buffer until it's not enough for first 8 byte of a message while(true) { - if(requestLength == 0 && payloadLength == 0) { + if(requestLength == 0 && payloadLength == 0 && _payloadStreamController.isClosed) { //New request - if(buffer.length > 8) { + if(buffer.length >= 8) { //Buffered data has more than 8 bytes, enough to read request length and body length - requestLength = buffer.sublist(0, 4).buffer.asByteData().getInt32(0); - payloadLength = buffer.sublist(4, 8).buffer.asByteData().getInt32(0); + requestLength = Uint8List.fromList(buffer.sublist(0, 4)).buffer.asInt32List()[0]; + payloadLength = Uint8List.fromList(buffer.sublist(4, 8)).buffer.asInt32List()[0]; //Clear the length indicator bytes buffer.removeRange(0, 8); //Create temp file to read payload (might be huge) - var tempFile = File('./temp${DateTime.now().microsecondsSinceEpoch}.temp')..createSync(); + var tempFile = File('${Directory.current.path}/.tmp/${DateTime.now().microsecondsSinceEpoch}')..createSync(); //Initialize payload transmission controller _payloadStreamController = StreamController(); //Bind file to stream - _payloadStreamController.stream.listen((data) { - tempFile.writeAsBytes(data, mode: FileMode.append); - }); + _payloadStreamController.stream.listen( + (data) { + tempFile.writeAsBytesSync(data, mode: FileMode.append, flush: true); + }, + onDone: () { + //Payload definetely ends after request is buffered + //Therefore transmit the request and payload to stream here + _inStreamController.add(TCPRequest(_requestBytes, tempFile)); + } + ); //Bind request construction on stream _requestStreamController = StreamController(); _requestStreamController.stream.listen((requestBytes) { //When request stream is closed by controller - var request = TCPRequest(requestBytes, tempFile); - _payloadStreamController.done.then((_) { - _streamController.add(request); - }); + //Request is intact, save to _request temporarily + _requestBytes = requestBytes; }); } else { @@ -80,7 +95,7 @@ class TCPController { //Currently awaiting full transmission if(requestLength > 0) { //Currently processing on a request - if(buffer.length > requestLength) { + if(buffer.length >= requestLength) { //Got intact request json //Emit request buffer through stream _requestStreamController.add(buffer.sublist(0, requestLength)); @@ -101,7 +116,7 @@ class TCPController { if(buffer.length >= payloadLength) { //Last few bytes to emit //Send the last few bytes to stream - _payloadStreamController.add(buffer.sublist(0, payloadLength)); + _payloadStreamController.add(Uint8List.fromList(buffer.sublist(0, payloadLength))); //Clear buffer buffer.removeRange(0, payloadLength); //Set payload length to zero @@ -112,11 +127,13 @@ class TCPController { else { //Part of payload //Transmit all to stream - _payloadStreamController.add(buffer); + _payloadStreamController.add(Uint8List.fromList(buffer)); //Reduce payload bytes left payloadLength -= buffer.length; //Clear buffer buffer.clear(); + //Exit and wait for another submit + break; } } } diff --git a/lib/tcpcontroller/payload/message.dart b/lib/tcpcontroller/payload/message.dart index c226f8d..c365e13 100644 --- a/lib/tcpcontroller/payload/message.dart +++ b/lib/tcpcontroller/payload/message.dart @@ -1,7 +1,7 @@ /* * @Author : Linloir * @Date : 2022-10-08 16:16:19 - * @LastEditTime : 2022-10-08 23:20:36 + * @LastEditTime : 2022-10-09 14:53:11 * @Description : Message Info Payload */ @@ -43,7 +43,7 @@ class Message { ..addAll(intToUint8List(targetid)) ..addAll(intToUint8List(timestamp)) ..addAll(content.codeUnits) - ), + ).toString(), "filemd5": filemd5 }; Message.fromJSONObject(Map data): _data = data; diff --git a/lib/tcpcontroller/request.dart b/lib/tcpcontroller/request.dart index e87ffa1..c9c449e 100644 --- a/lib/tcpcontroller/request.dart +++ b/lib/tcpcontroller/request.dart @@ -1,7 +1,7 @@ /* * @Author : Linloir * @Date : 2022-10-08 15:14:26 - * @LastEditTime : 2022-10-08 23:52:50 + * @LastEditTime : 2022-10-09 17:36:42 * @Description : */ import 'dart:convert'; @@ -9,7 +9,6 @@ import 'dart:io'; import 'dart:typed_data'; enum RequestType { - token ('TOKEN'), //Only exists when server is sending message checkState ('STATE'), //Check login state for device token register ('REGISTER'), //Register new user login ('LOGIN'), //Login via username and password @@ -41,7 +40,8 @@ class TCPRequest { final Map _data; File? payload; - TCPRequest(Uint8List data, this.payload): _data = jsonDecode(String.fromCharCodes(data)); + TCPRequest(List data, this.payload): _data = jsonDecode(String.fromCharCodes(data)); + TCPRequest.none(): _data = {}; String get toJSON => jsonEncode(_data); RequestType get requestType => RequestType.fromValue(_data['request'] as String); diff --git a/lib/tcpcontroller/response.dart b/lib/tcpcontroller/response.dart index aac21cd..171d7b0 100644 --- a/lib/tcpcontroller/response.dart +++ b/lib/tcpcontroller/response.dart @@ -1,7 +1,7 @@ /* * @Author : Linloir * @Date : 2022-10-08 22:40:47 - * @LastEditTime : 2022-10-08 23:05:01 + * @LastEditTime : 2022-10-09 16:39:02 * @Description : */ @@ -11,6 +11,38 @@ import 'dart:typed_data'; import 'package:tcp_server/tcpcontroller/request.dart'; +enum ResponseType { + token ('TOKEN'), //Only exists when server is sending message + checkState ('STATE'), //Check login state for device token + register ('REGISTER'), //Register new user + login ('LOGIN'), //Login via username and password + logout ('LOGOUT'), //Logout for current device token + profile ('PROFILE'), //Fetch current logged in user profile + modifyPassword('MODIFYPASSWD'), //Modify user password + modifyProfile ('MODIFYPROFILE'), //Modify user profile + sendMessage ('SENDMSG'), //Send message + forwardMessage('FORWARDMSG'), //Forward message + fetchMessage ('FETCHMSG'), //Fetch message + findFile ('FINDFILE'), //Find file by md5 before transmitting the file + fetchFile ('FETCHFILE'), //Fetch file and file md5 by message md5 + searchUser ('SEARCHUSR'), //Search username and userid by username + addContact ('ADDCONTACT'), //Add one-way relation to a user + fetchContact ('FETCHCONTACT'), //Fetch all contacts, including requesting and pending + unknown ('UNKNOWN'); //Wrong command + + const ResponseType(String value): _value = value; + final String _value; + String get value => _value; + + //Construct the enum type by value + factory ResponseType.fromValue(String value) { + return ResponseType.values.firstWhere((element) => element._value == value, orElse: () => ResponseType.unknown); + } + factory ResponseType.fromRequestType(RequestType type) { + return ResponseType.values.firstWhere((element) => element._value == type.value, orElse: () => ResponseType.unknown); + } +} + enum ResponseStatus { ok('OK'), err('ERR'); @@ -26,7 +58,7 @@ class TCPResponse { final File? payloadFile; TCPResponse({ - required RequestType type, + required ResponseType type, required ResponseStatus status, Map? body, String? errInfo, @@ -42,7 +74,7 @@ class TCPResponse { int get responseLength => responseJson.length; int get payloadLength => payloadFile?.lengthSync() ?? 0; - Stream get stream async* { + Stream> get stream async* { yield Uint8List(4)..buffer.asInt32List()[0] = responseLength; yield Uint8List(4)..buffer.asInt32List()[0] = payloadLength; yield Uint8List.fromList(responseJson.codeUnits);