Connection class
Representation of connection to server.
class Connection { /** * Flag which marks whether there are problems with connection or no. * If this is false, automatic checker starts sending requests and when * a response arrives, then it is set back to true. */ bool _connected = true; bool get isConnected => _connected; StreamController _onDisconnectedController = new StreamController.broadcast(); StreamController _onConnectedController = new StreamController.broadcast(); Stream get onDisconnected => _onDisconnectedController.stream; Stream get onConnected => _onConnectedController.stream; void _disconnect() { _connected = false; _onDisconnectedController.add(null); } void _reconnect() { _connected = true; _onConnectedController.add(null); } String _authenticatedUserId; String get authenticatedUserId => _authenticatedUserId; final StreamController<String> _onAuthenticatedUserIdChangeController = new StreamController.broadcast(); Stream<String> get onAuthenticatedUserIdChange => _onAuthenticatedUserIdChangeController.stream; final Transport _transport; /** * Dependency injection constructor of [Connection]. * * In majority of cases, you want to use either [createHttpConnection] or * [createLoopBackConnection] factories from [clean_ajax.client_browser] and * [clean_ajax.client_backend] libraries. */ Connection.config(this._transport) { this._transport.setHandlers(_prepareRequest, _handleResponse, _handleError, _disconnect, _reconnect); } /** * Queue of unprepared [ClientRequest]s. * The map entry should contain these keys and values: * 'createRequest': [CreateRequest] object * 'completer': [Completer] object which returns response for the request */ final Queue<Map> _requestQueue = new Queue<Map>(); final Set<Map> _periodicRequests = new Set<Map>(); /** * Maps [Request] names to their future responses. */ final Map<int, Completer> _responseMap = new Map<int, Completer>(); /** * Counts sent requests. Serves as unique ID for new requests. */ int requestCount = 0; List<PackedRequest> _prepareRequest() { var request_list = []; for (var request in _periodicRequests) { send(request['createRequest']).then((value) { request['controller'].add(value); }).catchError((e) { request['controller'].addError(e); }); } while (!_requestQueue.isEmpty) { var map = _requestQueue.removeFirst(); var clientRequest = map['createRequest'](); // create the request if (clientRequest == null) { map['completer'].completeError(new CancelError()); } else { request_list.add(new PackedRequest(requestCount, clientRequest)); _responseMap[requestCount++] = map['completer']; } } return request_list; } // void _handleResponse(List responses) { void _handleResponse(Map responsesAndAuthUser) { String newAuthUserId = responsesAndAuthUser['authenticatedUserId']; if (_authenticatedUserId != newAuthUserId) { _onAuthenticatedUserIdChangeController.add(newAuthUserId); _authenticatedUserId = newAuthUserId; } for (var responseMap in responsesAndAuthUser['responses']) { var id = responseMap['id']; var response = responseMap['response']; if (_responseMap.containsKey(id)) { _responseMap[id].complete(response); _responseMap.remove(id); } } _responseMap.forEach((id, request) { throw new Exception("Request $id was not answered!"); }); } void _handleError(error) { for (var completer in _responseMap.values) { completer.completeError(error); } _responseMap.clear(); } /** * Schedule the send of [ClientRequest] created by factory function * [createRequest]. * * Request will be created immediately before the send, that makes it possible * to send always mostly actual requests. * * Returned [Future] completes with the value of response. */ Future send(CreateRequest createRequest) { var completer = new Completer(); _requestQueue.add({'createRequest': createRequest, 'completer': completer}); _transport.markDirty(); return completer.future; } /** * Schedule the [ClientRequest]s to be sent periodically and return the * [Stream] of responses. * * Every time the transport layer notifies [Connection] about being ready to * send next request, [createRequest] is executed and resulting * [ClientRequest] is send. * * Similarly to [send], this method will notify the transport layer there is * request to be sent. This notification happens only as direct consequence * of calling [sendPeriodically], it won't happen multiple times for single * [createRequest] factory. * * The returned [Stream] can have only single listener, and periodical * requests are canceled when the subscription to results [Stream] is * canceled. */ Stream sendPeriodically(CreateRequest createRequest) { var periodicRequest = {'createRequest': createRequest}; var streamController = new StreamController( onCancel: () => _periodicRequests.remove(periodicRequest)); periodicRequest['controller'] = streamController; _periodicRequests.add(periodicRequest); _transport.markDirty(); return streamController.stream; } }
Constructors
new Connection.config(Transport _transport) #
Dependency injection constructor of Connection.
In majority of cases, you want to use either createHttpConnection
or
createLoopBackConnection
factories from [clean_ajax.client_browser] and
[clean_ajax.client_backend] libraries.
Connection.config(this._transport) { this._transport.setHandlers(_prepareRequest, _handleResponse, _handleError, _disconnect, _reconnect); }
Properties
Methods
Future send(CreateRequest createRequest) #
Schedule the send of ClientRequest created by factory function createRequest.
Request will be created immediately before the send, that makes it possible to send always mostly actual requests.
Returned Future completes with the value of response.
Future send(CreateRequest createRequest) { var completer = new Completer(); _requestQueue.add({'createRequest': createRequest, 'completer': completer}); _transport.markDirty(); return completer.future; }
Stream sendPeriodically(CreateRequest createRequest) #
Schedule the ClientRequests to be sent periodically and return the Stream of responses.
Every time the transport layer notifies Connection about being ready to send next request, createRequest is executed and resulting ClientRequest is send.
Similarly to send, this method will notify the transport layer there is request to be sent. This notification happens only as direct consequence of calling sendPeriodically, it won't happen multiple times for single createRequest factory.
The returned Stream can have only single listener, and periodical requests are canceled when the subscription to results Stream is canceled.
Stream sendPeriodically(CreateRequest createRequest) { var periodicRequest = {'createRequest': createRequest}; var streamController = new StreamController( onCancel: () => _periodicRequests.remove(periodicRequest)); periodicRequest['controller'] = streamController; _periodicRequests.add(periodicRequest); _transport.markDirty(); return streamController.stream; }