HTTPMaximumConnectionsPerHost is ignored in AFHTTPSessionManager
Asked Answered
H

2

1

I am trying to use AFNetworking2.0 with new NSURLSession class to fetch some data. In particular I am interested in new HTTPMaximumConnectionsPerHost property of NSURLSessionConfiguration but unfortunately it doesn't seem to work as I expect.

@interface ViewController ()
@property (nonatomic, strong) AFHTTPSessionManager *httpSessionManager;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSURLSessionConfiguration *sessionConfig = [[NSURLSessionConfiguration defaultSessionConfiguration] copy];
    sessionConfig.HTTPMaximumConnectionsPerHost = 5;

    self.httpSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:sessionConfig];
    self.httpSessionManager.responseSerializer = [AFImageResponseSerializer serializer];

    for (int i = 0; i < 100; ++i) {
        //http://i.dailymail.co.uk/i/pix/2012/07/03/article-2168112-13E70C13000005DC-867_964x946.jpg // 400k picture, the one from deviantart is 2k
        [self.httpSessionManager GET:@"http://a.deviantart.net/avatars/t/o/toaster-kitten.jpg" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
            NSLog(@"has finished task %p, context id %lu", task, task.taskIdentifier);
        } failure:^(NSURLSessionDataTask *task, NSError *error) {
            NSLog(@"has failed task %p, context id %lu", task, task.taskIdentifier);
        }];
    }
}

This is my sample implementation of download.

I have also tweaked AFHTTPSessionManager to produce logs:

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(NSDictionary *)parameters
                      success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                      failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];

    __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(task, error);
            }
        } else {
            if (success) {
                success(task, responseObject);
            }
        }
    }];

    [task addObserver:self forKeyPath:@"countOfBytesReceived" options:NSKeyValueObservingOptionOld context:(__bridge void*)task];
    NSLog(@"created task %p id %u", task, task.taskIdentifier);

    [task resume];

    return task;
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if([[change objectForKey:@"old"] integerValue] == 0 && [keyPath isEqualToString:@"countOfBytesReceived"]){
        NSLog(@"dtask %p id %u: started", object, ((NSURLSessionDataTask*)object).taskIdentifier);
        @try {
            if ((__bridge void *)object == context) {
                [object removeObserver:self forKeyPath:@"countOfBytesReceived" context:(__bridge void*)object];
            }
        }
        @catch (NSException *exception) {
        }
    }
}

With this code I receive the next log:

