NSURLSession with upload stream - subclassing NSInputStream - com.apple.NSURLConnectionLoader exception - ios

Basic task
I have some multiplatform library which is using some C++ stream interface. I have to use this stream interface to upload data by NSURLSession. My implementation should work on OS X and iOS (currently I'm testing on OS X)
What I did
Task looks quite simple and I was sure I will implement this quite fast.
I have configured NSURLSession which is working fine if I'm using NSURLRequest with simple NSData.
I'm trying to use stream like this:
NSURLSessionDataTask *dataTask = [m_Private.session uploadTaskWithStreamedRequest: request];
HTTPDownoadTaskProxy *dataTaskProxy = [HTTPDownoadTaskProxy new];
// store data to properly handle delegate
dataTaskProxy.coreTask = dataTask;
dataTaskProxy.cppRequest= req;
dataTaskProxy.cppResponseHandler = handler;
dataTaskProxy.cppErrorHandler = errorHandler;
m_Private.streamedDataTasks[dataTask] = dataTaskProxy;
[dataTask resume];
So far so good. According to documentation of uploadTaskWithStreamedRequest I should receive notification from delegate and I do receive it:
- (void)URLSession: (NSURLSession *)session
task: (NSURLSessionTask *)task
needNewBodyStream: (void (^)(NSInputStream *bodyStream))completionHandler
{
HTTPDownoadTaskProxy *proxyTask = self.streamedDataTasks[task];
CppInputStreamWrapper *objcInputStream = [[CppInputStreamWrapper alloc] initWithCppInputStream:proxyTask.cppRequest.GetDataStream()];
completionHandler(objcInputStream);
}
Now I should receive calls in subclass of NSInputStream which is in my case CppInputStreamWrapper, and also it is quite simple:
#implementation CppInputStreamWrapper
- (void)dealloc {
NSLog(#"%s", __PRETTY_FUNCTION__);
}
- (instancetype)initWithCppInputStream: (const std::tr1::shared_ptr<IInputStream>&) cppInputStream
{
if (self = [super init]) {
_cppInputStream = cppInputStream;
}
return self;
}
#pragma mark - overrides for NSInputStream
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
return (NSInteger)self.cppInputStream->Read(buffer, len);
}
- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len {
return NO;
}
- (BOOL)hasBytesAvailable {
return !self.cppInputStream->IsEOF();
}
#pragma mark - this methods are need to be overridden to make stream working
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
#pragma mark - Undocumented CFReadStream Bridged Methods
- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
callback:(__unused CFReadStreamClientCallBack)inCallback
context:(__unused CFStreamClientContext *)inContext {
return NO;
}
#end
So I'm using workaround needed when subclassing NSInputStream.
Problem
Now this should work. But I'm not receiving any call of methods of CppInputStreamWrapper (except for my call when construction object).
No errors no warning are reported, nothing!
When I've added exception breakpoint I'm catching
thread #8: tid = 0x155cb3, 0x00007fff8b770743 libobjc.A.dylib`objc_exception_throw, name = 'com.apple.NSURLConnectionLoader', stop reason = breakpoint 1.1
This comes from thread com.apple.NSURLConnectionLoader which I didn't create.
I'm totally puzzled and have no idea what else I can do.
Update
I've used code form link in comment which is hosted on github.
Now at least some parts of my class are invoked by framework, but I see strange crash.
Crash is located in this method:
- (BOOL)_setCFClientFlags:(CFOptionFlags)inFlags
callback:(CFReadStreamClientCallBack)inCallback
context:(CFStreamClientContext *)inContext {
if (inCallback != NULL) {
requestedEvents = inFlags;
copiedCallback = inCallback;
memcpy(&copiedContext, inContext, sizeof(CFStreamClientContext));
if (copiedContext.info && copiedContext.retain) {
copiedContext.retain(copiedContext.info);
}
copiedCallback((__bridge CFReadStreamRef)self, kCFStreamEventHasBytesAvailable, &copiedContext); // CRASH HERE
} else {
requestedEvents = kCFStreamEventNone;
copiedCallback = NULL;
if (copiedContext.info && copiedContext.release) {
copiedContext.release(copiedContext.info);
}
memset(&copiedContext, 0, sizeof(CFStreamClientContext));
}
return YES;
}
Crash is EXC_BAD_ACCESS (when running tests on OS X). when I see this code everything looks fine. It should work! self is pointing to proper object with retain count 3 so I have no idea why it is crashing.

Undocumented private bridging API is not the only problem in custom NSInputStream implementation especially in the context of CFNetworking integration. I'd like to recommend to use my POSInputStreamLibrary as basic building block. Instead of implementing a lot of NSInputStream methods and supporting async notifications you should implement much simpler POSBlobInputStreamDataSource interface. At least you can look at POSBlobInputStream to consult what kind of functionality you should implement to support NSInputStream contract completely.
POSInputStreamLibrary is used in the most popular Russian cloud storage service Cloud Mail.Ru and uploads >1M files per day without any crashes.
Good luck and feel free to ask any questions.

I see you do have implementations of the undocumented CFReadStream bridge methods -- that is one of the more common issues. However... note the comment in the NSStream.h header for the NSStream class:
// NSStream is an abstract class encapsulating the common API to NSInputStream and NSOutputStream.
// Subclassers of NSInputStream and NSOutputStream must also implement these methods.
That means you also need to implement -open, -close, -propertyForKey:, -streamStatus, etc. -- every method that is declared on NSStream and NSInputStream, basically. Try calling -open yourself in your code (which NSURLConnection will eventually do) -- you will get the idea since it should crash right there. You will probably need at least some minimal status handling so that -streamStatus does not return NSStreamStatusNotOpen after -open is called, for example. Basically, every concrete subclass needs to implement the entirety of the API. It's not like a normal class cluster where just a couple of core methods need to be overridden -- even the -delegate and -setDelegate: methods must be implemented (the superclass does not have instance variable storage for it, I'm pretty sure).
AFNetworking has an internal AFMultipartBodyStream which has the minimal implementations needed -- you can see that example inside AFURLRequestSerialization.m. Another code example is HSCountingInputStream.

