I am working with an azure API endpoint. ....azure-api.net/.... When I try to view a HTTP request / response using Charles proxy, the HTTP response is empty. The request works when I turn the proxy off.
I want to intercept the requests and mock the response for the purposes of automation tests.
I can view other HTTPs end-points to other servers using Charles proxy. So I believe there is something special about azure that is preventing the request from completing.
How does azure know there is a proxy in the middle and it is not talking to the client?
Is there anyway to configure the azure API to allow Charles to work? (Since viewing HTTP traffic is useful for development)
Is there another method that will allow the traffic to azure to be mocked? E.g. host redirect?
I am using standard iOS networking code
NSURL *url = [NSURL URLWithString:#"https://MyDomain.azure-api.net/a/b/2?subscription-key=myKey"];
[[[NSURLSession sharedSession] dataTaskWithRequest:[NSURLRequest requestWithURL:url]
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"%#, error: %#", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding], error);
;
}] resume];
Below are screenshots from Charles Proxy. There is no response after the connect request.
In order to intercept the request and return a fake response, you could use OHHTTPStubs
To be enable to do it, you should either log the response when debugging from Xcode or use the documentation.
Before you execute your test, call
stub = [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.absoluteString isEqualToString:#"https://MyDomain.azure-api.net/a/b/2?subscription-key=myKey"];
}
withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
NSString *fixture = OHPathForFileInBundle(#"fakeResponse.json", nil);
return [OHHTTPStubsResponse responseWithFileAtPath:fixture
statusCode:200
headers:#{#"Content-Type":#"text/json"}];
}];
And after you execute your test you should call
[OHHTTPStubs removeStub:stub];
"fakeResponse.json" must be a file added only to your test target and you can have different tests with different JSON files. For example you can pass a JSON with an error or empty content to test if your application behaves the way you want it to behave.
Related
We ran into the following issue with our app that uses AFNetworking 2.0.
When using AFHTTPRequestOperationManager's GET method, we got an error NSURLErrorDomain code -1012. The request used HTTPS and the server does not require user authentication. The request never reached the server by the way.
We have run several tests and this is the first time the error was produced and we are wondering how this error can get produced because it does not seem relevant.
Setup of AFHTTPRequestOperationManager :
httpOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:
[NSURL URLWithString: HTTPS_URL)]];
httpOperationManager.responseSerializer =
[AFXMLParserResponseSerializer serializer];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled: YES];
GET REQUEST
AFHTTPRequestOperation *op =[httpOperationManager GET:
[NSString stringWithFormat:SOME_PATH]
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//code to setup NSXMLParser ...
}
failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error %#", [error localizedDescription]);
}];
I think you already solved the problem, but if you are trying to authenticate in a server that doesn't have a valid certificate you have to set YES for property allowInvalidCertificates in your AFHTTPRequestOperationManager object:
[yourManager.requestSerializer setAuthorizationHeaderFieldWithUsername:#"your_username" password:#"your_password"];
[yourManager.securityPolicy setAllowInvalidCertificates:YES];
Also, as #a1phanumeric said, it can be necessary to include this line:
[yourManager.securityPolicy setValidatesDomainName:NO];
Cheers.
NSURLErrorDomain -1012 is NSURLErrorUserCancelledAuthentication. (See the error code list and search for -1012.)
You state, "the server does not require user authentication". But this error would not be called if that were true.
Possible causes:
Your server is erroneously requesting authorization (a server bug)
The URL formed with HTTPS_URL and SOME_PATH is not what you expect, and some other server is requesting authorization
Some intermediary (like a proxy server, or an access point) is requiring authorization.
Some debugging tips:
Set breakpoints inside the AFNetworking implementation to see which URL is being hit
Configure AFHTTPRequestOperationLogger so you can see the actual request body and response in your console log
Make the same request with curl or Advanced Rest Client and observe the server's response
Side note: I think [NSString stringWithFormat:SOME_PATH] is pointless - why not just use SOME_PATH?
I am trying to make my app handle HTTP client errors. Ideally, I would like to run a static method whenever such an error occurs, regardless of the request that caused it.
It seems that I am doing something wrong -- I have tried to add a RKDynamicResponseDescriptor to call the method, but it doesn't get called. Whenever my server responds with an error, the request never finishes (the network activity indicator keeps spinning and the AFNetworkingOperationDidFinishNotification is never posted.)
I can reproduce the behavior by creating a new AFHTTPRequest request like this:
request = [[RKObjectManager sharedManager].HTTPClient requestWithMethod:#"GET" path:#"events" parameters:nil];
AFHTTPRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"%#", JSON);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
Neither the success nor the failure block is called. If I replace the first line with
request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
the blocks get called. How can I make RestKit handle the HTTP error response?
Edit 1: Apparently this happens in both cases when the server returns 401 with an authentication challenge. I have tried setting a breakpoint in RKHTTPRequestOperation in didReceiveAuthenticationChallenge, but it is not triggered. My Fiddler shows that only one request is sent.
After lots of fiddling, I think I have found the reason for this problem. The AFUrlConnectionRequest class implements the connection:willSendRequestForAuthenticationChallenge:delegate method when SSL pinning is enabled. The implementation simply returns if the authentication challenge is not related to SSL.
From the documentation of NSUrlConnectionDelegate:
This method allows the delegate to make an informed decision about connection authentication at once. If the delegate implements this method, it has no need to implement
connection:canAuthenticateAgainstProtectionSpace:,
connection:didReceiveAuthenticationChallenge:,
connectionShouldUseCredentialStorage:.
In fact, these other methods are not invoked.
The methods will not be invoked, and thus RestKit never knows that the request resulted in HTTP error 401.
I solved the problem by removing this define, so the delegate method is not implemented:
#define _AFNETWORKING_PIN_SSL_CERTIFICATES_
I am building my first iOS app.
I have got the backend code done, but I am struggling with the Objective-C part of it.
I have a signup / login page.
But I don't know how to send that data to my server using Objective C.
I have read that AFNetworking is good, but I was wondering how I could use that for user login .
I have downloaded and added AFNetworking to my XCode Project and set up headers.
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://examplewebsite.com]];
[client setDefaultHeader:#"key" value:#"value"];
[client setAuthorizationHeaderWithUsername:#"username" password:#"password"];
[client setAuthorizationHeaderWithToken:#"token"];
NSURLRequest *request = [client requestWithMethod:#"someMethod" path:#"somePath" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
but I am still lost.
Since you're trying to login to your own API, you don't want setAuthorization stuff. That's for basic HTTP auth. Instead you want to use getPath:parameters:success:failure or the postPath version, depending on if your backend is expecting HTTP GET or HTTP POST.
Pass your userid / password in the parameters argument. You should set parameterEncoding to be the correct format. You're probably using HTTP Forms url encoding, or JSON. Whatever your backend expects.
You don't want to set the authorization headers in this case, since this is for "basic access HTTP authentication", which is a method for a HTTP user agent to provide a user name and password when making a request to a server.
You want to use your own API and interact with a restful server and therefore, I would recommend, that you subclass AFHTTPClient -> interact with an API, Web Service, or Application. - Take a look at the examples in the AFNetworking zip archive, if you have difficulties in subclassing AFHTTPClient.
Since you want to create an app with user login, the app needs to send these information to your server, and the server should return if the login was succesful or not.
This can be done like so - HTTP POST.
- (void)login {
// Login information from UITextFields
id params = #{
#"username": self.usernameField.text,
#"password": self.passwordField.text
};
//Call the AFHTTP subclass client, with post block. postPath = the path of the url, where the parameters should be posted.
[[theAuthAPIClient sharedClient] postPath:#"/login"
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//handle succesful response from server.
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// handle error - login failed
}
}];
}
You need to pass the parameters in the right format, depending on what format your server expects. This can be done by setting the right encoding in your AFHTTPClient subclass -> ParameterEncoding
Since I came here while searching for a working solution for AFNetworking 2.0, not knowing that AFHTTPClient was removed from the Framework, I will post the new way to establish this connection here:
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:#"http://examplewebsite.com"]];
[manager setRequestSerializer:[AFHTTPRequestSerializer serializer]];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername:#"userName" password:#"password"];
Several times before I used the method...
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
to send/receive data synchronously when in an NSOperation and it has worked.
I'm working on a new project (new API, new server) now and the error just isn't being populated.
I'm using Charles proxy to snoop the network traffic and I can see "Response Code 500 Internal Server Error" coming back from the failed request in Charles but in the app the error object is always nil.
Like I said, I've used this loads before and the error is always populated if an error occurs.
Is there something on the server side that I can point out to the server side dev? He's adamant that I must be doing something wrong. The only thing I don't understand is that Charles is picking up response code 500 from it.
N.B. The problem is not that the error is occurring. The problem is that when it does occur the NSError object is not reporting it back to me. Just to clear it up in case anyone tells me to ask the server dev to fix the error. :-)
Your error object is not populated because the request succeeded from NSURLConnection's perspective -- that is, it was able to send data to the server and receive a response.
For protocol-specific errors, you need to inspect the NSURLResponse you get back. In this case, cast the response to NSHTTPURLResponse and invoke -statusCode.
The error is only populated if there is no response from the server or not a valid HTTP response, e.g. connection loss etc.
"Response Code 500 Internal Server Error" means there was an internal server error and your server returns an HTTP message with the response code 500. You should use NSHTTPURLResponse instead of NSURLResponse and call the statusCode method to check the HTTP response code. If the response code starts with 2 everything is fine. So I usually have a check like this: statusCode / 100 == 2.
For more HTTP response codes see http://en.wikipedia.org/w/index.php?title=HTTP_response_codes
I am looking forward for posting some data and information on the web server through my iPhone application. I am not getting the way to post data to the web server from iPhone sdk.
It depends in what way you want to send data to the web server. If you want to just use the HTTP POST method, there are (at least) two options. You can use a synchronous or an asynchronous NSURLRequest. If you only want to post data and do not need to wait for a response from the server, I strongly recommend the asynchronous one, because it does not block the user interface. I.e. it runs "in the background" and the user can go on using (that is interacting with) your app. Asynchronous requests use delegation to tell the app that a request was sent, cancelled, completed, etc. You can also get the response via delegate methods if needed.
Here is an example for an asynchronous HTTP POST request:
// define your form fields here:
NSString *content = #"field1=42&field2=Hello";
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.example.com/form.php"]];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[content dataUsingEncoding:NSISOLatin1StringEncoding]];
// generates an autoreleased NSURLConnection
[NSURLConnection connectionWithRequest:request delegate:self];
Please refer to the NSURLConnection Class Reference for details on the delegate methods.
You can also send a synchronous request after generating the request:
[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
If you pass a NSURLResponse ** as returning response, you will find the server's response in the object that pointer points to. Keep in mind that the UI will block while the synchronous request is processed.