Help: EXC_BAD_INSTRUCTION with RESTful NSURLConnection

Discussion in 'iOS Programming' started by huanlai, Sep 14, 2009.

  1. huanlai macrumors newbie

    Joined:
    Sep 14, 2009
    #1
    Hello,

    I am currently using Andrian Kosmaczewski's REST client (http://kosmaczewski.net/projects/objective-c-rest-client/), which uses NSURLConnection and NSURLRequest to access a RESTful web service. (I am currently using BASIC authentication through https.) My app currently works perfectly fine when running on iPhone 2.2.1 and 3.0 (both simulator and device), but crashes when running on 3.1 (also both simulator and device).

    The error that is given in the debugger is:

    Code:
    objc[4701]: FREED(id): message release sent to freed object=0x3da1ca0
    Program received signal:  “EXC_BAD_INSTRUCTION”.
    
    Even after setting NSZombiesEnabled, this is all I am able to get from the stack trace. For this thread, as well as all other threads, the stack trade does not include anything that is part of my own code:

    Code:
    #0    0x93078bfa in _objc_error
    #1    0x93078c30 in __objc_error
    #2    0x93077637 in _freedHandler
    #3    0x00285cf5 in NSPopAutoreleasePool
    #4    0x00ccce71 in URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload
    #5    0x00ccda96 in URLConnectionClient::processEvents
    #6    0x00c7a7d5 in MultiplexerSource::perform
    #7    0x007b78e1 in CFRunLoopRunSpecific
    #8    0x007b6c48 in CFRunLoopRunInMode
    #9    0x000167ad in GSEventRunModal
    #10    0x00016872 in GSEventRun
    #11    0x0168a003 in UIApplicationMain
    #12    0x0000265a in main at main.m:14
    
    I have tried to create a brand new, barebones app that has none of the logic and essentually just tries to log into the web service and I am still receiving the same errors.

    Here is the code chunk that instanciates and calls the wrapper. I am positive that the username, password and web service url are correct, because the behavior is correct when I run using 3.0 or 2.2.1:

    Code:
    
         // Initialize the REST engine
        if(engine == nil) {
            engine = [[Wrapper alloc] init];
        }
       
        // Configure the connection with user input and the api key
        NSString *parameters = nil;
        engine.delegate = self;
        engine.username = *username*
        engine.password = *password*
    
     
    
        NSURL *url = [NSURL URLWithString: *web service url*];
        [engine sendRequestTo:url usingVerb: @"GET" withParameters: parameters]; 
    
     
    Setting breakpoints as well as NSLogs, it seems that it runs through that code chunk just fine. It also goes through the Wrapper library's sendRequestTo, startConnection and didReceiveAuthenticationChallenge functions just fine. But it never reaches didReceiveResponse, didReceiveData, didFailWithError or connectionDidFinishLoading. It seems to just die off somewhere after the authentication challenge, but before a response is received.

    Any pointers or advice would be greatly appreciated.

    Thank you,
    Huan
     
  2. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #2
    The log is telling you that an object has been over-released. The crash comes when an autorelease pool is being drained and this object was added to the autorelease pool but was dealloced sometime after that and before the pool was drained.

    Is URLConnectionClient part of the REST library that you're using? If so the bug is probably there.

    You might be able to track down the object that's being over-released with ObjectAlloc. It has a history of every object so when you get the crash you could look up the object and see where it was created, released, autoreleased.
     
  3. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #3
    Yes, the URLConnectionClient is part of the library that I am using.
    It seems the bug is definitely a problem with the library. Although I'm a bit curious as to why this bug comes up consistently with 3.1 but never before with 2.2.1 and 3.0...
    I'll look through the library's source code to see if I can find anything. If anyone is interested in looking at this with me :), here it is (I take no credit for it, this is all Adrian Kosmaczewski's work in public domain):

    Wrapper.h :
    Code:
    //
    // Wrapper.h
    // WrapperTest
    //
    // Created by Adrian on 10/18/08.
    // Copyright 2008 Adrian Kosmaczewski. All rights reserved.
    //
     
    #import <Foundation/Foundation.h>
    #import "WrapperDelegate.h"
     
    @interface Wrapper : NSObject
    {
    @private
        NSMutableData *receivedData;
        NSString *mimeType;
        NSURLConnection *conn;
        BOOL asynchronous;
        NSObject<WrapperDelegate> *delegate;
        NSString *username;
        NSString *password;
    }
     
    @property (nonatomic, readonly) NSData *receivedData;
    @property (nonatomic) BOOL asynchronous;
    @property (nonatomic, copy) NSString *mimeType;
    @property (nonatomic, copy) NSString *username;
    @property (nonatomic, copy) NSString *password;
    @property (nonatomic, assign) NSObject<WrapperDelegate> *delegate; // Do not retain delegates!
     
    - (void)sendRequestTo:(NSURL *)url usingVerb:(NSString *)verb withParameters:(NSDictionary *)parameters;
    - (void)uploadData:(NSData *)data toURL:(NSURL *)url;
    - (void)cancelConnection;
    - (NSDictionary *)responseAsPropertyList;
    - (NSString *)responseAsText;
     
    @end
    
    Wrapper.m :
    Code:
    //
    // Wrapper.m
    // WrapperTest
    //
    // Created by Adrian on 10/18/08.
    // Copyright 2008 Adrian Kosmaczewski. All rights reserved.
    //
     
    #import "Wrapper.h"
     
    @interface Wrapper (Private)
    - (void)startConnection:(NSURLRequest *)request;
    @end
     
    @implementation Wrapper
     
    @synthesize receivedData;
    @synthesize asynchronous;
    @synthesize mimeType;
    @synthesize username;
    @synthesize password;
    @synthesize delegate;
     
    #pragma mark -
    #pragma mark Constructor and destructor
     
    - (id)init
    {
        if(self = [super init])
        {
            receivedData = [[NSMutableData alloc] init];
            conn = nil;
     
            asynchronous = YES;
            mimeType = @"text/html";
            delegate = nil;
            username = @"";
            password = @"";
        }
     
        return self;
    }
     
    - (void)dealloc
    {
        [receivedData release];
        receivedData = nil;
        self.mimeType = nil;
        self.username = nil;
        self.password = nil;
        [super dealloc];
    }
     
    #pragma mark -
    #pragma mark Public methods
     
    - (void)sendRequestTo:(NSURL *)url usingVerb:(NSString *)verb withParameters:(NSDictionary *)parameters
    {
        NSData *body = nil;
        NSMutableString *params = nil;
        NSString *contentType = @"text/html; charset=utf-8";
        NSURL *finalURL = url;
        if (parameters != nil)
        {
            params = [[NSMutableString alloc] init];
            for (id key in parameters)
            {
                NSString *encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
                CFStringRef value = (CFStringRef)[[parameters objectForKey:key] copy];
                // Escape even the "reserved" characters for URLs
                // as defined in http://www.ietf.org/rfc/rfc2396.txt
                CFStringRef encodedValue = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                   value,
                                                                                   NULL,
                                                                                   (CFStringRef)@";/?:@&=+$,",
                                                                                   kCFStringEncodingUTF8);
                [params appendFormat:@"%@=%@&", encodedKey, encodedValue];
                CFRelease(value);
                CFRelease(encodedValue);
            }
            [params deleteCharactersInRange:NSMakeRange([params length] - 1, 1)];
        }
        
        if ([verb isEqualToString:@"POST"] || [verb isEqualToString:@"PUT"])
        {
            contentType = @"application/x-www-form-urlencoded; charset=utf-8";
            body = [params dataUsingEncoding:NSUTF8StringEncoding];
        }
        else
        {
            if (parameters != nil)
            {
                NSString *urlWithParams = [[url absoluteString] stringByAppendingFormat:@"?%@", params];
                finalURL = [NSURL URLWithString:urlWithParams];
            }
        }
     
        NSMutableDictionary* headers = [[[NSMutableDictionary alloc] init] autorelease];
        [headers setValue:contentType forKey:@"Content-Type"];
        [headers setValue:mimeType forKey:@"Accept"];
        [headers setValue:@"no-cache" forKey:@"Cache-Control"];
        [headers setValue:@"no-cache" forKey:@"Pragma"];
        [headers setValue:@"close" forKey:@"Connection"]; // Avoid HTTP 1.1 "keep alive" for the connection
     
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:finalURL
                                                               cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                           timeoutInterval:60.0];
        [request setHTTPMethod:verb];
        [request setAllHTTPHeaderFields:headers];
        if (parameters != nil)
        {
            [request setHTTPBody:body];
        }
        [params release];
        [self startConnection:request];
    }
     
    - (void)uploadData:(NSData *)data toURL:(NSURL *)url
    {
        // File upload code adapted from http://www.cocoadev.com/index.pl?HTTPFileUpload
        // and http://www.cocoadev.com/index.pl?HTTPFileUploadSample
     
        NSString* stringBoundary = [NSString stringWithString:@"0xKhTmLbOuNdArY"];
        
        NSMutableDictionary* headers = [[[NSMutableDictionary alloc] init] autorelease];
        [headers setValue:@"no-cache" forKey:@"Cache-Control"];
        [headers setValue:@"no-cache" forKey:@"Pragma"];
        [headers setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", stringBoundary] forKey:@"Content-Type"];
        
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url
                                                               cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                           timeoutInterval:60.0];
        [request setHTTPMethod:@"POST"];
        [request setAllHTTPHeaderFields:headers];
        
        NSMutableData* postData = [NSMutableData dataWithCapacity:[data length] + 512];
        [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [postData appendData:[@"Content-Disposition: form-data; name=\"image\"; filename=\"test.bin\"\r\n\r\n"
                              dataUsingEncoding:NSUTF8StringEncoding]];
        [postData appendData:data];
        [postData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [request setHTTPBody:postData];
        
        [self startConnection:request];
    }
     
    - (void)cancelConnection
    {
        [conn cancel];
        [conn release];
        conn = nil;
    }
     
    - (NSDictionary *)responseAsPropertyList
    {
        NSString *errorStr = nil;
        NSPropertyListFormat format;
        NSDictionary *propertyList = [NSPropertyListSerialization propertyListFromData:receivedData
                                                                      mutabilityOption:NSPropertyListImmutable
                                                                                format:&format
                                                                      errorDescription:&errorStr];
        [errorStr release];
        return propertyList;
    }
     
    - (NSString *)responseAsText
    {
        return [[[NSString alloc] initWithData:receivedData
                                     encoding:NSUTF8StringEncoding] autorelease];
    }
     
    #pragma mark -
    #pragma mark Private methods
     
    - (void)startConnection:(NSURLRequest *)request
    {
        if (asynchronous)
        {
            [self cancelConnection];
            conn = [[NSURLConnection alloc] initWithRequest:request
                                                   delegate:self
                                           startImmediately:YES];
            
            if (!conn)
            {
                if ([delegate respondsToSelector:@selector(wrapper:didFailWithError:)])
                {
                    NSMutableDictionary* info = [NSMutableDictionary dictionaryWithObject:[request URL] forKey:NSErrorFailingURLStringKey];
                    [info setObject:@"Could not open connection" forKey:NSLocalizedDescriptionKey];
                    NSError* error = [NSError errorWithDomain:@"Wrapper" code:1 userInfo:info];
                    [delegate wrapper:self didFailWithError:error];
                }
            }
        }
        else
        {
            NSURLResponse* response = [[NSURLResponse alloc] init];
            NSError* error = [[NSError alloc] init];
            NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
            [receivedData setData:data];
            [response release];
            response = nil;
            [error release];
            error = nil;
        }
    }
     
    #pragma mark -
    #pragma mark NSURLConnection delegate methods
     
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        NSInteger count = [challenge previousFailureCount];
        if (count == 0)
        {
            NSURLCredential* credential = [[NSURLCredential credentialWithUser:username
                                                                      password:password
                                                                   persistence:NSURLCredentialPersistenceNone] autorelease];
            [[challenge sender] useCredential:credential
                   forAuthenticationChallenge:challenge];
        }
        else
        {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
            if ([delegate respondsToSelector:@selector(wrapperHasBadCredentials:)])
            {
                [delegate wrapperHasBadCredentials:self];
            }
        }
    }
     
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
        int statusCode = [httpResponse statusCode];
        switch (statusCode)
        {
            case 200:
                break;
     
            case 201:
            {
                NSString* url = [[httpResponse allHeaderFields] objectForKey:@"Location"];
                if ([delegate respondsToSelector:@selector(wrapper:didCreateResourceAtURL:)])
                {
                    [delegate wrapper:self didCreateResourceAtURL:url];
                }
                break;
            }
                
            // Here you could add more status code handling... for example 404 (not found),
            // 204 (after a PUT or a DELETE), 500 (server error), etc... with the
            // corresponding delegate methods called as required.
            
            default:
            {
                if ([delegate respondsToSelector:@selector(wrapper:didReceiveStatusCode:)])
                {
                    [delegate wrapper:self didReceiveStatusCode:statusCode];
                }
                break;
            }
        }
        [receivedData setLength:0];
    }
     
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        [receivedData appendData:data];
    }
     
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
        [self cancelConnection];
        if ([delegate respondsToSelector:@selector(wrapper:didFailWithError:)])
        {
            [delegate wrapper:self didFailWithError:error];
        }
    }
     
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        [self cancelConnection];
        if ([delegate respondsToSelector:@selector(wrapper:didRetrieveData:)])
        {
            [delegate wrapper:self didRetrieveData:receivedData];
        }
    }
     
    @end
    
    WrapperDelegate.h
    Code:
    //
    // WrapperDelegate.h
    // WrapperTest
    //
    // Created by Adrian on 10/18/08.
    // Copyright 2008 Adrian Kosmaczewski. All rights reserved.
    //
     
    #import <Foundation/Foundation.h>
     
    @class Wrapper;
     
    @protocol WrapperDelegate
     
    @required
    - (void)wrapper:(Wrapper *)wrapper didRetrieveData:(NSData *)data;
     
    @optional
    - (void)wrapperHasBadCredentials:(Wrapper *)wrapper;
    - (void)wrapper:(Wrapper *)wrapper didCreateResourceAtURL:(NSString *)url;
    - (void)wrapper:(Wrapper *)wrapper didFailWithError:(NSError *)error;
    - (void)wrapper:(Wrapper *)wrapper didReceiveStatusCode:(int)statusCode;
     
    @end
    
     
  4. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #4
    I have created a fresh project and this problem seems to still exist.
    I am no longer longer using the 3rd party library.
    I reimplemented the NSURLConnection calls basing on this tutorial: http://brandontreb.com/tag/nsurlcredential-example/ and I seem to be doing everything he is doing

    In my new project, in the viewDidLoad of the only view controller, this gets called:
    Code:
    Wrapper *restWrapper = [[Wrapper alloc] init];
    restWrapper.username = *username*
    restWrapper.password = *password*
         
    NSURL *url = [NSURL URLWithString:*webservice url*];
    [restWrapper sendRequestTo:url];
    

    The Wrapper class is implemented through two simple files:

    Wrapper.m
    Code:
    #import "Wrapper.h"
    
    @implementation Wrapper
    
    @synthesize receivedData;
    @synthesize username;
    @synthesize password;
    
    - (void)sendRequestTo:(NSURL *)url {
        theRequest = [NSMutableURLRequest requestWithURL:url];
         theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
         
         if (theConnection) {
              NSLog(@"Connection created");
              receivedData = [[NSMutableData data] retain];
         } else {
              NSLog(@"sentRequestTo failed to create connection");
         }
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
        if ([challenge previousFailureCount] == 0) {
              NSLog(@"Trying authentication");
            NSURLCredential* credential = [NSURLCredential credentialWithUser:[self username]
                                                                      password:[self password]
                                                                   persistence:NSURLCredentialPersistenceNone];
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
              [credential release];
        } else {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
            NSLog(@"Invalid username or password");
        }
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
         NSLog(@"didReceiveResponse");
         
         [receivedData setLength:0];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
         NSLog(@"didReceiveData");
         
        [receivedData appendData:data];
    }
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
         NSLog(@"didFailWithError");
         [theConnection release];
         [receivedData release];
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
         NSLog(@"didFinishLoading");
         
         [theConnection release];
         [receivedData release];
    }
    
    - (void)dealloc {
        [receivedData release];
         [username release];
         [password release];
        [super dealloc];
    }
    
    @end
    
    and Wrapper.h:
    Code:
    #import <Foundation/Foundation.h> 
    
    @interface Wrapper : NSObject {
        NSString *username;
        NSString *password;
         
        NSMutableData *receivedData;
        NSURLConnection *theConnection;
         NSMutableURLRequest *theRequest;
    }
    
    @property (nonatomic, retain) NSString *username;
    @property (nonatomic, retain) NSString *password;
    @property (nonatomic, retain) NSMutableData *receivedData;
    
    - (void)sendRequestTo:(NSURL *)url;
    
    @end
    
    Can anyone spot if I'm making any sort of mistake? The webservice I'm connecting to uses BASIC authentication over https.
    The username, password and url are all constants.

    When I build for the 3.0 simulator, it works just fine and this is the printout:
    Code:
    2009-09-14 16:46:24.888 ConnectionTest[2812:20b] Connection created
    2009-09-14 16:46:24.986 ConnectionTest[2812:20b] Trying authentication
    2009-09-14 16:46:25.061 ConnectionTest[2812:20b] didReceiveResponse
    2009-09-14 16:46:25.062 ConnectionTest[2812:20b] didReceiveData
    2009-09-14 16:46:25.064 ConnectionTest[2812:20b] didFinishLoading
    
    But when I build for the 3.1 simulator, this is the printout:
    Code:
    2009-09-14 16:50:02.406 ConnectionTest[2853:20b] Connection created
    2009-09-14 16:50:02.501 ConnectionTest[2853:20b] Trying authentication
    Program received signal:  “EXC_BAD_ACCESS”.
    
    Here is the stack trace:
    Code:
    #0     0x93088688 in objc_msgSend
    #1     0x00285cf5 in NSPopAutoreleasePool
    #2     0x00ccce71 in URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload
    #3     0x00ccda96 in URLConnectionClient::processEvents
    #4     0x00c7a7d5 in MultiplexerSource::perform
    #5     0x007b7197 in CFRunLoopRunSpecific
    #6     0x007b6c48 in CFRunLoopRunInMode
    #7     0x0001d7ad in GSEventRunModal
    #8     0x0001d872 in GSEventRun
    #9     0x0168a003 in UIApplicationMain
    #10     0x00001de8 in main at main.m:14
    
    Thank you,
    Huan Lai
     
  5. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #5
    Code:
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
        if ([challenge previousFailureCount] == 0) {
              NSLog(@"Trying authentication");
            NSURLCredential* credential = [NSURLCredential credentialWithUser:[self username]
                                                                      password:[self password]
                                                                   persistence:NSURLCredentialPersistenceNone];
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
              [B][credential release];[/B]
    
    Why are you releasing credential here? Comment this out and see what happens...
     
  6. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #6
    I'm releasing it there only because the tutorial did so. It crashes, but differently, when I comment it out.

    There printout stays the same:
    Code:
    2009-09-14 17:05:39.651 ConnectionTest[2945:20b] Connection created
    2009-09-14 17:05:39.702 ConnectionTest[2945:20b] Trying authentication
    [Switching to process 2945 thread 0x3c03]
    [Switching to process 2945 thread 0x3c03]
    Program received signal:  “EXC_BAD_ACCESS”.
    
    But the stack trace is different:
    Thread-1
    Code:
    #0	0xffff0292 in ___spin_lock at cpu_capabilities.h:234
    #1	??
    
    Thread-2
    Code:
    #0	0x93509286 in mach_msg_trap
    #1	0x93510a7c in mach_msg
    #2	0x007b7382 in CFRunLoopRunSpecific
    #3	0x007b6c48 in CFRunLoopRunInMode
    #4	0x02d6f803 in RunWebThread
    #5	0x9353a155 in _pthread_start
    #6	0x9353a012 in thread_start
    
    Thread-3
    Code:
    #0	0x007796c0 in CFStringGetLength
    #1	0x00787342 in CFStringCompare
    #2	0x00cbe6fe in HTTPConnectionCacheEntry::copyConnectionForProtocol
    #3	0x00cbcd04 in HTTPConnectionCache::CopyConnectionForProtocol
    #4	0x00cb8236 in HTTPProtocol::createStream
    #5	0x00cbadfe in HTTPProtocol::createAndOpenStream
    #6	0x00cbb531 in HTTPProtocol::useCredential
    #7	0x00cce7e9 in URLConnectionLoader::loaderUseCredential
    #8	0x00cceb2f in URLConnectionLoader::LoaderConnectionEventQueue::processAllEventsAndConsumePayload
    #9	0x00ccf0fb in URLConnectionLoader::processEvents
    #10	0x00c7a7d5 in MultiplexerSource::perform
    #11	0x007b78e1 in CFRunLoopRunSpecific
    #12	0x007b6c48 in CFRunLoopRunInMode
    #13	0x002df80e in +[NSURLConnection(NSURLConnectionReallyInternal) _resourceLoadLoop:]
    #14	0x00289f7d in -[NSThread main]
    #15	0x00289b18 in __NSThread__main__
    #16	0x9353a155 in _pthread_start
    #17	0x9353a012 in thread_start
    
    Thread-4
    Code:
    #0	0x935586fa in select$DARWIN_EXTSN
    #1	0x007f165e in __CFSocketManager
    #2	0x9353a155 in _pthread_start
    #3	0x9353a012 in thread_start
    


    When I try adding the credential to the autorelease pool, it crashes in the same way that it had originally
    Code:
    2009-09-14 17:08:35.751 ConnectionTest[2981:20b] Connection created
    2009-09-14 17:08:35.809 ConnectionTest[2981:20b] Trying authentication
    objc[2981]: FREED(id): message release sent to freed object=0x3d66ee0
    
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    Apparently URLConnectionClient is part of the system, not your library code.

    Anyway, dejo is right that your code was mishandling the credential object. Is that object valid when it's created?

    You don't say which thread crashed in #6. If it's thread 3 then the bad object is probably a string and it's probably part of the credentials you pass. Can you confirm that the strings you give to the credential object are valid when you create the credential object?

    Can you connect to a site that doesn't require login credentials and have that work with this same code? Say, http://www.apple.com/

    One other bug: In the code shown in #4 you release receivedData twice. In either didFailWithError or connectionDidFinishLoading and in dealloc. You should probably set the ivar to nil once it's released.
     
  8. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #8
    I have removed the releasing of credentials and am still crashing. Here is the new Wrapper.m code:
    Code:
    #import "Wrapper.h"
    
    @implementation Wrapper
    
    @synthesize receivedData;
    @synthesize username;
    @synthesize password;
    
    - (void)sendRequestTo:(NSURL *)url {
        theRequest = [NSMutableURLRequest requestWithURL:url];
         theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
         
         if (theConnection) {
              NSLog(@"Connection created");
              receivedData = [[NSMutableData data] retain];
         } else {
              NSLog(@"sentRequestTo failed to create connection");
         }
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
        if ([challenge previousFailureCount] == 0) {
              NSLog(@"Trying authentication");
              NSLog([self username]);
              NSLog([self password]);
            NSURLCredential* credential = [NSURLCredential credentialWithUser:[self username]
                                                                      password:[self password]
                                                                   persistence:NSURLCredentialPersistenceNone];
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        } else {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
            NSLog(@"Invalid username or password");
        }
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
         NSLog(@"didReceiveResponse");
         
         [receivedData setLength:0];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
         NSLog(@"didReceiveData");
         
        [receivedData appendData:data];
    }
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
         NSLog(@"didFailWithError");
         
         [theConnection release];
         [receivedData release];
         receivedData = nil;
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
         NSLog(@"didFinishLoading");
         
         [theConnection release];
         [receivedData release];
         receivedData = nil;
    }
    
    - (void)dealloc {
        [receivedData release];
         [username release];
         [password release];
        [super dealloc];
    }
    
    @end
    


    Here is the console printout:
    Code:
    2009-09-16 09:23:28.089 ConnectionTest[509:20b] Connection created
    2009-09-16 09:23:28.211 ConnectionTest[509:20b] Trying authentication
    2009-09-16 09:23:28.212 ConnectionTest[509:20b] *password*
    2009-09-16 09:23:28.212 ConnectionTest[509:20b] *username*
    ...
    Program received signal:  “EXC_BAD_ACCESS”.
    

    Here is the stack trace for the thread that crashed. My previous post has the other threads (sorry I forgot to mention which thread crashed there):
    Code:
    #0     0x0077e4c0 in CFStringGetCStringPtr
    #1     0x00788633 in CFStringCompareWithOptionsAndLocale
    #2     0x007873b5 in CFStringCompareWithOptions
    #3     0x00787370 in CFStringCompare
    #4     0x00cbe6fe in HTTPConnectionCacheEntry::copyConnectionForProtocol
    #5     0x00cbcd04 in HTTPConnectionCache::CopyConnectionForProtocol
    #6     0x00cb8236 in HTTPProtocol::createStream
    #7     0x00cbadfe in HTTPProtocol::createAndOpenStream
    #8     0x00cbb531 in HTTPProtocol::useCredential
    #9     0x00cce7e9 in URLConnectionLoader::loaderUseCredential
    #10     0x00cceb2f in URLConnectionLoader::LoaderConnectionEventQueue::processAllEventsAndConsumePayload
    #11     0x00ccf0fb in URLConnectionLoader::processEvents
    #12     0x00c7a7d5 in MultiplexerSource::perform
    #13     0x007b78e1 in CFRunLoopRunSpecific
    #14     0x007b6c48 in CFRunLoopRunInMode
    #15     0x002df80e in +[NSURLConnection(NSURLConnectionReallyInternal) _resourceLoadLoop:]
    #16     0x00289f7d in -[NSThread main]
    #17     0x00289b18 in __NSThread__main__
    #18     0x9353a155 in _pthread_start
    #19     0x9353a012 in thread_start
    
    The printout shows that the strings that I pass as the credentials are valid.

    Interestingly enough, I tried using this same code to connect to http://twitter.com/account/verify_credentials.xml and it works just fine, never crashes either with valid or invalid credentials.

    The webservice that I am currently connecting to is https://api.constantcontact.com/ws/huanlai/

    This is quite a mystery to me as to why this isn't working. I'm pretty sure that the webservice is working because I am able to connect to it just fine when I use 3.0 instead of 3.1.

    Both the constantcontact and twitter web services are using basic authentication. Does anyone have any ideas?

    One difference that I can spot is that the Twitter API returns straight XML whereas the constantcontact one returns application/atomsvc+xml.

    Could this be an issue?
     
  9. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #9
    So interesting discovery. If you go to the url that I am using any web browser: https://api.constantcontact.com/ws/customers/username/
    It will give you an authentication challenge. Put in a blank username and password. If you're on Safari 4.0.3 on Leopard, or Firefox on any OS, or MS Internet Explorer, it will work just fine, giving you a 401 return code. If you're on Safari 4.0.3 on Snow Leopard or Windows XP it crashs.

    This leads me to suspect there might be an issue that arose with some sort of lower level incompatibility between what my company's web services are doing and Apple's new Foundation frameworks that was introduced in Mac 10.6, iPhone OS 3.1 and the automated Cocoa port to Windows for Safari 4.0.3.
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    Maybe it's time to submit a bug report to Apple?
     
  11. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #11
    I've also been keeping this thread running on the apple developer site, will probably post a bug report as well.
    I'm talking to our web services guys to see if it might be something obscure on our end as well.

    In the meantime, since I doubt either apple or our web services guys will be able to give any solution immediately, does anyone have any suggestions for a possible work around? I would like to get this app out the door, it was already in the approval process when this bug arose.
     
  12. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #12
    Did you see this thread?

    https://devforums.apple.com/message/109867

    It provides a similar crash to yours and says the cause is the response from the web server. Of course the system shouldn't crash if it gets a bad response. MobileSafari also crashes with his server.

    You should try to look at the exact response from the server. I think you need a packet sniffer for that.

    Have you tried to pass the user/password in the URL?
     
  13. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #13
    Thank you for that link. I didn't know someone else was having this issue.

    I did try the username/pass in thr URL and it still crashes.

    I'll dig deeper into this and report anything that i find out.
     
  14. huanlai thread starter macrumors newbie

    Joined:
    Sep 14, 2009
    #14
    We've figured out the problem. It's related to specifying both OAuth and Basic methods of WWW-Authentication; to avoid the problem, only specify one of those. The bug has been filed as Radar #7235041. Thanks again for all of your help.
     
  15. waanz macrumors newbie

    Joined:
    Oct 31, 2007
    #15
    What's your workaround for that. I have the exact same issue. Where to you specify the authentication ? you mean on the server side ? I use this to connect to a third party (plaxo) which offer both basic http and Oauth but I can't change their server.
     

Share This Page