Related

Function cannot run concurrently

I would like to make a function that only run once and cancel if is still running.
I tried it using a simple lock boolean on start/end, but sometimes it's "overlapping".
There's a better and secure way to do that?
#property (assign) BOOL lock;
- (void)myFuntion
{
if (self.lock) {
NSLog(#"(Canceled) Syncing is already running...");
return;
}
self.lock = YES;
// My Code
self.lock = NO;
}
The NSLock class should be able to help you here. I have not tried this example directly, but something like:
NSLock *myFunctionLock=[NSLock new]; // this should be a class data member/property/etc.
- (void)myFuntion
{
if (![myFunctionLock tryLock])
return; /* already running */
// My Synchronized Code
[myFunctionLock unlock];
}
We are all assuming that you are talking about concurrent programming, where you are running the same code on different threads. If that's not what you mean then you would need to explain what you DO mean, since code that runs on the same thread can only execute a function once at any particular moment.
Take a look at NSLock's tryLock function. The first caller to assert the lock gets back a TRUE, and can proceed to access the critical resource. Other callers get back FALSE and should not access the critical resource, but won't block.

Avoid using flags to control invocation of code

I am new to ReactiveCocoa and trying to understand , how I can model the following example using reactive cocoa commands: I am trying to read bytes from stream and this is done in the function "readStream". I want to prevent further calls to readStream , until I am done with the first batch of reading. As shown, I am using the "reading" flag, which is set when reading starts and is reset when reading is done. I would like to avoid using the flag "reading", can this be coded in a better way using reactive cocoa.
- (void) stream:(NSStream *) aStream handleEvent:(NSStreamEvent)eventCode {
..
case NSStreamEventHasBytesAvailable:
if (!self.reading){
self.reading = YES; //
[self readStream]; // Reads bytes from the stream
}
break;
- (void) readStream {
// Read data
self.reading = NO;
}
RACCommands enables button until the returned signal complete, I'm not sure the context with your code, if it locates in lower level global singleton component, you still need keep the flag.

ios - get initial data from server on app start

I'm new to iOS development and trying to solve following problem.
In my app (which speaks with REST API) I want to make initial request to server on app start to get user info. I decided to use separate service class with singleton method. It makes request to server once and then returns user instance.
#implementation LSSharedUser
+ (LSUser *)getUser {
// make request to api server on the first call
// on other calls return initialized user
static LSUser *_sharedUser = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
LSHTTPClient *api = [LSHTTPClient create];
[api getUser:^(AFHTTPRequestOperation *operation, id user) {
_sharedUser = [[LSUser alloc] initWithDictionary:user];
} failure:nil];
});
return _sharedUser;
}
#end
My question is it a proper way of initializing global data from server? As you see request is async (with AFNetworking lib) so it will return null until request is finished.
Another problem here is that once it failed (bad connection for example) user will be null forever.
update your code like this
static LSUser *_sharedUser ;
+ (LSUser *)getUser {
// make request to api server on the first call
// on other calls return initialized user
if(_sharedUser){
//this will execute only at first time
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
LSHTTPClient *api = [LSHTTPClient create];
[api getUser:^(AFHTTPRequestOperation *operation, id user) {
_sharedUser = [[LSUser alloc] initWithDictionary:user];
return _sharedUser;
} failure:nil];
});
}
//this will execute 2nd time
return _sharedUser;
}
for answer to line ->
Ques 2 )Another problem here is that once it failed (bad connection for example) user will be null forever.
->once _sharedUser is initialized user will get _sharedData. but until shared data is not initialized it will return null whenever called.
Ques 1 )My question is it a proper way of initializing global data from server? As you see request is async (with AFNetworking lib) so it will return null until request is finished.
a better way is to implement your own custom delegate methods. once request is fetched or when call that do your work in that delegate method.
for 1st time: execute calling delegate methods when request is fetched or failed.
for 2nd time: after if block call delegate methods.
The basic approach requires an asynchronous design.
Say you have an asynchronous method:
- (void) loadUserWithCompletion:(void (^)(NSDictionary* user, NSError* error))completion;
You execute whatever you need to execute in the "Continuation" (the completion block):
[self loadUserWithCompletion:^(NSDictionary* params, NSError*error) {
if (user) {
User* user = [[User alloc] initWithDictionary:params];
// better we ensure we execute the following on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
self.model.user = user;
// If we utilize KVO, observers may listen on self.model.user
// which now get a notification.
// We also may to notify the user through an alert:
...
});
}
else {
// handler error
}
}];
With the asynchronous programming style you need to be more carefully when letting the user execute arbitrary tasks (say, tabbing buttons). You may consider to disable tasks which require a user model. Alternatively, display an alert sheet when the user model is not yet available.
Usually, you use some "observer" technique to get notified when the user model is eventually available. You may use KVO or completion handlers, or use some dedicated third party library which is specialized for those problems (e.g. a Promise library).
You should also let the user cancel that task at any point. This however requires a more elaborated approach, where you have "cancelable" tasks and where you hold references to these tasks in order to able to send them a cancel message.

