2

I am using latest RestKit 0.23.3. As you likely know it works against AFNetworking 1.3.x.

I have Team resource and obtain collection of teams from API server: I use RKObjectManager and its -managedObjectRequestOperationWithRequest:managedObjectContext:success:failure:

RestKit itself works correctly for me - I do obtain teams using GET /teams request - and I see 200 on first request and 304 on consequent requests.

However when I use exactly the same code with HEAD requests I never receive 304 but always 200.

I did more investigation of all this and found that after my iOS application receives first response to GET /teams from Rails server, this response contains Etag:, and so on subsequent requests iOS application adds "If-None-Match:"[etag code...]" that helps Rails to make a decision to respond with 304. What I noticed is that when I use HEAD request iOS application does not mix in "If-None-Match" like it does in case of GET requests.

I tried setting all possible cache policies other than NSURLRequestUseProtocolCachePolicy but none of these policies changed things for me.

If to request headers of HEAD /teams I manually mix If-None-Match:"[etag code...]" with etag obtained from HTTP log i.e. response to GET /teams I do start getting 304 for HEAD requests.

This makes me think that there is some problem in internals of NSURLConnection (bug?) that prevents my iOS application from sending HEAD with If-None-Match: header present and correct.

I would be very thankful to anyone who can shed some light on this. Thanks.


I guess that this may be a bug inside NSURLConnection, I would also appreciate any hints on if it is possible to obtain this etag and mix it manually to my HEAD requests.

Related topic: What response should If-Modified-Since HTTP Header return if request is HEAD?


Important comment by @MikeS:

I did a quick test with straight NSURLConnection and it seems to work fine. I make a GET request, get back an ETag, and then make a HEAD request to the same URL and I can see the if-none-match header on the server side.

...

My original test was using iOS 8.0 in the simulator. I just re-tested with iOS 7.1 in the simulator and this time I did not get the if-none-match header with the HEAD request. Based on that, it does look like it's a bug in iOS 7.1, but it's fixed in iOS 8.0.

Community
  • 1
  • 1
Stanislav Pankevich
  • 11,044
  • 8
  • 69
  • 129
  • I think the question really comes down to why you're making the HEAD request to begin with. If you're just using it to detect if something has changed on the server, you might as well make the GET request and get the new data at the same time, as that seems like the logical thing to do anyway. So it doesn't look like a bug, given that HEAD is essentially a GET with no history. – Jon Shier Sep 16 '14 at 22:25
  • 1
    @jshire, thanks for your comment. I would prefer to keep the question why I want this out of the scope of this technical question. If I understand it right RFC http://tools.ietf.org/html/rfc7232#section-3.2 states it clearly that both GET and HEAD requests should result in 304 response if they contain correct If-None-Match. My own answer below with my workaround is good demonstration that it is exactly this behavior that our server written on Rails 4 exhibits. – Stanislav Pankevich Sep 16 '14 at 22:58
  • I did a quick test with straight `NSURLConnection` and it seems to work fine. I make a `GET` request, get back an `ETag`, and then make a `HEAD` request to the same URL and I can see the `if-none-match` header on the server side. – Mike S Sep 17 '14 at 00:29
  • @MikeS, thanks for comment. I myself also tried skipping RestKit/AFNetworking and sending just `-[NSURLConnection sendAsynchronousConnection:...]` but result was the same - I have never seen 304 response for HEAD request. Charles Proxy shows me that my `HEAD /teams` request does not have 'If-None-Match' within its headers. I tested this both on iPhone 4, iOS 7.1.2 and iOS Simulator 7.1. What version did you test it against? – Stanislav Pankevich Sep 17 '14 at 12:04
  • My original test was using iOS 8.0 in the simulator. I just re-tested with iOS 7.1 in the simulator and this time I **did not** get the `if-none-match` header with the `HEAD` request. Based on that, it does look like it's a bug in iOS 7.1, but it's fixed in iOS 8.0. – Mike S Sep 17 '14 at 17:10

1 Answers1

0

While I don't have any explanation of this, the following workaround works for me:

- (void)headTeamsWithHandler:(APIRequestArrayHandler)handler {
    RKObjectManager *manager = ...;

    NSDictionary *parameters = ...; // correct parameters with token and device ID

    NSMutableURLRequest *teamsHEADRequest = [manager requestWithObject:nil method:RKRequestMethodHEAD path:TEAMS_PATH parameters:[self addDefaultParamsTo:nil]];

    NSMutableURLRequest *teamsGETRequest  = [manager requestWithObject:nil method:RKRequestMethodGET path:TEAMS_PATH parameters:[self addDefaultParamsTo:nil]];

    NSCachedURLResponse *cachedTeamsResponse     = [[NSURLCache sharedURLCache] cachedResponseForRequest:teamsGETRequest];
    NSHTTPURLResponse   *cachedTeamsHTTPResponse = (NSHTTPURLResponse *)cachedTeamsResponse.response;

    NSString *cachedTeamsEtag = cachedTeamsHTTPResponse.allHeaderFields[@"Etag"];

    [teamsHEADRequest setValue:cachedTeamsEtag forHTTPHeaderField:@"If-None-Match"];

    AFHTTPRequestOperation *requestOperation = [manager.HTTPClient HTTPRequestOperationWithRequest:teamsHEADRequest success:^(AFHTTPRequestOperation *operation, id responseObject) {
        // ...
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // ...
    }];

    [requestOperation start];
}

This of course needs both cases to be handled: when there is cached GET /teams response and when there is not a one.

Stanislav Pankevich
  • 11,044
  • 8
  • 69
  • 129