I've been killing myself over this bug/problem for a few days now, and I am going insane. I have no idea why it is break. I was wondering if you guys and gals can lend me a hand and rid of my insanity. So thank you in advance.
What I am trying to do: upload a .wav file to a wcf json web service (.net 4.0) from an iphone app (ios 4.3). I have verified that the service does work from a different client.
The problem: the problem is the same code on the iphone app worked 5 days ago. Yesterday and today the code decided to not work. Nothing had changed on the service side and the iphone app.
I'll post as little code as I can to keep things relevant and simple to the topic. If there is a need to post more code to make it easier for you all, I will.
I am consistently getting status code 400 back from the response in the didReceiveResponse method. I've check the url which i post to many times, and the url seem valid to me.
The size of the file which I am posting to the json web service is 1KB < fileSize < 450KB.
here is a sample url that i post to:
http://random-ec2-id-east-1.elb.amazonaws.com/sampleApp/?key=ee404d54-ea45-421a-9633-1ea35c0c211e&token=zJSRqiZgmU6nOW44CeAzhWYxasdD0158yysNDCiASMk.eyJpdiI6IlU3Y2UwbWNXVGN6WVVBLU42SDVieGcifQ.kLbcRPOJ_QnrrtsBe-zF2-2IIbAffArvqeyAmwp_OpOWAoADMugHYjTPcnjkjQvzxEIMcm2k3933i3GqF2YFhAFDtItwvqre5fIGlixbuwsYhrVCm9FBoue4dCQ_pPX-yjUtq_898FGWa5INl0RG0A&type=c&platform=i
// ################################
- (id)init {
self = [super init];
if (self) {
self.sampleAppInstance = [sampleAppInstance sharedSampleAppInstance];
self.sampleAppUrl = #"http://random-ec2-id.us-east-1.elb.amazonaws.com/sampleApp/";
self.key = #"ee404d54-ea45-421a-9633-1ea35c0c211e";
self.token = self.sampleAppInstance.facebook.accessToken; // facebook access token
self.type = #"c";
self.platform = #"i";
}
return self;
}
// #################################
- (BOOL) save:(NSString *)_fileName {
NSString *url = [NSString stringWithFormat:#"%#?key=%#&token=%#&type=%#&platform=%#", self.sampleAppUrl, self.key, self.token, self.type, self.platform];
NSData *voiceData = [NSData dataWithContentsOfFile:_fileName];
//NSLog(#"%#", url);
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]] autorelease];
[request setValue:#"text/plain" forHTTPHeaderField:#"content-type"];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [voiceData length]] forHTTPHeaderField:#"content-length"];
[request setHTTPBody:voiceData];
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
return FALSE;
}
// #################################
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
//NSLog(#"did failed");
[self.delegate responseDidFailWithError:error];
}
// #################################
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//NSLog(#"did receive data");
//NSLog(#"%#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
[self.delegate responseDidReceive:data];
}
// #################################
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
int statusCode = [httpResponse statusCode];
// if get 400, then malform syntax
//NSLog(#"%d", statusCode);
if (statusCode != 200)
[self.delegate responseDidFailWithStatusCode:statusCode];
}
it turns out that my service only accept file size < 65k by default. the fix was to configure my service to accept file > 65k.
Related
I Have a NSURLConnection that has been working for a while and all of a sudden is not working.
For some reason, the only delegate method that gets called is:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:
None of the other delegate methods get called. I have other classes which uses pretty much the same code, just a different request and url and they all seem to work fine. I have read alot of post that talk about making sure the connection is on the same thread as the delegate etc but nothing seems to work for me. I know the server is returning a response because if I pass the same information through a simple html form I get a response in my browser, and I can see evidence that my server side script is running because I can see the changes it is making in the SQL data. And the app is getting some sort of resonse, its just not getting any data or calling the connectionDidFinishLoading delegate method.
Any ideas of what the problem might be?
Here is a simplified version on my code:
#import "RegistrationViewController.h"
#interface RegistrationViewController ()
#end
NSMutableData *responseData;
NSURLConnection *theconnection;
#implementation RegistrationViewController
/*
* Attempt to Register online. Returns False if valiation failed
*/
- (BOOL) registerOnline{
// OTHER CODE HERE TO BUILD DATA FOR THE REQUEST
// URL Request
NSURL *requestUrl = [NSURL URLWithString:THEURL];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:requestUrl];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:[NSData dataWithBytes:[bodyData UTF8String] length:strlen([bodyData UTF8String])]];
// Initialse Response Object
responseData = [[NSMutableData alloc] init];
// Conection
theconnection = [NSURLConnection connectionWithRequest:request delegate:self];
[theconnection start];
return YES;
}
/*
* Handle the event of registration failing
*/
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"didFailWithError");
// OTHER CODE HERE TO HANDLE THE ERROR....
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"did receive response ");
}
/*
* Handle the reciept of Data
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(#"didReceiveData");
// Add data to the response
[responseData appendData:data];\
}
/*
* Data finnished loading
*/
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
// OTHER CODE HERE TO HANDLE THE RESPONSE....
}
You should look at the response and diagnose what's going on. For example, if statusCode is not 200, you might have a problem. See the HTTP/1.1 Status Code Definitions.
So, you might check the statusCode:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode == 200) {
NSLog(#"received successful (200) response ");
} else {
NSLog(#"whoops, something wrong, received status code of %d", statusCode);
}
} else {
NSLog(#"Not a HTTP response");
}
}
You're also calling:
theconnection = [NSURLConnection connectionWithRequest:request delegate:self];
That starts the connection automatically. By calling
[theconnection start];
you're starting it a second time. Remove the start method, or use initWithRequest:delegate:startImmediately: with NO for that final parameter.
If it can help anybody, the problem for me was that i was starting the connection in an another thread
So be sure to call
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
or
theconnection = [NSURLConnection connectionWithRequest:request delegate:self];
[theconnection start];
in the main thread.
id like to achive what is mentioned in the title, can anyone point me in the right direction regarding ressources or torturials? I do understand the basics of the HTTP protocol, but i am fairly new to OS X programming.
In fact you can use the NSMutableURLRequest, if you want to make a test to start you can do this:
//test.h
#import <Foundation/Foundation.h>
#interface test : NSObject<NSURLConnectionDataDelegate>{
NSMutableData* _responseData;
}
//test.m
#implementation test
//Just call this method to start the request.
-(void)testRequest{
//set request
NSURL url = [NSURL URLWithString:#"http://ip/file.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLCacheStorageNotAllowed
timeoutInterval:20.0];
//Start the request
NSURLConnection * connection;
connection = [[NSURLConnection alloc] initWithRequest: request delegate:self];
}
after this you have to implement all the methods as woz said but catching the response:
#pragma mark - NSURLConectionDlegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
_responseData = [[NSMutableData alloc] init];
}
//Receive data from the server
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable
[_responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
//in this method you can check the response.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
NSString *receivedDataString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
NSLog(#"this is reponse: %#",receivedDataString);
}
server side
//file.php
echo "hello";
I like short solutions, and using blocks.
- (void)sendRequestWithURL:(NSURL*) url {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
NSLog(#"%#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
else {
///log error
}
}];
}
I am developing a very simple application which accesses a written url. So i am wondering what is the difference between access by nsurlconnection and access by just using browser. cause some sites respond but they don`t send data when i used the nsurlconnection.
- (void)getWikiData:(NSString *)keyword{
NSString* tmpURL = #"http://wikipedia.simpleapi.net/api?keyword=";
NSString* encodedString;
CFStringRef strRef = CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)keyword, NULL, (CFStringRef)#"!*'();:#&=+$,/?%#[]~", kCFStringEncodingUTF8);
encodedString = [NSString stringWithString:(NSString *)strRef];
CFRelease(strRef);
[tmpURL stringByAppendingString:encodedString];
[tmpURL stringByAppendingString:#"&output=html"];
NSURL *url = [NSURL URLWithString:tmpURL];
NSString *userAgent = #"Custom User Agent";
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url] autorelease];
[request setValue:userAgent forHTTPHeaderField:#"User-Agent"];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response{
NSLog(#"Receive Response");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(#"Receive Data");
}
Thanks in advance.
The difference is in the user agent string of the resulting application. MobileSafari reports itself as "Safari, iOS like Mac OS X", however, a plain NSURLConnection sends a CFNetwork description, which is not very useful for most sites to do 'browser' (rather 'client') detection, that's why they may refuse to send data to an unrecognized user agent.
I have a basecamp account which I'm trying to access via its XML API.
I have created a NSURL Request with proper header fields.
NSURL* projectsUrl = [NSURL URLWithString:[self.accountURL stringByAppendingString:#"/projects.xml"]];
NSMutableURLRequest* projectsRequest = [NSMutableURLRequest requestWithURL:projectsUrl];
[projectsRequest setValue:#"application/xml" forHTTPHeaderField:#"Content-Type"];
[projectsRequest setValue:#"application/xml" forHTTPHeaderField:#"Accept"];
I create a connection and start it.
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:projectsRequest delegate:self];
[connection start];
[connection autorelease];
Basecamp uses HTTP basic authentication. So I use a NSCredential object to handle it as below.
-(void) connection:(NSURLConnection*)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)authenticationChallenge
{
if ([authenticationChallenge previousFailureCount] <=2 ) {
NSURLCredential* basecampCredentials;
basecampCredentials = [NSURLCredential credentialWithUser:[self username]
password:[self password]
persistence:NSURLCredentialPersistenceNone];
[[authenticationChallenge sender] useCredential:basecampCredentials forAuthenticationChallenge:authenticationChallenge];
}else {
[[authenticationChallenge sender] cancelAuthenticationChallenge:authenticationChallenge];
}
}
And when I receive data from connection i handle it with these delegates.
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[[self responseData] appendData: data];
NSLog(#"I received %#", [self responseData]);
}
-(void) connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(#"I FINALLY received %#", [self responseData]);
NSXMLParser* parser = [[NSXMLParser alloc] initWithData:[self responseData]];
[parser setDelegate:self];
BOOL success = [parser parse];
if (success) {
NSLog(#"XML parse success");
}else {
NSLog(#"XML parse ERROR");
}
[parser autorelease];
}
The problem is even after using right urls, authentication credentials and header fields I get null data in responseData whereas when I try to access the same url with curl I get proper xml response from the basecamp server. I'm new to objective C. Please explain and tell me what else should I set to get this right.
Before starting connection ([connection start];) you should initialise property responseData, for example, self.responseData = [[NSMutableData alloc] init];.
In method - (void)connection:(NSURLConnection *)conn didReceiveResponse:(NSURLResponse *)response you should set the length of that data to zero : [self.responseData setLength:0];
Are you sure you set [self responseData]?
Try the same URL with an ASIHTTPRequest and see if it works.
I am writing a program in Objective-C and I need to make web requests to web server, but asynchronously and I am fairly new on mac, I am very good at windows technologies, but I need to know that if I use NSOperation (introduced in 10.5, i am assuming that it will not run in 10.4 MAC?), or if it was implemented such that it utilizes system threading which will be available on 10.4?
Or I should create a new thread and create a new runloop, also how to use cookies etc, if anyone can give me one small example, that will be of great help. I want this sample to run on mac 10.4 too if possible.
There's a good example of using NSURLRequest and NSHTTPCookies to do a full web application example of logging into a website, storing the SessionID cookie and resubmitting it in future requests.
NSURLConnection, NSHTTPCookie
By logix812:
NSHTTPURLResponse * response;
NSError * error;
NSMutableURLRequest * request;
request = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://temp/gomh/authenticate.py?setCookie=1"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60] autorelease];
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"RESPONSE HEADERS: \n%#", [response allHeaderFields]);
// If you want to get all of the cookies:
NSArray * all = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:#"http://temp"]];
NSLog(#"How many Cookies: %d", all.count);
// Store the cookies:
// NSHTTPCookieStorage is a Singleton.
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:all forURL:[NSURL URLWithString:#"http://temp"] mainDocumentURL:nil];
// Now we can print all of the cookies we have:
for (NSHTTPCookie *cookie in all)
NSLog(#"Name: %# : Value: %#, Expires: %#", cookie.name, cookie.value, cookie.expiresDate);
// Now lets go back the other way. We want the server to know we have some cookies available:
// this availableCookies array is going to be the same as the 'all' array above. We could
// have just used the 'all' array, but this shows you how to get the cookies back from the singleton.
NSArray * availableCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:#"http://temp"]];
NSDictionary * headers = [NSHTTPCookie requestHeaderFieldsWithCookies:availableCookies];
// we are just recycling the original request
[request setAllHTTPHeaderFields:headers];
request.URL = [NSURL URLWithString:#"http://temp/gomh/authenticate.py"];
error = nil;
response = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"The server saw:\n%#", [[[NSString alloc] initWithData:data encoding: NSASCIIStringEncoding] autorelease]);
For asynchronous requests, you need to use NSURLConnection.
For cookies, see NSHTTPCookie and NSHTTPCookieStorage.
UPDATE:
The code below is real, working code from one of my applications. responseData is defined as NSMutableData* in the class interface.
- (void)load {
NSURL *myURL = [NSURL URLWithString:#"http://stackoverflow.com/"];
NSURLRequest *request = [NSURLRequest requestWithURL:myURL
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[responseData release];
[connection release];
// Show error message
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Use responseData
[responseData release];
[connection release];
}
I am able to fetch cookie in such way:
NSArray* arr = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString: #"http://google.com" ]];
This one works fine for asynchronous request as well.