get custom headers of AFHTTPRequestOperation - xcode

I attempt to get custom header key using AFHTTPRequestOperation, I try using allHeaderFields but nothing, here is header response
HTTP/1.1 302 Moved Temporarily
Server nginx
Date Tue, 19 Feb 2013 16:38:29 GMT
Content-Type text/html
Transfer-Encoding chunked
Connection keep-alive
Set-Cookie AUTH-ID="fjArrnmlyNMU9kfIu38Oc0LS451Y/UaMn0rb5sKj46CxmfJj8y8yr8CfwOewItFY"; HTTPOnly
X-AUTH-TOKEN mfy+426BNZdq1h92As3oXdZbf2iOI7wV7EOEUMAV3hAqtY7cOnWvA4df7h6RfjeD
Location /home.php
I use AFHTTPRequestOperation like this
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *headerData = [[operation response] allHeaderFields];
if ([headerData objectForKey:#"X-AUTH-TOKEN"] != nil)
token = [headerData objectForKey:#"X-AUTH-TOKEN"];
NSLog(#"headers = %#", headerData);
NSLog(#"token = %#", token);
...
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
...
}];
but X-AUTH-TOKEN key don't appear in headers NSLog, I'm sure the header key is present because I use Charles proxy to debug and Charles show me the X-AUTH-TOKEN key. Maybe the 302 status code is the problem, can anyone help me please?
Thanks.
[EDIT] when I try to show status code of response, it's 200.

Solved using some tweaks of AFURLConnectionOperation for intercept redirect response.

Related

Why must I send the CSRF token with AFNetworking but not with cURL?

When I perform a POST request via cURL, it completes successfully (HTTP 201) even without sending the CSRF token header.
curl -X POST http://server/app/addrecord/ -d '{"pk":"1", "lname":"Smith", "fname":"Robert"}' -H "Content-Type: application/json" > out.html
However when I attempt the same operation using AFNetworking in Obj-C, I get a 403 error that states the CSRF token is required.
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFJSONRequestSerializer *serializer = [AFJSONRequestSerializer serializer];
[serializer setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[serializer setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[manager setRequestSerializer:serializer];
AFJSONResponseSerializer *respSerializer = [AFJSONResponseSerializer serializer];
respSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/html", #"text/plain", #"text/json", nil];
manager.responseSerializer = respSerializer;
[manager POST:#"http://server/app/addrecord/" parameters:parameters success:^(NSURLSessionDataTask * task, id responseObject) {
NSLog(#"Success");
} failure:^(NSURLSessionDataTask * task, NSError * error) {
NSLog(#"Error");
}];
Simply by adding the following line to my request serializer, the error goes away.
[serializer setValue:csrf forHTTPHeaderField:#"X-CSRFTOKEN"];
While I don't need the AFNetworking code to work without the CSRF Token being sent, I would like to understand why the cURL command works without the token but the AFNetworking code doesn't.

AFNetworking and Authorization Header

I'm new to AFNetworking, and I'm trying to use it to talk to an API that I've written in Go. I'm having difficulty getting the Authorization header to work. I've subclassed AFHTTPSessionManager and configured it as follows
+ (HMAPIClient *)sharedHMAPIClient
{
static HMAPIClient* _sharedHMAPIClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedHMAPIClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:HMBaseURL]];
});
return _sharedHMAPIClient;
}
- (instancetype)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (self) {
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.requestSerializer = [AFJSONRequestSerializer serializer];
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:RegistrationAPIKey
password:#"Doesn't matter what goes here."];
}
return self;
}
- (void)hitTestEndpoint
{
[self GET:#"testEndpoint" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"%#", responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#", error);
}];
}
When I call -(void)hitTestEndpoint, I see the following headers in my server logs (Authorization is missing):
Key: Accept-Encoding Value: [gzip, deflate]
Key: Connection Value: [keep-alive]
Key: Accept-Language Value: [en;q=1]
Key: User-Agent Value: [TestApp/2 (iPad Simulator; iOS 8.1; Scale/2.00)]
Key: Accept Value: [*/*]
For comparison, when I hit the same endpoint with the following curl command,
curl https://api.example.com/v1/testEndpoint/ -u test_xqzwjcasogptbnpa:
I see the following headers:
Key: Authorization Value: [Basic eHF6d2pjYXNvZ3B0Ym5wYTo=]
Key: User-Agent Value: [curl/7.30.0]
Key: Accept Value: [*/*]
Can someone point me in the right direction? -Thanks
Update:
I have added AFNetworkActivityLogger so that I can see each request. The Authorization header is indeed included. Also, I tried hitting http://headers.jsontest.com, which returns the HTTP request headers received from the client. The Authorization header is present in that output.
So, the problem must be with my server. I'm already logging all headers for each request, and I'm not sure where else to look. Going to tag this question with Go to see if someone has an idea.
Update 2:
I added a call to httputil.DumpRequest at the top of my request handler, and it also shows that the Authorization header is missing. By the way, any custom headers that I set do appear as expected. It's just the Authorization header that's missing.
Here's the Go Code:
func testResponse(rw http.ResponseWriter, request *http.Request) {
// check output from DumpRequest()
dump,err := httputil.DumpRequest(request,true)
check(err)
fmt.Println("Output of DumpRequest():")
fmt.Println(string(dump))
fmt.Println("============")
fmt.Println("request.Headers:")
for key, value := range request.Header {
fmt.Println("Key:", key, "Value:", value)
}
fmt.Println("===============")
// return some dummy JSON
rw.Header().Set("Content-Type", "application/json")
rw.Write(PersonToJson(getPerson("2f6251b8-d7c4-400f-a91f-51e09b8bfaf4")))
}
The server log you're showing looks like the headers after Go has already parsed them. It would be helpful to see the raw, plaintext HTTP headers that Go received. That would tell you if Go is ignoring the header or if something upstream is stripping it out.
Edit: Not sure why Go would strip out the Authorization header before giving you the supposedly raw request. But I think the Authorization header is normally sent by the client only after making a previous un-authorized request and getting a 401 response from the server with a WWW-Authenticate header. Since it sounds like your client is sending the Authorization header out of the blue, maybe the Go server API is ignoring & stripping the header because it never asked the client to send it.
If you just want to send a simple auth token on every request, what if you simply used a made up X- header instead, since you indicated that other headers you set arrive just fine?

Please help me understand Ajax request versus Backbone fetch()

My app can currently hit our API with a standard JQuery Ajax GET request and get good data back. CORS has been properly implemented on the remote server as far as I can see. Here are the response headers:
company_client_envelope_id: 88764736-6654-22e4-br344-a1w2239a892d
access-control-allow-headers: X-Requested-With, Cookie, Set-Cookie, Accept, Access-Control
Allow-Credentials, Origin, Content-Type, Request-Id , X-Api-Version, X-Request-Id,Authorization, COMPANY_AUTH_WEB
access-control-expose-headers: Location
response-time: 55
request-id: 88764736-6654-22e4-br344-a1w2239a892d
company_api_version: 0.01.09
server: localhost
transfer-encoding: chunked
connection: close
access-control-allow-credentials: true
date: Sun, 09 Feb 2014 14:44:05 GMT
access-control-allow-origin: *
access-control-allow-methods: GET, POST
content-type: application/json
However, using Backbone and calling the same GET request by using fetch() causes the following CORS error:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
I cannot figure out what the difference is. Both requests are running from localhost.
In the case of the AJAX query, the following is being sent as requested by the API guys:
headers: {
"accept":"application/json"
}
And in the case of the model and collection declaration I am sending the headers like so:
MyApp.someCollection = Backbone.Collection.extend(
{
model:MyApp.someModel,
headers: {
'Accept':'application/json',
'withCredentials': 'true'
},
url: MYCOMPANY_GLOBALS.API + '/endpoint'
});
and my fetch is simply:
someCollection.fetch();
===============================
Added in response to: #ddewaele
These are the headers from the network tab:
Request URL:http://api-blah.com:3000/
Request Headers CAUTION: Provisional headers are shown.
Accept:application/json
Cache-Control:no-cache
Origin:http://localhost
Pragma:no-cache
Referer:http://localhost/blah/blah/main.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107Safari/537.36
There is no pre-flight or remote headers from the API server:
many thanks,
Wittner
I've recommended to you rewrite Backbone.sync method, because in your app you have some security field for example and other reason.
var oldBackboneSync = Backbone.sync;
// Override Backbone.Sync
Backbone.sync = function (method, model, options) {
if (method) {
if (options.data) {
// properly formats data for back-end to parse
options.data = JSON.stringify(options.data);
}
// transform all delete requests to application/json
options.contentType = 'application/json';
}
return oldBackboneSync.apply(this, [method, model, options]);
}
You can add different headers as you want.

How to fix System.ArgumentException in HttpWebResponse?

I am facing this exception when receiving HttpWebResponse for my WindowsPhone app. How am I supposed to fix this. It happens very often but I need to make sure my app doesn't crash if it happens. Please have a look at the screenshot.
My expected response is
Headers:-
HTTP/1.1 500 Internal Server Error
Date: Wed, 28 Nov 2012 06:41:24 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=30
Set-Cookie: ...........; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Internal Server Error:
Json:-
{"status":0,"error_code":1001,"data":{"msg":"Something went wrong. Please try again later. [error code 1001]"}}
It also shows in the InnerException the message as Specified value has invalid HTTP Header characters.
Parameter name: name
Please help. I don't know why webRequest.EndGetResponse(asynchronousResult) is not able to read the response. Is there an alternative?
UPDATE
to start the request:
_webRequest.BeginGetRequestStream(new AsyncCallback(GetReqeustStreamCallback), _webRequest);
private void GetReqeustStreamCallback(IAsyncResult asynchronousResult)
{
if ((!ReqIdEnabled || Network.RequestUniqueId == this.RequestUniqueId))
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
// End the stream request operation
using (Stream postStream = webRequest.EndGetRequestStream(asynchronousResult))
{
// Add the post data to the web request
postStream.Write(_postDataInBytes, 0, _postDataInBytes.Length);
//postStream.Dispose();
}
// Start the web request
webRequest.BeginGetResponse(new AsyncCallback(GetResponseCallback), webRequest);
}
}
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
try
{
//**throws Exception here when my server returns 503/500 and is not caught by the catch block below**
using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult))
{
ReadResponse(response);
}
}
catch (WebException ee)
{
}
}
Put a breakpoint in your catch block, and look at the lower level stacks, and look for System.Net.ni.dll!System.Net.WebHeaderCollection.this[string].set(string name, string value).
In the local variables, you can see for which particular value, the parse is failing.
For me, It was Google App Engine's custom User-Agent header which was creating the problem.