How can I wait for a NSURLConnection delegate to finish before executing the next statement?

This has been a hard one to search.
I found a similar question, iOS 5 Wait for delegate to finish before populating a table?, but the accepted answer was 'Refresh the table view,' and that does not help me. The other results I found tended to be in c#.
I have an app that streams from iPhone to Wowza servers. When the user hits record, I generate a unique device id, then send it to a PHP script on the server that returns a JSON document with configuration settings (which includes the rtmp dump link).
The problem is, the delegate methods are asynchronous, but I need to get the config settings before the next lines of code in my - (IBAction)recordButtonPressed method, since that code is what sets the profile settings, and then records based on those settings.
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
Is there not some simple waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag flag I can send to the delegator so I can have sequential operations that pull data from the web?
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
You have analyzed and understood the situation and you have described its possible solutions perfectly. I just don't agree with your conclusions. This kind of thing happens all the time:
- (void) doPart1 {
// do something here that will eventually cause part2 to be called
}
- (void) doPart2 {
}
You can play various games with invocations to make this more elegant and universal, but my advice would be, don't fight the framework, as what you're describing is exactly the nature of being asynchronous. (And do not use a synchronous request on the main thread, since that blocks the main thread, which is a no-no.)
Indeed, in an event-driven framework, the very notion "wait until" is anathema.
Why not to use synchronous request?
Wrap your asynchronous NSURLConnection request in a helper method which has a completion block as a parameter:
-(void) asyncDoSomething:(void(^)(id result)completionHandler ;
This method should be implemented in the NSURLConnectionDelegate. For details see the example implementation and comments below.
Elsewhere, in your action method:
Set the completion handler. The block will dispatch further on the main thread, and then perform anything appropriate to update the table data, unless the result was an error, in which case you should display an alert.
- (IBAction) recordButtonPressed
{
[someController asyncConnectionRequst:^(id result){
if (![result isKindOfClass:[NSError class]]) {
dispatch_async(dispatch_get_main_queue(), ^{
// We are on the main thread!
someController.tableData = result;
});
}
}];
}
The Implementation of the method asyncConnectionRequst: could work as follows: take the block and hold it in an ivar. When it is appropriate call it with the correct parameter. However, having blocks as ivars or properties will increase the risk to inadvertently introduce circular references.
But, there is a better way: a wrapper block will be immediately dispatched to a suspended serial dispatch queue - which is hold as an ivar. Since the queue is suspended, they will not execute any blocks. Only until after the queue will be resumed, the block executes. You resume the queue in your connectionDidFinish: and connectionDidFailWithError: (see below):
In your NSURLConnectionDelegate:
-(void) asyncConnectionRequst:(void(^)(id result)completionHandler
{
// Setup and start the connection:
self.connection = ...
if (!self.connection) {
NSError* error = [[NSError alloc] initWithDomain:#"Me"
code:-1234
userInfo:#{NSLocalizedDescriptionKey: #"Could not create NSURLConnection"}];
completionHandler(error);
});
return;
}
dispatch_suspend(self.handlerQueue); // a serial dispatch queue, now suspended
dispatch_async(self.handlerQueue, ^{
completionHandler(self.result);
});
[self.connection start];
}
Then in the NSURLConnectionDelegate, dispatch a the handler and resume the
handler queue:
- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
self.result = self.responseData;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
Likewise when an error occurred:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.result = error;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
There are even better ways, which however involve a few more basic helper classes which deal with asynchronous architectures which at the end of the day make your async code look like it were synchronous:
-(void) doFourTasksInAChainWith:(id)input
{
// This runs completely asynchronous!
self.promise = [self asyncWith:input]
.then(^(id result1){return [self auth:result1]);}, nil)
.then(^(id result2){return [self fetch:result2];}, nil)
.then(^(id result3){return [self parse:result3];}, nil)
.then(^(id result){ self.tableView.data = result; return nil;}, ^id(NSError* error){ ... })
// later eventually, self.promise.get should contain the final result
}

