mirror of
https://github.com/Linloir/Simple-TCP-Server.git
synced 2025-12-16 23:48:11 +08:00
Bug Fix and API adjustment
- Check token validity - Prevent unlogged user to fetch contact - Fix missing 'tolist' conversion in contact fetch method - (IMPORTANT) Fix async bug in TCP controller (reconstruct the handler)
This commit is contained in:
parent
834e810663
commit
52c32c2e74
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-06 15:44:16
|
* @Date : 2022-10-06 15:44:16
|
||||||
* @LastEditTime : 2022-10-12 18:23:31
|
* @LastEditTime : 2022-10-14 10:26:00
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -30,9 +30,9 @@ void main(List<String> arguments) async {
|
|||||||
listenSocket.listen(
|
listenSocket.listen(
|
||||||
(socket) {
|
(socket) {
|
||||||
var controller = TCPController(socket: socket);
|
var controller = TCPController(socket: socket);
|
||||||
controller.inStream.listen((request) async {
|
controller.requestStreamBroadcast.listen((request) async {
|
||||||
print('[L] ${request.toJSON}');
|
print('[L] ${request.toJSON}');
|
||||||
if(request.tokenID == null) {
|
if(!(await DataBaseHelper().isTokenValid(tokenid: request.tokenID))) {
|
||||||
if(controllerMap[controller] == null) {
|
if(controllerMap[controller] == null) {
|
||||||
controllerMap[controller] = (() async => (await DataBaseHelper().createToken()))();
|
controllerMap[controller] = (() async => (await DataBaseHelper().createToken()))();
|
||||||
}
|
}
|
||||||
@ -46,7 +46,6 @@ void main(List<String> arguments) async {
|
|||||||
);
|
);
|
||||||
controller.outStream.add(tokenResponse);
|
controller.outStream.add(tokenResponse);
|
||||||
}
|
}
|
||||||
//TODO: check if token id is not in tokenid list
|
|
||||||
tokenMap[request.tokenID!] = tokenMap[request.tokenID!] ?? controller;
|
tokenMap[request.tokenID!] = tokenMap[request.tokenID!] ?? controller;
|
||||||
switch(request.requestType) {
|
switch(request.requestType) {
|
||||||
case RequestType.checkState: {
|
case RequestType.checkState: {
|
||||||
@ -151,6 +150,11 @@ void main(List<String> arguments) async {
|
|||||||
controller.outStream.add(response);
|
controller.outStream.add(response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case RequestType.addContact: {
|
||||||
|
var response = await onAddContact(request, socket);
|
||||||
|
controller.outStream.add(response);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case RequestType.fetchContact: {
|
case RequestType.fetchContact: {
|
||||||
var response = await onFetchContact(request, socket);
|
var response = await onFetchContact(request, socket);
|
||||||
controller.outStream.add(response);
|
controller.outStream.add(response);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-06 16:15:01
|
* @Date : 2022-10-06 16:15:01
|
||||||
* @LastEditTime : 2022-10-13 20:36:34
|
* @LastEditTime : 2022-10-14 12:13:23
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -83,6 +83,24 @@ class DataBaseHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isTokenValid({
|
||||||
|
required int? tokenid,
|
||||||
|
}) async {
|
||||||
|
if(tokenid == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenQueryResult = await _database.query(
|
||||||
|
'tokens',
|
||||||
|
where: 'tokenid = ?',
|
||||||
|
whereArgs: [
|
||||||
|
tokenid
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return tokenQueryResult.isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
//Creates new token
|
//Creates new token
|
||||||
Future<int> createToken() async {
|
Future<int> createToken() async {
|
||||||
//Insert new row
|
//Insert new row
|
||||||
@ -628,13 +646,19 @@ class DataBaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Find current binded userID
|
//Find current binded userID
|
||||||
var currentUserID = (await _database.query(
|
var currentUserIDQueryResult = (await _database.query(
|
||||||
'bindings',
|
'bindings',
|
||||||
where: 'tokenid = ?',
|
where: 'tokenid = ?',
|
||||||
whereArgs: [
|
whereArgs: [
|
||||||
tokenID
|
tokenID
|
||||||
]
|
]
|
||||||
))[0]['userid'] as int;
|
));
|
||||||
|
|
||||||
|
if(currentUserIDQueryResult.isEmpty) {
|
||||||
|
throw Exception('User not logged in');
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentUserID = currentUserIDQueryResult[0]['userid'] as int;
|
||||||
|
|
||||||
//Fetch all contacts
|
//Fetch all contacts
|
||||||
var contactsQueryResult = await _database.query(
|
var contactsQueryResult = await _database.query(
|
||||||
@ -665,14 +689,19 @@ class DataBaseHelper {
|
|||||||
throw Exception('Invalid device token');
|
throw Exception('Invalid device token');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Find current binded userID
|
var currentUserIDQueryResult = (await _database.query(
|
||||||
var currentUserID = (await _database.query(
|
|
||||||
'bindings',
|
'bindings',
|
||||||
where: 'tokenid = ?',
|
where: 'tokenid = ?',
|
||||||
whereArgs: [
|
whereArgs: [
|
||||||
tokenID
|
tokenID
|
||||||
]
|
]
|
||||||
))[0]['userid'] as int;
|
));
|
||||||
|
|
||||||
|
if(currentUserIDQueryResult.isEmpty) {
|
||||||
|
throw Exception('User not logged in');
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentUserID = currentUserIDQueryResult[0]['userid'] as int;
|
||||||
|
|
||||||
//Fetch pending contacts
|
//Fetch pending contacts
|
||||||
var contactsQueryResult = await _database.query(
|
var contactsQueryResult = await _database.query(
|
||||||
@ -707,13 +736,19 @@ class DataBaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Find current binded userID
|
//Find current binded userID
|
||||||
var currentUserID = (await _database.query(
|
var currentUserIDQueryResult = (await _database.query(
|
||||||
'bindings',
|
'bindings',
|
||||||
where: 'tokenid = ?',
|
where: 'tokenid = ?',
|
||||||
whereArgs: [
|
whereArgs: [
|
||||||
tokenID
|
tokenID
|
||||||
]
|
]
|
||||||
))[0]['userid'] as int;
|
));
|
||||||
|
|
||||||
|
if(currentUserIDQueryResult.isEmpty) {
|
||||||
|
throw Exception('User not logged in');
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentUserID = currentUserIDQueryResult[0]['userid'] as int;
|
||||||
|
|
||||||
//Fetch pending contacts
|
//Fetch pending contacts
|
||||||
var contactsQueryResult = await _database.query(
|
var contactsQueryResult = await _database.query(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-08 20:52:48
|
* @Date : 2022-10-08 20:52:48
|
||||||
* @LastEditTime : 2022-10-13 20:30:53
|
* @LastEditTime : 2022-10-14 11:29:03
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -259,9 +259,9 @@ Future<TCPResponse> onFetchContact(TCPRequest request, Socket socket) async {
|
|||||||
type: ResponseType.fromRequestType(request.requestType),
|
type: ResponseType.fromRequestType(request.requestType),
|
||||||
status: ResponseStatus.ok,
|
status: ResponseStatus.ok,
|
||||||
body: {
|
body: {
|
||||||
"contacts": contacts.map((e) => e.jsonObject),
|
"contacts": contacts.map((e) => e.jsonObject).toList(),
|
||||||
"pending": pendingContacts.map((e) => e.jsonObject),
|
"pending": pendingContacts.map((e) => e.jsonObject).toList(),
|
||||||
"requesting": requestingContacts.map((e) => e.jsonObject)
|
"requesting": requestingContacts.map((e) => e.jsonObject).toList()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-08 15:10:04
|
* @Date : 2022-10-08 15:10:04
|
||||||
* @LastEditTime : 2022-10-12 13:45:01
|
* @LastEditTime : 2022-10-14 10:23:16
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:async/async.dart';
|
||||||
import 'package:tcp_server/tcpcontroller/request.dart';
|
import 'package:tcp_server/tcpcontroller/request.dart';
|
||||||
import 'package:tcp_server/tcpcontroller/response.dart';
|
import 'package:tcp_server/tcpcontroller/response.dart';
|
||||||
|
|
||||||
@ -24,26 +25,31 @@ class TCPController {
|
|||||||
int payloadLength = 0;
|
int payloadLength = 0;
|
||||||
|
|
||||||
//Construct a stream which emits events on intact requests
|
//Construct a stream which emits events on intact requests
|
||||||
StreamController<List<int>> _requestStreamController = StreamController()..close();
|
StreamController<List<int>> _requestRawStreamController = StreamController();
|
||||||
|
StreamController<File> _payloadRawStreamController = StreamController();
|
||||||
|
|
||||||
//Construct a payload stream which forward the incoming byte into temp file
|
//Construct a payload stream which forward the incoming byte into temp file
|
||||||
StreamController<List<int>> _payloadStreamController = StreamController()..close();
|
StreamController<List<int>> _payloadPullStreamController = StreamController()..close();
|
||||||
|
|
||||||
//Provide a request stream for caller functions to listen on
|
//Provide a request stream for caller functions to listen on
|
||||||
final StreamController<TCPRequest> _inStreamController = StreamController();
|
final StreamController<TCPRequest> _requestStreamController = StreamController();
|
||||||
Stream<TCPRequest> get inStream => _inStreamController.stream;
|
Stream<TCPRequest>? _requestStreamBroadcast;
|
||||||
|
Stream<TCPRequest> get requestStreamBroadcast {
|
||||||
|
_requestStreamBroadcast ??= _requestStreamController.stream.asBroadcastStream();
|
||||||
|
return _requestStreamBroadcast!;
|
||||||
|
}
|
||||||
|
|
||||||
//Provide a post stream for caller functions to push to
|
//Provide a post stream for caller functions to push to
|
||||||
final StreamController<TCPResponse> _outStreamController = StreamController();
|
final StreamController<TCPResponse> _responseStreamController = StreamController();
|
||||||
StreamSink<TCPResponse> get outStream => _outStreamController.sink;
|
StreamSink<TCPResponse> get outStream => _responseStreamController.sink;
|
||||||
|
|
||||||
TCPController({
|
TCPController({
|
||||||
required this.socket
|
required this.socket
|
||||||
}) {
|
}) {
|
||||||
socket.listen(socketHandler);
|
socket.listen(_pullRequest);
|
||||||
//This future never ends, would that be bothersome?
|
//This future never ends, would that be bothersome?
|
||||||
Future(() async {
|
Future(() async {
|
||||||
await for(var response in _outStreamController.stream) {
|
await for(var response in _responseStreamController.stream) {
|
||||||
await socket.addStream(response.stream);
|
await socket.addStream(response.stream);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -52,15 +58,26 @@ class TCPController {
|
|||||||
// _outStreamController.stream.listen((response) async {
|
// _outStreamController.stream.listen((response) async {
|
||||||
// await socket.addStream(response.stream);
|
// await socket.addStream(response.stream);
|
||||||
// });
|
// });
|
||||||
|
Future(() async {
|
||||||
|
var requestQueue = StreamQueue(_requestRawStreamController.stream);
|
||||||
|
var payloadQueue = StreamQueue(_payloadRawStreamController.stream);
|
||||||
|
while(await Future<bool>(() => !_requestRawStreamController.isClosed && !_payloadRawStreamController.isClosed)) {
|
||||||
|
var response = await requestQueue.next;
|
||||||
|
var payload = await payloadQueue.next;
|
||||||
|
await _pushRequest(requestBytes: response, tempFile: payload);
|
||||||
|
}
|
||||||
|
requestQueue.cancel();
|
||||||
|
payloadQueue.cancel();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Listen to the incoming stream and emits event whenever there is a intact request
|
//Listen to the incoming stream and emits event whenever there is a intact request
|
||||||
void socketHandler(Uint8List fetchedData) {
|
void _pullRequest(Uint8List fetchedData) {
|
||||||
//Put incoming data into buffer
|
//Put incoming data into buffer
|
||||||
buffer.addAll(fetchedData);
|
buffer.addAll(fetchedData);
|
||||||
//Consume buffer until it's not enough for first 8 byte of a message
|
//Consume buffer until it's not enough for first 8 byte of a message
|
||||||
while(true) {
|
while(true) {
|
||||||
if(requestLength == 0 && payloadLength == 0 && _payloadStreamController.isClosed) {
|
if(requestLength == 0 && payloadLength == 0 && _payloadPullStreamController.isClosed) {
|
||||||
//New request
|
//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
|
//Buffered data has more than 8 bytes, enough to read request length and body length
|
||||||
@ -68,24 +85,19 @@ class TCPController {
|
|||||||
payloadLength = Uint8List.fromList(buffer.sublist(4, 8)).buffer.asInt32List()[0];
|
payloadLength = Uint8List.fromList(buffer.sublist(4, 8)).buffer.asInt32List()[0];
|
||||||
//Clear the length indicator bytes
|
//Clear the length indicator bytes
|
||||||
buffer.removeRange(0, 8);
|
buffer.removeRange(0, 8);
|
||||||
//Create temp file to read payload (might be huge)
|
|
||||||
var tempFile = File('${Directory.current.path}/.tmp/${DateTime.now().microsecondsSinceEpoch}')..createSync();
|
|
||||||
//Initialize payload transmission controller
|
//Initialize payload transmission controller
|
||||||
_payloadStreamController = StreamController();
|
_payloadPullStreamController = StreamController();
|
||||||
//Create a future that listens to the status of the payload transmission
|
//Create a future that listens to the status of the payload transmission
|
||||||
var payloadTransmission = Future(() async {
|
() {
|
||||||
await for(var data in _payloadStreamController.stream) {
|
var payloadPullStream = _payloadPullStreamController.stream;
|
||||||
await tempFile.writeAsBytes(data, mode: FileMode.append, flush: true);
|
var tempFile = File('${Directory.current.path}/.tmp/${DateTime.now().microsecondsSinceEpoch}')..createSync();
|
||||||
}
|
Future(() async {
|
||||||
});
|
await for(var data in payloadPullStream) {
|
||||||
//Bind request construction on stream
|
await tempFile.writeAsBytes(data, mode: FileMode.append, flush: true);
|
||||||
_requestStreamController = StreamController();
|
}
|
||||||
_requestStreamController.stream.listen((requestBytes) {
|
_payloadRawStreamController.add(tempFile);
|
||||||
//When request stream is closed by controller
|
|
||||||
payloadTransmission.then((_) {
|
|
||||||
_inStreamController.add(TCPRequest(requestBytes, tempFile));
|
|
||||||
});
|
});
|
||||||
});
|
}();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//Buffered data is not long enough
|
//Buffered data is not long enough
|
||||||
@ -100,8 +112,7 @@ class TCPController {
|
|||||||
if(buffer.length >= requestLength) {
|
if(buffer.length >= requestLength) {
|
||||||
//Got intact request json
|
//Got intact request json
|
||||||
//Emit request buffer through stream
|
//Emit request buffer through stream
|
||||||
_requestStreamController.add(buffer.sublist(0, requestLength));
|
_requestRawStreamController.add(buffer.sublist(0, requestLength));
|
||||||
_requestStreamController.close();
|
|
||||||
//Remove proccessed buffer
|
//Remove proccessed buffer
|
||||||
buffer.removeRange(0, requestLength);
|
buffer.removeRange(0, requestLength);
|
||||||
//Clear awaiting request length
|
//Clear awaiting request length
|
||||||
@ -118,18 +129,18 @@ class TCPController {
|
|||||||
if(buffer.length >= payloadLength) {
|
if(buffer.length >= payloadLength) {
|
||||||
//Last few bytes to emit
|
//Last few bytes to emit
|
||||||
//Send the last few bytes to stream
|
//Send the last few bytes to stream
|
||||||
_payloadStreamController.add(Uint8List.fromList(buffer.sublist(0, payloadLength)));
|
_payloadPullStreamController.add(Uint8List.fromList(buffer.sublist(0, payloadLength)));
|
||||||
//Clear buffer
|
//Clear buffer
|
||||||
buffer.removeRange(0, payloadLength);
|
buffer.removeRange(0, payloadLength);
|
||||||
//Set payload length to zero
|
//Set payload length to zero
|
||||||
payloadLength = 0;
|
payloadLength = 0;
|
||||||
//Close the payload transmission stream
|
//Close the payload transmission stream
|
||||||
_payloadStreamController.close();
|
_payloadPullStreamController.close();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//Part of payload
|
//Part of payload
|
||||||
//Transmit all to stream
|
//Transmit all to stream
|
||||||
_payloadStreamController.add(Uint8List.fromList(buffer));
|
_payloadPullStreamController.add(Uint8List.fromList(buffer));
|
||||||
//Reduce payload bytes left
|
//Reduce payload bytes left
|
||||||
payloadLength -= buffer.length;
|
payloadLength -= buffer.length;
|
||||||
//Clear buffer
|
//Clear buffer
|
||||||
@ -141,4 +152,11 @@ class TCPController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _pushRequest({
|
||||||
|
required List<int> requestBytes,
|
||||||
|
required File tempFile
|
||||||
|
}) async {
|
||||||
|
_requestStreamController.add(TCPRequest(requestBytes, tempFile));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.1"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
|
|||||||
@ -12,6 +12,7 @@ dependencies:
|
|||||||
sqflite_common: ^2.3.0
|
sqflite_common: ^2.3.0
|
||||||
crypto: ^3.0.2
|
crypto: ^3.0.2
|
||||||
convert: ^3.0.2
|
convert: ^3.0.2
|
||||||
|
async: ^2.9.0
|
||||||
|
|
||||||
# dependencies:
|
# dependencies:
|
||||||
# path: ^1.8.0
|
# path: ^1.8.0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user