Why do I get two redirectResponses when receiving one 302 response?

I use the following code:
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSHTTPURLResponse *)response {
NSLog(#"Received redirect Response: %# %#", [response allHeaderFields], [NSHTTPURLResponse localizedStringForStatusCode:[response statusCode]]);
return request;
}
When I receive a 302 with the following header data:
< HTTP/1.1 302 Found
< Date: Wed, 03 Mar 2010 07:47:17 GMT
< Server: lighttpd/1.4.19
< Content-length: 0
< Content-type: text/html;charset=utf-8
< Location: `<new Location>`
< Vary: Accept-Encoding
this is the output in gdb console:
2010-03-03 08:42:03.265 MyProg[68106:207] Received redirect Response:
(null) server error 2010-03-03 08:42:14.414 MyProg[68106:207]
Received redirect Response: {
Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 20;
"Content-Type" = "text/html;charset=utf-8";
Date = "Wed, 03 Mar 2010 07:42:10 GMT";
"Keep-Alive" = "timeout=15, max=100";
Location = "<new Location>";
Server = "lighttpd/1.4.19";
Vary = "Accept-Encoding"; } found
When using Curl I only get one response and tracedump tells the same, so I am sure that the server sends only one redirect.
Why is this selector called twice?
connection:willSendRequest:redirectResponse: gets called before every request, so it is called once on the original request, which was not a redirect so response is nil; then it gets called when loading the redirection target, where response is the 302 response to the initial request.

Resources