2014-04-02 17:10:38.950 test_block[2704:60b] created task 0x1701a4fa0 id 0
2014-04-02 17:10:38.972 test_block[2704:60b] created task 0x1781a5be0 id 1
2014-04-02 17:10:38.974 test_block[2704:60b] created task 0x1701a5160 id 2
2014-04-02 17:10:38.976 test_block[2704:60b] created task 0x1701a5320 id 3
2014-04-02 17:10:38.977 test_block[2704:60b] created task 0x1701a54e0 id 4
2014-04-02 17:10:38.979 test_block[2704:60b] created task 0x1701a56a0 id 5
2014-04-02 17:10:38.980 test_block[2704:60b] created task 0x1701a5940 id 6
2014-04-02 17:10:38.982 test_block[2704:1803] dtask 0x1701a4fa0 id 0: started
2014-04-02 17:10:38.984 test_block[2704:6403] dtask 0x1781a5be0 id 1: started
2014-04-02 17:10:38.986 test_block[2704:4b03] dtask 0x1701a56a0 id 5: started
2014-04-02 17:10:38.987 test_block[2704:5b03] dtask 0x1701a5160 id 2: started
2014-04-02 17:10:38.988 test_block[2704:6703] dtask 0x1701a5320 id 3: started
2014-04-02 17:10:38.990 test_block[2704:60b] created task 0x1781a5da0 id 7
2014-04-02 17:10:38.990 test_block[2704:1803] dtask 0x1701a54e0 id 4: started
2014-04-02 17:10:38.992 test_block[2704:6903] dtask 0x1701a5940 id 6: started
2014-04-02 17:10:38.993 test_block[2704:60b] created task 0x1701a4ec0 id 8
2014-04-02 17:10:38.993 test_block[2704:1803] dtask 0x1781a5da0 id 7: started
2014-04-02 17:10:38.994 test_block[2704:60b] created task 0x1781a5cc0 id 9
2014-04-02 17:10:38.996 test_block[2704:6403] dtask 0x1701a4ec0 id 8: started
2014-04-02 17:10:38.997 test_block[2704:60b] created task 0x1701a5240 id 10
2014-04-02 17:10:38.998 test_block[2704:5103] dtask 0x1781a5cc0 id 9: started
2014-04-02 17:10:38.999 test_block[2704:60b] created task 0x1781a6200 id 11
2014-04-02 17:10:39.391 test_block[2704:4603] dtask 0x1701a5240 id 10: started
2014-04-02 17:10:39.399 test_block[2704:60b] created task 0x1781a62e0 id 12
2014-04-02 17:10:39.406 test_block[2704:3507] dtask 0x1781a6200 id 11: started
2014-04-02 17:10:39.409 test_block[2704:60b] created task 0x1701a63c0 id 13
2014-04-02 17:10:39.411 test_block[2704:60b] created task 0x1701a6580 id 14
2014-04-02 17:10:39.414 test_block[2704:1803] dtask 0x1781a62e0 id 12: started
2014-04-02 17:10:39.418 test_block[2704:3507] dtask 0x1701a63c0 id 13: started
2014-04-02 17:10:39.421 test_block[2704:60b] created task 0x1781a6ac0 id 15
2014-04-02 17:10:39.423 test_block[2704:5b03] dtask 0x1701a6580 id 14: started
2014-04-02 17:10:39.425 test_block[2704:60b] created task 0x1701a5cc0 id 16
2014-04-02 17:10:39.427 test_block[2704:5b03] dtask 0x1781a6ac0 id 15: started
2014-04-02 17:10:39.428 test_block[2704:60b] created task 0x1701a5f60 id 17
2014-04-02 17:10:39.430 test_block[2704:5103] dtask 0x1701a5cc0 id 16: started
2014-04-02 17:10:39.431 test_block[2704:60b] created task 0x1781a64a0 id 18
2014-04-02 17:10:39.433 test_block[2704:3507] dtask 0x1701a5f60 id 17: started
2014-04-02 17:10:39.434 test_block[2704:60b] created task 0x1701a64a0 id 19
2014-04-02 17:10:39.435 test_block[2704:3507] dtask 0x1781a64a0 id 18: started
2014-04-02 17:10:39.437 test_block[2704:60b] created task 0x1701a6820 id 20
2014-04-02 17:10:39.438 test_block[2704:3507] dtask 0x1701a64a0 id 19: started
2014-04-02 17:10:39.440 test_block[2704:60b] created task 0x1781a6580 id 21
2014-04-02 17:10:39.441 test_block[2704:3507] dtask 0x1701a6820 id 20: started
2014-04-02 17:10:39.442 test_block[2704:60b] created task 0x1701a6120 id 22
2014-04-02 17:10:39.444 test_block[2704:3507] dtask 0x1781a6580 id 21: started
2014-04-02 17:10:39.445 test_block[2704:60b] created task 0x1701a7540 id 23
2014-04-02 17:10:39.446 test_block[2704:3507] dtask 0x1701a6120 id 22: started
2014-04-02 17:10:39.447 test_block[2704:60b] created task 0x1781a6c80 id 24
2014-04-02 17:10:39.448 test_block[2704:60b] created task 0x1701a5e80 id 25
2014-04-02 17:10:39.450 test_block[2704:60b] created task 0x1701a70e0 id 26
2014-04-02 17:10:39.450 test_block[2704:5103] dtask 0x1701a7540 id 23: started
2014-04-02 17:10:39.451 test_block[2704:5b03] dtask 0x1781a6c80 id 24: started
2014-04-02 17:10:39.453 test_block[2704:5103] dtask 0x1701a5e80 id 25: started
2014-04-02 17:10:39.454 test_block[2704:60b] created task 0x1701a7a80 id 27
...
2014-04-02 17:10:39.694 test_block[2704:60b] has finished task 0x1781aaaa0, context id 79
2014-04-02 17:10:39.695 test_block[2704:60b] has finished task 0x1701ac320, context id 80
2014-04-02 17:10:39.695 test_block[2704:60b] has finished task 0x1701abfa0, context id 78
2014-04-02 17:10:39.696 test_block[2704:60b] has finished task 0x1701ac4e0, context id 81
2014-04-02 17:10:39.697 test_block[2704:60b] has finished task 0x1701ac6a0, context id 82
2014-04-02 17:10:39.698 test_block[2704:60b] has finished task 0x1701ac860, context id 83
2014-04-02 17:10:39.699 test_block[2704:60b] has finished task 0x1781aac60, context id 84
2014-04-02 17:10:39.699 test_block[2704:60b] has finished task 0x1701aca20, context id 85
2014-04-02 17:10:39.700 test_block[2704:60b] has finished task 0x1781ab1a0, context id 86
2014-04-02 17:10:39.700 test_block[2704:60b] has finished task 0x1781ab360, context id 87
2014-04-02 17:10:39.701 test_block[2704:60b] has finished task 0x1701acbe0, context id 88
2014-04-02 17:10:39.702 test_block[2704:60b] has finished task 0x1701acda0, context id 89
2014-04-02 17:10:39.702 test_block[2704:60b] has finished task 0x1781ab6e0, context id 92
2014-04-02 17:10:39.703 test_block[2704:60b] has finished task 0x1781ab520, context id 90
2014-04-02 17:10:39.705 test_block[2704:60b] has finished task 0x1701ace80, context id 91
2014-04-02 17:10:39.713 test_block[2704:60b] has finished task 0x1781ab8a0, context id 94
2014-04-02 17:10:39.714 test_block[2704:60b] has finished task 0x1781aba60, context id 95
2014-04-02 17:10:39.714 test_block[2704:60b] has finished task 0x1701ad040, context id 93
2014-04-02 17:10:39.715 test_block[2704:60b] has finished task 0x1701ad200, context id 96
2014-04-02 17:10:39.715 test_block[2704:60b] has finished task 0x1701ad3c0, context id 97
2014-04-02 17:10:39.716 test_block[2704:60b] has finished task 0x1701ab520, context id 98
2014-04-02 17:10:39.716 test_block[2704:60b] has finished task 0x1701accc0, context id 99

