-
Notifications
You must be signed in to change notification settings - Fork 232
Implement SPDY Push #59
base: master
Are you sure you want to change the base?
Conversation
This changeset introduces support for SPDY Push by utilizing a new delegate on `SPDYSession`.
Thanks for splitting this out and cleaning things up. Just so we're on the same page, here's my read of the changes at a high level. Is this right? The app is required to track sessions coming & going, and must attach a delegate to each session in order to be notified of incoming push responses. The pushed data is buffered in the stream until done, at which point the app is notified all at once. Is there a way for the app to cancel a pushed response? Is there any way for the app to know which original request the pushed responses are associated with? Thanks Blake. |
Your assessment of the implementation is spot on. As to your questions: You can cancel the pushes by canceling the original - (void)openPushChannel
{
NSURLSessionConfiguration *pushConfiguration = [self.sessionConfiguration copy];
pushConfiguration.allowsCellularAccess = YES;
NSTimeInterval timeoutInterval = 60 * 60 * 24; // 24 hours
pushConfiguration.timeoutIntervalForRequest = timeoutInterval;
pushConfiguration.timeoutIntervalForResource = timeoutInterval;
pushConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"push" relativeToURL:self.baseURL]];
request.allHTTPHeaderFields = self.URLSession.configuration.HTTPAdditionalHeaders; // Radar 17809816 Workaround
request.timeoutInterval = timeoutInterval; // Radar 17809816 Workaround
LYRLogVerbose(@"opening a push channel on endpoint: %@", request.URL);
NSURLSession *pushSession = [NSURLSession sessionWithConfiguration:pushConfiguration];
[[pushSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
if (!([error.domain isEqualToString:SPDYStreamErrorDomain] && (error.code == SPDYStreamCancel || error.code == SPDYSocketTransportError)) &&
!([error.domain isEqualToString:NSURLErrorDomain] && (error.code == NSURLErrorCancelled))) {
LYRLogError(@"received a response when opening a push channel: %@ %@", response, error);
}
if (self.isConnected) {
LYRLogDebug(@"Reopening SPDY push channel");
[self openPushChannel];
} else {
LYRLogDebug(@"Declining to reopen SPDY push channel: transport is not currently connected.");
}
}
}] resume];
[pushSession finishTasksAndInvalidate];
self.pushSession = pushSession;
}
- (void)closePushChannel
{
[self.pushSession invalidateAndCancel];
self.pushSession = nil;
}
- (BOOL)isPushChannelOpen
{
return self.pushSession != nil;
} We could presumably figure out the request that the response was pushed against. We haven’t needed that info in our implementation so we haven’t done the homework. Presumably we’d just need to do the homework around tracing the SPDY stream ID back to the request and then deliver it with the callback. I can try to knock this out if you’d like. |
Will fix the change around |
…uards for `allSessions`
…session is never returned
Thanks Blake! I've had more time to digest this and talk it over with @goaway, and this looks like a great starting point that we definitely want to use. I'm going to pull it into a local branch and iterate on it and see where it goes. There are a few things that I think will have to change, as we have divergent requirements from you. I'd like to ensure that they will meet our needs, your needs, and the needs of anyone using CocoaSPDY in the future. The spirit of SPDY push requests, per the spec, is that of resources a client will likely request in the near future as part of processing a regular response. That's why there must be an associated stream id. Given this, it would make sense to notify the app of new push responses in a manner that ties them to the original request, such as a delegate that is supplied in the NSURLRequest, rather than being tied to the SPDY session.
Your model is more of a permanent channel for the server to communicate back with the client, if I understand correctly. While not true to the philosophy, I understand the need. I think the model I proposed above would still work for this -- your long-lived GET would receive the notifications about new push responses. Seems like a fairly minor change for you? The session management changes may have to be substantially altered. I think... need to dive deeper. There's a major revision of the object ownership model under consideration (see https://github.com/twitter/CocoaSPDY/compare/new_dispatch) that gives a lot of good benefits -- better dispatching, better stream load balancing, more flexibility with creating / killing tcp connections, etc. But it's going to conflict. I don't believe the changes you made are necessary if one doesn't need to be aware of sessions coming & going via a new delegate, but I may have misread the code. Moving that notification delegate for new push responses into the NSURLRequest is better anyway, as it allows for request-by-request decision on whether pushed responses are processed or not, and they can be better localized to the code that made the original requests and presumably knows what future resources it will want. Let me know what you think, or especially if I've misunderstood anything. Thanks a lot for taking the time to help out! |
All your enhancements to the push support make good sense and evolve the implementation from our specific use case needs into a more robust implementation. We’re currently only using the SPDY push channel to transmit very small Thrift objects in response to real time messaging events so chunking, streaming, and caching never hit our requirements. I don’t see any issues for us in the changes you outlined on push. Looking forward to getting behind an aligned implementation. I read through the changes on the On a tangentially related note, I do have another branch that ports CocoaSPDY to using Grand Central Dispatch laying around. The driver for this was that I could access the thread execution context at will and not have to rely on NSURLProtocol to wake me up inside the NSURLConnectionLoader thread in order to get scheduled on the proper run loop. It allowed me to drop the use of |
I've been working on this the past few weeks and have something nearly done. I squashed it into a single commit to make it easy to review, see kgoodier@46e8cbe. The evolution was basically:
I started with the tests you guys implemented in your branch, but refactored them substantially and added a lot more. I think the comments and interfaces in SPDYProtocol.h should be sufficient to show how to actually use these new push requests, but would appreciate feedback. It's different from what you had but shouldn't be hard to switch to. A few small TODO's remain. I also need to merge in the upcoming changes in 'develop' for 'new_dispatch'. |
@kgoodier did this work get abandoned? I’m looking at brings my fork inline with the |
Hi @blakewatters, nope, this is still in progress, just got de-prioritized for a bit. I ran into some subtle issues about how to expose request/response metadata, and that led to some changes to the push API presented here. It got simplified a lot so the app doesn't have to do much at all or implement any new interfaces. For your use case of a long pull followed by notifications of new pushed streams, it should work like:
I'm working on the final integration tests to make sure everything actually works, but the bulk of the coding is done except for the NSNotification stuff. I won't get much done next week, but expect to focus 100% on finishing this up the week after. We're also intending to get the current develop branched merged into master and make another release. It's been a while and a lot of good things have gone in. That will happen prior to push getting integrated. |
|
This changeset introduces support for SPDY Push by utilizing a new delegate on
SPDYSession
. Closes #1This is a focused subset of the larger changes that we have been maintaining in the @layerhq branch. This isolates the changes necessary to support SPDY Push.
Utilizing push requires registering a delegate for the
SPDYSessionManager
so that you can attach a delegate to eachSPDYSession
as it is dispatched by CocoaSPDY. You must also issue a hanging GET request so that the server has a SPDY stream that it can push responses back against. We’re using it successfully with an Erlang server utilizing the Cowboy server.