MTTcpTransportContext
class
MTTcpTransportContext
// schemes
@property (nonatomic, strong) NSArray<MTTransportScheme *> * _Nonnull schemes;
// tcp connection with specific scheme
@property (nonatomic, strong) MTTcpConnection *connection;
// proxy settings
@property (nonatomic, strong) MTSocksProxySettings *proxySettings;
// connection connected
@property (nonatomic) bool connectionConnected;
// connection is valid
@property (nonatomic) bool connectionIsValid;
// connection behavior
@property (nonatomic, strong) MTTcpConnectionBehaviour *connectionBehaviour;
// stopped
@property (nonatomic) bool stopped;
// is network available
@property (nonatomic) bool isNetworkAvailable;
// will request transaction on next queue pass
@property (nonatomic) bool willRequestTransactionOnNextQueuePass;
// transaction lock time
@property (nonatomic) NSTimeInterval transactionLockTime;
// is waiting for transaction to become ready
@property (nonatomic) bool isWaitingForTransactionToBecomeReady;
// request another transaction when ready
@property (nonatomic) bool requestAnotherTransactionWhenReady;
// did send actualization ping after connection
@property (nonatomic) bool didSendActualizationPingAfterConnection;
// current actualization ping message id
@property (nonatomic) int64_t currentActualizationPingMessageId;
// actualization ping resend timer
@property (nonatomic, strong) MTTimer *actualizationPingResendTimer;
// connection watch dog timer
@property (nonatomic, strong) MTTimer *connectionWatchdogTimer;
// sleep watch dog timer
@property (nonatomic, strong) MTTimer *sleepWatchdogTimer;
// sleep watch dog timer last time
@property (nonatomic) CFAbsoluteTime sleepWatchdogTimerLastTime;
MTTcpTransport
class
MTTcpTransport:
MTTransport,
MTTcpConnectionDelegate,
MTTcpConnectionBehaviourDelegate
MTTcpTransportContext *_transportContext;
__weak MTContext *_context;
NSInteger _datacenterId;
MTNetworkUsageCalculationInfo *_usageCalculationInfo;
// transport queue
+ (MTQueue *)tcpTransportQueue
{
static MTQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[MTQueue alloc] initWithName:"org.mtproto.tcpTransportQueue"];
});
return queue;
}
- (instancetype)initWithDelegate:(id<MTTransportDelegate>)delegate context:(MTContext *)context datacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes proxySettings:(MTSocksProxySettings *)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo
{
#ifdef DEBUG
NSAssert(context != nil, @"context should not be nil");
NSAssert(datacenterId != 0, @"datacenterId should not be nil");
NSAssert(schemes.count != 0, @"schemes should not be empty");
#endif
self = [super initWithDelegate:delegate context:context datacenterId:datacenterId schemes:schemes proxySettings:proxySettings usageCalculationInfo:usageCalculationInfo];
if (self != nil)
{
_context = context;
_datacenterId = datacenterId;
_usageCalculationInfo = usageCalculationInfo;
MTTcpTransportContext *transportContext = [[MTTcpTransportContext alloc] init];
_transportContext = transportContext;
// setup transport context
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^{
transportContext.schemes = schemes;
transportContext.connectionBehaviour = [[MTTcpConnectionBehaviour alloc] initWithQueue:[MTTcpTransport tcpTransportQueue]];
transportContext.connectionBehaviour.delegate = self;
transportContext.isNetworkAvailable = true;
// apiEnvironment.socksProxySettins
transportContext.proxySettings = context.apiEnvironment.socksProxySettings;
}];
}
return self;
}
// set network usage calculation info
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo {
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^{
_usageCalculationInfo = usageCalculationInfo;
// set connection usageCalculationInfo
[_transportContext.connection setUsageCalculationInfo:usageCalculationInfo];
}];
}
- (bool)needsParityCorrection
{
return true;
}
// update connection state
- (void)updateConnectionState
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
id<MTTransportDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(transportNetworkAvailabilityChanged:isNetworkAvailable:)])
[delegate transportNetworkAvailabilityChanged:self isNetworkAvailable:transportContext.isNetworkAvailable];
if ([delegate respondsToSelector:@selector(transportConnectionStateChanged:isConnected:proxySettings:)])
[delegate transportConnectionStateChanged:self isConnected:transportContext.connectionConnected proxySettings:transportContext.proxySettings];
if ([delegate respondsToSelector:@selector(transportConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[delegate transportConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:transportContext.currentActualizationPingMessageId != 0];
}];
}
// set delegate needs transaction
- (void)setDelegateNeedsTransaction
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (!transportContext.willRequestTransactionOnNextQueuePass)
{
transportContext.willRequestTransactionOnNextQueuePass = true;
dispatch_async([MTTcpTransport tcpTransportQueue].nativeQueue, ^
{
transportContext.willRequestTransactionOnNextQueuePass = false;
if (transportContext.connection == nil)
// request connection
[transportContext.connectionBehaviour requestConnection];
else if (transportContext.connectionConnected)
// request transaction
[self _requestTransactionFromDelegate];
});
}
}];
}
// start if needed
- (void)startIfNeeded
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection == nil)
{
MTContext *context = _context;
// choose scheme
MTTransportScheme *scheme = [context chooseTransportSchemeForConnectionToDatacenterId:_datacenterId schemes:transportContext.schemes];
if (scheme != nil) {
// start connection watchdog
[self startConnectionWatchdogTimer:scheme];
// start sleep watchdog
[self startSleepWatchdogTimer];
transportContext.connection = [[MTTcpConnection alloc] initWithContext:context datacenterId:_datacenterId scheme:scheme interface:nil usageCalculationInfo:_usageCalculationInfo];
transportContext.connection.delegate = self;
// start connection
[transportContext.connection start];
}
}
}];
}
// reset
- (void)reset
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
// stop connection
[transportContext.connection stop];
}];
}
// stop
- (void)stop
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
[self activeTransactionIds:^(NSArray *activeTransactionId)
{
id<MTTransportDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(transportTransactionsMayHaveFailed:transactionIds:)])
[delegate transportTransactionsMayHaveFailed:self transactionIds:activeTransactionId];
}];
transportContext.stopped = true;
transportContext.connectionConnected = false;
transportContext.connectionIsValid = false;
// notify connection state changed
id<MTTransportDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(transportConnectionStateChanged:isConnected:proxySettings:)])
[delegate transportConnectionStateChanged:self isConnected:false proxySettings:transportContext.proxySettings];
transportContext.connectionBehaviour.needsReconnection = false;
transportContext.connection.delegate = nil;
[transportContext.connection stop];
transportContext.connection = nil;
// stop connection watchdog timer
[self stopConnectionWatchdogTimer];
// stop sleep watchdog timer
[self stopSleepWatchdogTimer];
[transportContext.actualizationPingResendTimer invalidate];
transportContext.actualizationPingResendTimer = nil;
}];
}
// restart sleep watchdog timer
- (void)restartSleepWatchdogTimer
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
transportContext.sleepWatchdogTimerLastTime = MTAbsoluteSystemTime();
[transportContext.sleepWatchdogTimer resetTimeout:MTTcpTransportSleepWatchdogTimeout];
}];
}
// stop seleep watchdog timer
- (void)stopSleepWatchdogTimer
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
[transportContext.sleepWatchdogTimer invalidate];
transportContext.sleepWatchdogTimer = nil;
}];
}
// start connection watchdog timer
- (void)startConnectionWatchdogTimer:(MTTransportScheme *)scheme
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connectionWatchdogTimer == nil)
{
__weak MTTcpTransport *weakSelf = self;
transportContext.connectionWatchdogTimer = [[MTTimer alloc] initWithTimeout:20.0 repeat:false completion:^
{
__strong MTTcpTransport *strongSelf = weakSelf;
[strongSelf connectionWatchdogTimeout:scheme];
} queue:[MTTcpTransport tcpTransportQueue].nativeQueue];
[transportContext.connectionWatchdogTimer start];
}
}];
}
// stop connection watchdog timer
- (void)stopConnectionWatchdogTimer
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
[transportContext.connectionWatchdogTimer invalidate];
transportContext.connectionWatchdogTimer = nil;
}];
}
// connection watchdog timer timeout
- (void)connectionWatchdogTimeout:(MTTransportScheme *)scheme
{
MTTcpTransportContext *transportContext = _transportContext;
[transportContext.connectionWatchdogTimer invalidate];
transportContext.connectionWatchdogTimer = nil;
id<MTTransportDelegate> delegate = self.delegate;
if (scheme != nil) {
// notify connection failed
if ([delegate respondsToSelector:@selector(transportConnectionFailed:scheme:)]) {
[delegate transportConnectionFailed:self scheme:scheme];
}
// notify connection status changed
if ([delegate respondsToSelector:@selector(transportConnectionProblemsStatusChanged:scheme:hasConnectionProblems:isProbablyHttp:)]) {
[delegate transportConnectionProblemsStatusChanged:self scheme:scheme hasConnectionProblems:true isProbablyHttp:false];
}
}
}
// start actualization ping resend timer
- (void)startActualizationPingResendTimer
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.actualizationPingResendTimer != nil)
[transportContext.actualizationPingResendTimer invalidate];
__weak MTTcpTransport *weakSelf = self;
transportContext.actualizationPingResendTimer = [[MTTimer alloc] initWithTimeout:3 repeat:false completion:^
{
__strong MTTcpTransport *strongSelf = weakSelf;
[strongSelf resendActualizationPing];
} queue:[MTTcpTransport tcpTransportQueue].nativeQueue];
[transportContext.actualizationPingResendTimer start];
}];
}
// stop actualization ping resend timer
- (void)stopActualizationPingResendTimer
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.actualizationPingResendTimer != nil)
{
[transportContext.actualizationPingResendTimer invalidate];
transportContext.actualizationPingResendTimer = nil;
}
}];
}
// resend actualization ping
- (void)resendActualizationPing
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
// stop ping resend timer
[self stopActualizationPingResendTimer];
if (transportContext.currentActualizationPingMessageId != 0)
{
transportContext.didSendActualizationPingAfterConnection = false;
transportContext.currentActualizationPingMessageId = 0;
// request transaction
[self _requestTransactionFromDelegate];
}
}];
}
// tcp connection opened
- (void)tcpConnectionOpened:(MTTcpConnection *)connection
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection != connection)
return;
transportContext.connectionConnected = true;
// connection is valid = false ?
transportContext.connectionIsValid = false;
[transportContext.connectionBehaviour connectionOpened];
id<MTTransportDelegate> delegate = self.delegate;
// notify connection status changed
if ([delegate respondsToSelector:@selector(transportConnectionStateChanged:isConnected:proxySettings:)])
[delegate transportConnectionStateChanged:self isConnected:true proxySettings:transportContext.proxySettings];
transportContext.didSendActualizationPingAfterConnection = false;
transportContext.currentActualizationPingMessageId = 0;
// request transaction
[self _requestTransactionFromDelegate];
}];
}
// connection closed
- (void)tcpConnectionClosed:(MTTcpConnection *)connection error:(bool)error
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection != connection)
return;
transportContext.connectionConnected = false;
transportContext.connectionIsValid = false;
transportContext.connection.delegate = nil;
transportContext.connection = nil;
[transportContext.connectionBehaviour connectionClosed];
transportContext.didSendActualizationPingAfterConnection = false;
transportContext.currentActualizationPingMessageId = 0;
[self restartSleepWatchdogTimer];
id<MTTransportDelegate> delegate = self.delegate;
// notify connection state changed
if ([delegate respondsToSelector:@selector(transportConnectionStateChanged:isConnected:proxySettings:)])
[delegate transportConnectionStateChanged:self isConnected:false proxySettings:transportContext.proxySettings];
// notify connection failed
if (error) {
if ([delegate respondsToSelector:@selector(transportConnectionFailed:scheme:)]) {
[delegate transportConnectionFailed:self scheme:connection.scheme];
}
}
// notify transaction may have failed
if ([delegate respondsToSelector:@selector(transportTransactionsMayHaveFailed:transactionIds:)])
[delegate transportTransactionsMayHaveFailed:self transactionIds:@[connection.internalId]];
}];
}
// received data in connnection
- (void)tcpConnectionReceivedData:(MTTcpConnection *)connection data:(NSData *)data
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection != connection)
return;
if (transportContext.currentActualizationPingMessageId != 0 && transportContext.actualizationPingResendTimer == nil)
// start ping resend timer
[self startActualizationPingResendTimer];
__weak MTTcpTransport *weakSelf = self;
// process incoming data
[self _processIncomingData:data scheme:connection.scheme transactionId:connection.internalId requestTransactionAfterProcessing:false decodeResult:^(id transactionId, bool success)
{
if (success)
{
__strong MTTcpTransport *strongSelf = weakSelf;
[strongSelf connectionIsValid:transactionId];
}
else
{
__strong MTTcpTransport *strongSelf = weakSelf;
[strongSelf connectionIsInvalid];
}
}];
}];
}
// mark transaction valid
- (void)connectionIsValid:(id)transactionId
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection != nil && [transportContext.connection.internalId isEqual:transactionId])
{
transportContext.connectionIsValid = true;
[transportContext.connectionBehaviour connectionValidDataReceived];
}
[self stopConnectionWatchdogTimer];
}];
}
// mark connection is valid
- (void)connectionIsInvalid
{
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
id<MTTransportDelegate> delegate = self.delegate;
MTTransportScheme *scheme = _transportContext.connection.scheme;
if (scheme != nil) {
// notify connection problems status changed
if ([delegate respondsToSelector:@selector(transportConnectionProblemsStatusChanged:scheme:hasConnectionProblems:isProbablyHttp:)]) {
[delegate transportConnectionProblemsStatusChanged:self scheme:scheme hasConnectionProblems:true isProbablyHttp:true];
}
}
}];
}
// received quick ack
- (void)tcpConnectionReceivedQuickAck:(MTTcpConnection *)connection quickAck:(int32_t)quickAck
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection != connection)
return;
id<MTTransportDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(transportReceivedQuickAck:quickAckId:)])
[delegate transportReceivedQuickAck:self quickAckId:quickAck];
}];
}
// notify delegate to decode packet process token
- (void)tcpConnectionDecodePacketProgressToken:(MTTcpConnection *)connection data:(NSData *)data token:(int64_t)token completion:(void (^)(int64_t token, id packetProgressToken))completion
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection != connection)
return;
id<MTTransportDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(transportDecodeProgressToken:scheme:data:token:completion:)])
[delegate transportDecodeProgressToken:self scheme:connection.scheme data:data token:token completion:completion];
}];
}
// data receive progress updated
- (void)tcpConnectionProgressUpdated:(MTTcpConnection *)connection packetProgressToken:(id)packetProgressToken packetLength:(NSUInteger)packetLength progress:(float)progress
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connection != connection)
return;
id<MTTransportDelegate> delegate = self.delegate;
// notify data receive progress updated
if ([delegate respondsToSelector:@selector(transportUpdatedDataReceiveProgress:progressToken:packetLength:progress:)])
[delegate transportUpdatedDataReceiveProgress:self progressToken:packetProgressToken packetLength:packetLength progress:progress];
}];
}
// connection behaviour requests reconnection
- (void)tcpConnectionBehaviourRequestsReconnection:(MTTcpConnectionBehaviour *)behaviour error:(bool)error
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.connectionBehaviour != behaviour)
return;
if (!transportContext.stopped) {
// start if needed
[self startIfNeeded];
}
}];
}
// request transaction
- (void)_requestTransactionFromDelegate
{
MTTcpTransportContext *transportContext = _transportContext;
if (transportContext.isWaitingForTransactionToBecomeReady)
{
if (!transportContext.didSendActualizationPingAfterConnection)
{
if (MTLogEnabled()) {
MTLog(@"[MTTcpTransport#%x unlocking transaction processing due to connection context update task]", (int)self);
}
transportContext.isWaitingForTransactionToBecomeReady = false;
transportContext.transactionLockTime = 0.0;
}
else if (CFAbsoluteTimeGetCurrent() > transportContext.transactionLockTime + 1.0)
{
if (MTLogEnabled()) {
MTLog(@"[MTTcpTransport#%x unlocking transaction processing due to timeout]", (int)self);
}
transportContext.isWaitingForTransactionToBecomeReady = false;
transportContext.transactionLockTime = 0.0;
}
else
{
if (MTLogEnabled()) {
MTLog(@"[MTTcpTransport#%x skipping transaction request]", (int)self);
}
transportContext.requestAnotherTransactionWhenReady = true;
return;
}
}
id<MTTransportDelegate> delegate = self.delegate;
MTTransportScheme *scheme = transportContext.connection.scheme;
if (scheme != nil && [delegate respondsToSelector:@selector(transportReadyForTransaction:scheme:transportSpecificTransaction:forceConfirmations:transactionReady:)])
{
transportContext.isWaitingForTransactionToBecomeReady = true;
transportContext.transactionLockTime = CFAbsoluteTimeGetCurrent();
MTMessageTransaction *transportSpecificTransaction = nil;
if (!transportContext.didSendActualizationPingAfterConnection)
{
transportContext.didSendActualizationPingAfterConnection = true;
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
MTBuffer *pingBuffer = [[MTBuffer alloc] init];
[pingBuffer appendInt32:(int32_t)0x7abe77ec];
[pingBuffer appendInt64:randomId];
// ping
MTOutgoingMessage *outgoingMessage = [[MTOutgoingMessage alloc] initWithData:pingBuffer.data metadata:@"ping" shortMetadata:@"ping"];
outgoingMessage.requiresConfirmation = false;
__weak MTTcpTransport *weakSelf = self;
transportSpecificTransaction = [[MTMessageTransaction alloc] initWithMessagePayload:@[outgoingMessage] prepared:nil failed:nil completion:^(__unused NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId)
{
MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[outgoingMessage.internalId];
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (preparedMessage != nil)
{
__strong MTTcpTransport *strongSelf = weakSelf;
if (strongSelf != nil) {
transportContext.currentActualizationPingMessageId = preparedMessage.messageId;
// notify context update state changed
id<MTTransportDelegate> delegate = strongSelf.delegate;
if ([delegate respondsToSelector:@selector(transportConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)]) {
[delegate transportConnectionContextUpdateStateChanged:strongSelf isUpdatingConnectionContext:true];
}
}
}
}];
}];
transportSpecificTransaction.requiresEncryption = true;
}
// notify to delegate `ready for transaction`
__weak MTTcpTransport *weakSelf = self;
[delegate transportReadyForTransaction:self scheme:scheme transportSpecificTransaction:transportSpecificTransaction forceConfirmations:transportSpecificTransaction != nil transactionReady:^(NSArray *transactionList)
{
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
__strong MTTcpTransport *strongSelf = weakSelf;
if (strongSelf != nil) {
for (MTTransportTransaction *transaction in transactionList)
{
if (transaction.payload.length != 0)
{
bool acceptTransaction = true;
/*#ifdef DEBUG
if (arc4random_uniform(10) < 5) {
acceptTransaction = false;
}
#endif*/
if (transportContext.connection != nil && acceptTransaction)
{
id transactionId = transportContext.connection.internalId;
// send transaction payload
[transportContext.connection sendDatas:@[transaction.payload] completion:^(bool success)
{
if (transaction.completion)
transaction.completion(success, transactionId);
} requestQuickAck:transaction.needsQuickAck expectDataInResponse:transaction.expectsDataInResponse];
}
else if (transaction.completion != nil)
transaction.completion(false, nil);
}
}
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
transportContext.isWaitingForTransactionToBecomeReady = false;
if (transportContext.requestAnotherTransactionWhenReady)
{
transportContext.requestAnotherTransactionWhenReady = false;
[strongSelf _requestTransactionFromDelegate];
}
}];
}
}];
}];
}
}
// query active transaction ids
- (void)activeTransactionIds:(void (^)(NSArray *activeTransactionId))completion
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (completion && transportContext.connection != nil)
completion(@[transportContext.connection.internalId]);
}];
}
// network availability changed
- (void)_networkAvailabilityChanged:(bool)networkAvailable
{
MTTcpTransportContext *transportContext = _transportContext;
[super _networkAvailabilityChanged:networkAvailable];
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
transportContext.isNetworkAvailable = networkAvailable;
if (networkAvailable)
[transportContext.connectionBehaviour clearBackoff];
[transportContext.connection stop];
}];
}
// session changed
- (void)mtProtoDidChangeSession:(MTProto *)__unused mtProto
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
[self stopActualizationPingResendTimer];
transportContext.currentActualizationPingMessageId = 0;
id<MTTransportDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(transportConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[delegate transportConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:false];
}];
}
// server did change session
// firstValidMessageId
// otherValidMessageId
- (void)mtProtoServerDidChangeSession:(MTProto *)__unused mtProto firstValidMessageId:(int64_t)firstValidMessageId otherValidMessageIds:(NSArray *)otherValidMessageIds
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.currentActualizationPingMessageId != 0 && (transportContext.currentActualizationPingMessageId < firstValidMessageId && ![otherValidMessageIds containsObject:@(transportContext.currentActualizationPingMessageId)]))
{
[self stopActualizationPingResendTimer];
transportContext.currentActualizationPingMessageId = 0;
id<MTTransportDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(transportConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[delegate transportConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:false];
}
}];
}
// received message
- (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage *)incomingMessage
{
// handle pong message
if ([incomingMessage.body isKindOfClass:[MTPongMessage class]])
{
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
if (transportContext.currentActualizationPingMessageId != 0 && ((MTPongMessage *)incomingMessage.body).messageId == transportContext.currentActualizationPingMessageId)
{
// stop ping resend timer
[self stopActualizationPingResendTimer];
transportContext.currentActualizationPingMessageId = 0;
id<MTTransportDelegate> delegate = self.delegate;
// notify context update state changed
if ([delegate respondsToSelector:@selector(transportConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[delegate transportConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:false];
}
}];
}
}
// message delivery failed
- (void)mtProto:(MTProto *)__unused mtProto messageDeliveryFailed:(int64_t)messageId
{
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^
{
MTTcpTransportContext *transportContext = _transportContext;
if (transportContext.currentActualizationPingMessageId != 0 && messageId == transportContext.currentActualizationPingMessageId)
{
// stop ping resend timer
[self stopActualizationPingResendTimer];
transportContext.currentActualizationPingMessageId = 0;
id<MTTransportDelegate> delegate = self.delegate;
// notify context update state changed
if ([delegate respondsToSelector:@selector(transportConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[delegate transportConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:false];
}
}];
}
// update schemes
- (void)updateSchemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes {
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^{
if ([transportContext.schemes isEqualToArray:schemes]) {
return;
}
transportContext.schemes = schemes;
bool reset = false;
// reset if current scheme in connection is not included in new updated schemes
if (![transportContext.schemes containsObject:transportContext.connection.scheme]) {
reset = true;
} else if (!transportContext.connectionIsValid) {
reset = true;
}
if (reset) {
// reset connection
[transportContext.connectionBehaviour requestConnection];
}
}];
}