How can I catch EPIPE in my NSFIleHandle handling?

I'm having a problem with EPIPE in my iOS app, and it's not being caught in the #try/#catch/#finally block. How can I catch this signal (SIGPIPE, likely)...
I've built a "web proxy" into my app that will handle certain kinds of URLs - in this error case, it seems that the remote end (also in my app, but hiding in the iOS libraries) closes its end of the socket. I don't get a notification (should I? Is there something I should register for with the NSFileHandle that might help here?).
I've based this proxy on HTTPServer that Matt Gallagher put together (available here), and the problem is in a subclass of the HTTPRequestHandler class he put together. Here's the code (this code is the equivalent of the startResponse method in the base class):
-(void)proxyTS:(SSProxyTSResource *)proxyTS didReceiveResource:(NSData *)resource
{
NSLog(#"[%# %#]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
CFHTTPMessageRef response =
CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(response,
(CFStringRef)#"Content-Type",
(__bridge CFStringRef)s_MIMEtype);
CFHTTPMessageSetHeaderFieldValue(response,
(CFStringRef)#"Connection",
(CFStringRef)#"close");
CFHTTPMessageSetBody(response,
(__bridge CFDataRef)resource);
CFDataRef headerData = CFHTTPMessageCopySerializedMessage(response);
#try
{
NSLog(#" -> writing %u bytes to filehandle...",[((__bridge NSData *)headerData) length]);
[self.fileHandle writeData:(__bridge NSData *)headerData];
}
#catch (NSException *exception)
{
// Ignore the exception, it normally just means the client
// closed the connection from the other end.
}
#finally
{
NSLog(#" *ding*");
CFRelease(headerData);
CFRelease(response);
[self.server closeHandler:self];
}
}
And here's what shows up in the console log when it crashes:
Jan 15 14:55:10 AWT-NoTouch-iPhone-1 Streamer[1788] <Warning>: [SSProxyTSResponseHandler proxyTS:didReceiveResource:]
Jan 15 14:55:10 iPhone-1 Streamer[1788] <Warning>: -> writing 261760 bytes to filehandle...
Jan 15 14:55:11 iPhone-1 com.apple.launchd[1] (UIKitApplication:com.XXX.Streamer[0xf58][1788]) <Warning>: (UIKitApplication:com.XXX.Streamer[0xf58]) Exited abnormally: Broken pipe: 13
It seems that because the other end closed the pipe the write() fails, so if someone can point me at how I can either discover that it's already closed and not try to write data to it OR whatever will make it not crash my program that would be very helpful.
The immediate problem of crashing with SIGPIPE is solved. I'm not entirely giggly about this solution, but at least the app doesn't crash. It's not clear that it's working 100% correctly, but it does seem to be behaving quite a bit better.
I've resolved this issue by examining further what's going on. In doing some research, I found that perhaps I should be using NSFileHandle's writeabilityHandler property to install a block to do the writing. I'm not fully sold on that approach (it felt convoluted to me), but it might help.
Writability-handler solution:
In doing some web searching on writeabilityHandler, I stumbled on Bert Leung's blog entry on some issues he was having in a similar area. I took his code and modified it as follows, replacing the #try/#catch/#finally block above with this code:
self.pendingData = [NSMutableData dataWithData:(__bridge NSData *)(headerData)];
CFRelease(headerData);
CFRelease(response);
self.fileHandle.writeabilityHandler = ^(NSFileHandle* thisFileHandle)
{
int amountSent = send([thisFileHandle fileDescriptor],
[self.pendingData bytes],
[self.pendingData length],
MSG_DONTWAIT);
if (amountSent < 0) {
// errno is provided by system
NSLog(#"[%# %#] Error while sending response: %d", NSStringFromClass([self class]), NSStringFromSelector(_cmd), errno);
// Setting the length to 0 will cause this handler to complete processing.
self.pendingData.length = 0;
} else {
[self.pendingData replaceBytesInRange:NSMakeRange(0, amountSent)
withBytes:NULL
length:0];
}
if ([self.pendingData length] == 0) {
thisFileHandle.writeabilityHandler = nil;
// Hack to avoid ARC cycle with self. I don't like this, but...
[[NSNotificationCenter defaultCenter] postNotification:self.myNotification];
}
};
That worked fine but it didn't solve the problem. I was still getting SIGPIPE/EPIPE.
SIGPIPE be gone!
This wasn't a surprise, exactly, as this does pretty much the same thing as the former writeData: did but does it using send() instead. The key difference though is that using send() allows errno to be set. This was quite helpful, actually - I was getting a couple of error codes (in errno), such as 54 (Connection reset by peer) and 32 (Broken pipe). The 54's were fine, but the 32's resulted in the SIGPIPE/EPIPE. Then it dawned on me - perhaps I should just ignore SIGPIPE.
Given that thought, I added a couple of hooks into my UIApplicationDelegate in application:didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[self installSignalHandlers];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
...
and applicationWillTerminate::
- (void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self removeSignalHandlers];
[self saveContext];
}
-(void)installSignalHandlers
{
signal(SIGPIPE,SIG_IGN);
}
-(void)removeSignalHandlers
{
signal(SIGPIPE, SIG_DFL);
}
Now at least the app doesn't crash. It's not clear that it's working 100% correctly, but it does seem to be behaving.
I also switched back to the #try/#catch/#finally structure because it's more direct. Further, after ignoring SIGPIPE, the #catch block does get triggered. Right now, I'm logging the exception, but only so I can see that it's working. In the released code, that log will be disabled.

Resources