Which is not exactly what I expect to see. As you can see more than 5 data tasks are started until at least on of previously started has finished.

I test on iPhone with iOS7.1 and iOS7.0.3

Could anyone please explain why this happens and how to get these tasks queued by NSURLSession (or at least make them fail after some limit)?

Could anyone confirm or refute this behavior?

P.S. I have already implemented my own queuing code so this question is not about how to queue requests but rather how to make it work with NSURLSession.

Hirohito answered 2/4, 2014 at 15:34 Comment(0)
S
1

The documentation states that it will limit the number of connections per host. That has nothing to do with the number of tasks created.

You can easily log the delegate callbacks which notify of incremental updates to see which tasks represent actual connections. The other tasks are in a queue, waiting for their turn to be connected to the host.

Steinbok answered 2/4, 2014 at 18:13 Comment(2)
I am going to describe how I understand the process, correct me if I'm wrong: I log task as started when it gets first portion of data. This means that connection is already established when I get the log. Since it is TCP and my server doesn't support pipelining it means that I can make another request only when I receive response to the current one. From logs I see that around 7 tasks are started without even one finished. So I believe it doesn't work as it should.Hirohito
did you found any solution for thisUnwieldy
D
0

I found out that, for background sessions, setting timeoutIntervalForRequest to a very high value actually makes the next task on queue to wait until the previous task is over (or until the value set at timeoutIntervalForRequest is over).

This looks like a bug, but it worked for me. If you don’t set anything, I think the default is 60 seconds, so the next task will start after 60 seconds, regardless of httpMaximumConnectionsPerHost being 1.

Derna answered 19/10, 2017 at 11:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.