How to solve timeout issues caused by bad HTTP persistent connection?

前端 未结 3 1702
独厮守ぢ
独厮守ぢ 2021-02-06 09:12

I\'ve been struggling with an HTTP timeout issue recently. After more than one month of investigation I\'m quite sure that it is caused by bad HTTP persistent connections. Detai

相关标签:
3条回答
  • 2021-02-06 09:48

    After 2 weeks of research, I can give answers to question 3 and 4:

    1. nginx's persistent connection timeout is set to 5s on server, which should not be the cause. Server engineers found those timed-out requests are actually normally received and responded. So it is more likely a client side issue. Since I have a minimal reproducible code to rule out my code as the cause, the cause should be in iOS.
    2. The only way I found is to use CFNetwork. Higher level API such as NSURLConnection or NSURLSession's Connection header will be overwritten by system.
    0 讨论(0)
  • 2021-02-06 10:02

    Same issue here, iOS just try to reuse the connection after server drops it.

    Why CFNetwork is NOT Enough

    About two years ago, I switched to CFNetwork to workaround this issue, but recently I found it's not possible to implement SSL pinning with CFNetwork API. So now I'm considering go back to NSURLSession.

    The Workaround

    After some dig around, I found system will NOT reuse connections across NSURLSessions, so creating new sessions within period of time, should solve the issue.

    But I also found (at least on macOS): each connection made by NSURLSession can persistence last by 180 seconds, and that connection didn't close by release or reset the session, so you may need to implement some caching mechanism to avoid creating a lots of connections at the same time.

    Here is the simple mechanism I'm currently use:

    @interface HTTPSession : NSObject
    
    @property (nonatomic, strong) NSURLSession * urlSession;
    @property (nonatomic, assign) CFTimeInterval flushTime;
    
    @end
    
    + (NSURLSession *)reuseOrCreateSession
    {
        static NSMutableArray<HTTPSession *> * sessions = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sessions = [NSMutableArray<HTTPSession *> array];
        });
    
        const CFTimeInterval serverTimeoutSeconds = 10;
        const CFTimeInterval systemTimeoutSeconds = 40;
        CFTimeInterval now = CFAbsoluteTimeGetCurrent();
    
        HTTPSession * resultSession = nil;
        for (HTTPSession * session in sessions) {
            CFTimeInterval lifeTime = now - session.flushTime;
            if (lifeTime < serverTimeoutSeconds) {
                resultSession = session;
                break;
            }
            if (lifeTime > systemTimeoutSeconds) {
                resultSession = session;
                resultSession.flushTime = now;
                break;
            }
        }
    
        if (!resultSession) {
            resultSession = [HTTPSession new];
    
            NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    
            // setup session
    
            resultSession.urlSession = session;
            resultSession.flushTime = now;
    
            [sessions addObject:resultSession];
        }
    
        return resultSession.urlSession;
    }
    
    0 讨论(0)
  • 2021-02-06 10:05

    What if you add timestamp to all your request URL? I think this will make each request unique and maybe iOS will establish new connection every time you send request ( I'm not sure. Need trying )

    0 讨论(0)
提交回复
热议问题