Home telegram - mtTcpTransportContext
Post
Cancel

telegram - mtTcpTransportContext

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];
        }
    }];
}
This post is licensed under CC BY 4.0 by the author.