Cocoa: Handling 407 http response cfnetwork - objective-c

I am creating downloader application.
I am facing a problem with proxy authentication.
I am getting 407 response code i.e proxy authentication required. I have valid proxy authentication details.
Following is Code Flow:
1. Create Http request using CFHTTPMessageCreateRequest
2. Set necessary header field values like Cache-Control, Accept-Ranges, Range & User-Agent using CFHTTPMessageSetHeaderFieldValue
3. Create read stream using CFReadStreamCreateForHTTPRequest
4. Set proxy server URL & port properties on read stream using CFReadStreamSetProperty
5. Set kCFStreamPropertyHTTPShouldAutoredirect to kCFBooleanTrue using CFReadStreamSetProperty
6. open read stream using CFReadStreamOpen
7. In a loop wait for stream to get opened
while (1)
{
if (kCFStreamStatusOpen == CFReadStreamGetStatus)
{
if (CFReadStreamHasBytesAvailable)
{
Get Http response header using CFReadStreamCopyProperty
Get response code using CFHTTPMessageGetResponseStatusCode
if (200 || 206 is response code)
SUCCESS
else check if response code is 407.
}
}
}
I tried using following code
if (407 == nsiStatusCode)
{
CFStreamError err;
cfAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, cfHttpResponse);
if ((cfAuthentication) && (CFHTTPAuthenticationIsValid(cfAuthentication, &err)))
{
if (CFHTTPAuthenticationRequiresUserNameAndPassword(cfAuthentication))
{
CFHTTPMessageApplyCredentials(cfHttpRequest, cfAuthentication, (CFStringRef)pnsUserName, (CFStringRef)pnsPassword, &err);
}
}
}
but unable to make it work.
How do I handle 407 status code so as to communicate with authenticating HTTP server?
Thanks in advance.
Vaibhav.

Build a CFHTTPMessageRef
-(CFHTTPMessageRef)buildMessage
{
NSURL *myURL = [NSURL URLWithString:#"http://myurl.com"];
NSData *dataToPost = [[NSString stringWithString:#"POST Data It Doesn't Matter What It Is"] dataUsingEncoding:NSUTF8StringEncoding];
//Create with the default allocator (NULL), a post request,
//the URL, and pick either
//kCFHTTPVersion1_0 or kCFHTTPVersion1_1
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CSTR("POST"), (CFURLRef)myURL, kCFHTTPVersion1_1);
CFHTTPMessageSetBody(request, (CFDataRef)dataToPost);
//Unfortunately, this isn't smart enough to set reasonable headers for you
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("HOST"), (CFStringRef)[myURL host]);
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Length"), (CFStringRef)[NSString stringWithFormat:"%d", [dataToPost length]);
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), CFSTR("charset=utf-8"));
return [NSMakeCollectable(request) autorelease];
}
Send it to the server and read back the response
-(CFHTTPMessageRef)performHTTPRequest:(CFHTTPMessageRef)request
{
CFReadStreamRef requestStream = CFReadStreamCreateForHTTPRequest(NULL, request);
CFReadStreamOpen(requestStream);
NSMutableData *responseBytes = [NSMutableData data];
CFIndex numBytesRead = 0 ;
do
{
UInt8 buf[1024];
numBytesRead = CFReadStreamRead(requestStream, buf, sizeof(buf));
if(numBytesRead > 0)
[responseBytes appendBytes:buf length:numBytesRead];
} while(numBytesRead > 0);
CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(requestStream, kCFStreamPropertyHTTPResponseHeader);
CFHTTPMessageSetBody(response, (CFDataRef)responseBytes);
CFReadStreamClose(requestStream);
CFRelease(requestStream);
return [NSMakeCollectable(response) autorelease];
}
Adding Authentication to an HTTP Request
-(void)addAuthenticationToRequest:(CFHTTPMessageRef)request withResponse:(CFHTTPMessageRef)response
{
CFHTTPAuthenticationRef authentication = CFHTTPAuthenticationCreateFromResponse(NULL, response);
[NSMakeCollectable(authentication) autorelease];
CFStreamError err;
Boolean success = CFHTTPMessageApplyCredentials(request, authentication, CFSTR("username"), CFSTR("password"), &err);
}
Putting It All Together
-(void)magicHappens
{
CFHTTPMessageRef request = [self buildMessage];
CFHTTPMessageRef response = [self performHTTPRequest: request];
UInt32 statusCode;
statusCode = CFHTTPMessageGetResponseStatusCode(response);
//An HTTP status code of 401 or 407 indicates that authentication is
//required I use an auth count to make sure we don't get stuck in an
//infinite loop if our credentials are bad. Sometimes, making the
//request more than once lets it go through.
//I admit I don't know why.
int authCount = 0;
while((statusCode == 401 || statusCode == 407) && authCount < 3)
{
request = [self buildMessage];
[self addAuthenticationToRequest:request withResponse:response];
response = [self performHTTPRequest: request];
statusCode = CFHTTPMessageGetResponseStatusCode;
authCount++;
}
NSData *responseBodyData = [(NSData*)CFHTTPMessageCopyBody(response) autorelease];
NSString *responseBody = [[[NSString alloc] initWithData:responseBodyData encoding:NSUTF8StringEncoding] autorelease];
NSLog(responseBody);
}
Refer this link.

Related

How to make an API Call every second using ESP8266?

I tried making a HTTP request to my localhost that is running Laravel Api.
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(url + "update"); //request destination
http.addHeader("Content-Type", "application/x-www-form-urlencoded"); //content-type header
String stringData = "payload=" + data;
int httpCode = http.POST(stringData);
String payload = http.getString();
Serial.print(httpCode);
http.end();
}
delay(2000);
}
When I reduce delay value <= 2000, nodeMCU is not performing as expected.
While testing getting 429 error.
Please suggest an alternative that can update every second.
429 Too Many Requests "indicates the user has sent too many requests in a given amount of time". The server could be slow, or rate limited.
The server may send a Retry-After header; if it does, it tells you how long you have to wait before a new request is made.
I suspect you would have to change things on the server side to make it as fast as you want; I doubt the ESP8266 is to blame.
Note that if handling a request takes longer than 1s, you're out of luck anyway.
BTW could you try the code below and see if it works? Just to rule out some other potential problems. It removes the inefficient delay() and only does HTTPClient http; once.
HTTPClient http;
unsigned long int lastPost = 0;
int postInterval = 1000; // Post every second
void setup() {
// Setup stuffs
}
void loop() {
if (WiFi.status() == WL_CONNECTED && (millis() - lastPost) >= postInterval) {
http.begin(url + "update"); //request destination
http.addHeader("Content-Type", "application/x-www-form-urlencoded"); //content-type header
String stringData = "payload=" + data;
int httpCode = http.POST(stringData);
String payload = http.getString();
Serial.print(httpCode);
http.end();
lastPost = millis();
}
}
Untested, just typed it in, but you get the idea.

Getting certificate error in Uploading image with https Amazon AWS

I am facing an issue while uploading image in Amazon AWS. Here is my code:
import UIKit
protocol ContentUploaderDelegate {
func onContentLoadComplete(status:Bool,serverResponse:String)
}
class ContentUploader
{
let contentURL = "https:<MY URL>amazonaws.com/api/v1/contents"
var delegate:ContentUploaderDelegate?
func uploadImage(image:UIImage,xAuth:String,mimeType:String,imageName:String)
{
let url = NSURL(string: contentURL)
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let boundary = generateBoundaryString()
//define the multipart request type
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.setValue(xAuth, forHTTPHeaderField: "x-auth-token")
request.setValue("application/json", forHTTPHeaderField: "accept")
let image_data = UIImageJPEGRepresentation(image, 1.0)
if(image_data == nil)
{
return
}
let body = NSMutableData()
//name to save in server
let fname = imageName
let mimetype = mimeType
//define the data post parameter
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition:form-data; name=\"test\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("hi\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition:form-data; name=\"files\"; filename=\"\(fname)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: \(mimetype)\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(image_data!)
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
//set the HTTPBody
request.HTTPBody = body
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
self.delegate?.onContentLoadComplete(false, serverResponse: (error?.description)!)
return
}
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("success \(dataString)")
self.delegate?.onContentLoadComplete(true, serverResponse:dataString! as String)
}
task.resume()
}
private func generateBoundaryString() -> String
{
return "Boundary-\(NSUUID().UUIDString)"
}
The following delegate method never gets called. What could be the reason?
func URLSession(session: NSURLSession,
task: NSURLSessionTask,
didReceiveChallenge challenge: NSURLAuthenticationChallenge,
completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?)
-> Void) {
let protectionSpace = challenge.protectionSpace
let theSender = challenge.sender
if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if (challenge.protectionSpace.host == "ec2-52-36-216-81.us-west-2.compute.amazonaws.com") {
if let theTrust = protectionSpace.serverTrust{
let theCredential = NSURLCredential(trust: theTrust)
theSender!.useCredential(theCredential, forAuthenticationChallenge: challenge)
return
}
}
}
theSender!.performDefaultHandlingForAuthenticationChallenge!(challenge)
return
}
}
And I am getting the following error. Any idea why getting this error?
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this
server is invalid. You might be connecting to a server that is
pretending to be “.amazonaws.com” which could put your
confidential information at risk."
UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=, NSLocalizedRecoverySuggestion=Would you like to
connect to the server anyway?, _kCFStreamErrorDomainKey=3,
_kCFStreamErrorCodeKey=-9813, NSErrorPeerCertificateChainKey={type = immutable, count = 1, values = (
0 : .com i: www..com> )}, NSUnderlyingError=0x7f9d42aedc10 {Error
Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)"
UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
kCFStreamPropertySSLPeerTrust=,
_kCFNetworkCFStreamSSLErrorOriginalValue=-9813, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9813, kCFStreamPropertySSLPeerCertificates={type = immutable, count = 1, values = (
0 : .com i: www..com> )}}}, NSLocalizedDescription=The certificate for this server is
invalid. You might be connecting to a server that is pretending to be
“.amazonaws.com” which could put your confidential information
at risk., NSErrorFailingURLKey=https://amazonaws.com/api/v1/contents,
NSErrorFailingURLStringKey=https://.amazonaws.com/api/v1/contents,
NSErrorClientCertificateStateKey=0}
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “.amazonaws.com”
I believe something is wrong with the common name “.amazonaws.com”
NSErrorFailingURLKey=https://amazonaws.com/api/v1/contents,
NSErrorFailingURLStringKey=https://.amazonaws.com/api/v1/contents
The URLs shown in the error message do not appear to be a well know endpoint. I would expect to see something like https://ec2-2-2-2-2.compute-1.amazonaws.com or another Fully Qualified Domain name there.
The error message also confirms this. You are connecting a host, but the name on the certificate does not match. This is the reason for the pretending to be “.amazonaws.com” error.
Confirm the correct endpoint, and how your code is forming the full URL.
The following delegate method never gets called. What could be the
reason?
The error occurs before the function is called. The session is never established because of the certificate error.

Writing a URL to an embed-field using PodioKit

I hope to find some help to diving deeper into Podiokit, the ObjC-API to Podio. I try to set a link-field's value to a URL. My first simple try looked like this:
NSDictionary *embedAttributes = [NSDictionary dictionaryWithObject: #"http://www.google.com" forKey: #"url"];
PKTEmbed *embed = [[PKTEmbed alloc] initWithDictionary: embedAttributes];
item[#"linkfield"] = embed;
I found an example using PHP but had no luck to transform it into Objective-C:
$attributes = array( 'url' => 'http://www.infranet.com' );
$embed = PodioEmbed::create( $attributes );
$attribute['embed']['embed\_id'] = $embed->embed\_id;
$attribute['file']['file\_id'] = $embed->files[0]->file\_id;
$this->orgItem->field('organizationlink')->set\_value($attribute);
Maybe someone knows how to get it right, would be fine :-)
[Edit] The PodioKit-Manual just says:
PKTEmbed *link = ...;
item[#"link"] = link;
[Edit 2] The error occurs when I try to save the item. The log says:
Error: Saving file Error Domain=com.alamofire.error.serialization.response Code=-1011 "Request failed: Ungültige Anforderung (400)" UserInfo=0x600000c7ee80 {com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x6000008358e0> { URL: https://api.podio.com/item/app/_xxxx_/ } { status code: 400, headers {
"Content-Length" = 263;
"Content-Type" = "application/json; charset=utf-8";
Date = "Sat, 27 Sep 2014 19:16:22 GMT";
Server = nginx;
"X-Podio-Request-Id" = yqyl6yku;
"X-Rate-Limit-Limit" = 250;
"X-Rate-Limit-Remaining" = 248;
} }, NSLocalizedDescription=Request failed: Ungültige Anforderung (400), NSErrorFailingURLKey=https://api.podio.com/item/app/_xxxx_/}
Thanks,
Michael / Hamburg
Sebastian at Podio here. You need to first create the PKTEmbed object server side, then use it as the value of the item field. So you would use:
PKTItem *item = ...;
[[PKTEmbed createEmbedForURLString:#"https://www.google.com"] onSuccess:^(PKTEmbed *embed) {
item[#"link-field"] = embed;
} onError:^(NSError *error) {
// Handle error
}];
The server will assign you an embedID and generate a thumbnail for you etc. I will look into adding the ability to simply provide a URL string directly, as I agree that makes a lot of sense.
Hope that helps!

i am having a issue with json codeigniter rest its not closing the tag

i am having a problem with json codeigniter rest
i am making this call to the server and the problem its that its not closing the json tags
s, USA","clientUID":"7","email":null,"idipad":"2","dateModified":null},{"id":"19","uid":null,"name":"Wayne Corporation, Inc.","phone":"932345324","address":"Second st. 312, Gotham City","clientUID":"7","email":"waynecorp#gmail.com","idipad":"1","dateModified":null}]
its missing the final }
this is the code that creates the response :
$this->response(array('login'=>'login success!','user_admin_id'=>$user_id,'client'=>$client,'users'=>$users,'projects'=>$projects,'plans'=>$plans,'meetings'=>$meetings,'demands'=>$demands,'tasks'=>$tasks,'presences'=>$presences,'contractors'=>$contractors,'companies'=>$companies), 200);
this is the client call using curl :
$this->curl->create('http://dev.onplans.ch/onplans/index.php/api/example/login/format/json');
// Option & Options
$this->curl->option(CURLOPT_BUFFERSIZE, 10);
$this->curl->options(array(CURLOPT_BUFFERSIZE => 10));
// More human looking options
$this->curl->option('buffersize', 10);
// Login to HTTP user authentication
$this->curl->http_login('admin', '1234');
// Post - If you do not use post, it will just run a GET request
//$post = array('remember'=>'true','email'=>'admin.architect#onplans.ch','password'=>'password');
$post = array('remember'=>'true','email'=>'admin.architect#onplans.ch','password'=>'password');
$this->curl->post($post);
// Cookies - If you do not use post, it will just run a GET request
$vars = array('remember'=>'true','email'=>'manuel#ffff.com','password'=>'password');
$this->curl->set_cookies($vars);
// Proxy - Request the page through a proxy server
// Port is optional, defaults to 80
//$this->curl->proxy('http://example.com', 1080);
//$this->curl->proxy('http://example.com');
// Proxy login
//$this->curl->proxy_login('username', 'password');
// Execute - returns responce
echo $this->curl->execute();
// Debug data ------------------------------------------------
// Errors
$this->curl->error_code; // int
$this->curl->error_string;
print_r('error :::::LOGINN REMOTE:::::'.$this->curl->error_string);
// Information
$this->curl->info; // array
print_r('info :::::::::::::'.$this->curl->info);
the response belong to the rest api codeigniter from phil
/**
* Response
*
* Takes pure data and optionally a status code, then creates the response.
*
* #param array $data
* #param null|int $http_code
*/
public function response($data = array(), $http_code = null)
{
global $CFG;
// If data is empty and not code provide, error and bail
if (empty($data) && $http_code === null)
{
$http_code = 404;
// create the output variable here in the case of $this->response(array());
$output = NULL;
}
// If data is empty but http code provided, keep the output empty
else if (empty($data) && is_numeric($http_code))
{
$output = NULL;
}
// Otherwise (if no data but 200 provided) or some data, carry on camping!
else
{
// Is compression requested?
if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE)
{
if (extension_loaded('zlib'))
{
if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
{
ob_start('ob_gzhandler');
}
}
}
is_numeric($http_code) OR $http_code = 200;
// If the format method exists, call and return the output in that format
if (method_exists($this, '_format_'.$this->response->format))
{
// Set the correct format header
header('Content-Type: '.$this->_supported_formats[$this->response->format]);
$output = $this->{'_format_'.$this->response->format}($data);
}
// If the format method exists, call and return the output in that format
elseif (method_exists($this->format, 'to_'.$this->response->format))
{
// Set the correct format header
header('Content-Type: '.$this->_supported_formats[$this->response->format]);
$output = $this->format->factory($data)->{'to_'.$this->response->format}();
}
// Format not supported, output directly
else
{
$output = $data;
}
}
header('HTTP/1.1: ' . $http_code);
header('Status: ' . $http_code);
// If zlib.output_compression is enabled it will compress the output,
// but it will not modify the content-length header to compensate for
// the reduction, causing the browser to hang waiting for more data.
// We'll just skip content-length in those cases.
if ( ! $this->_zlib_oc && ! $CFG->item('compress_output'))
{
header('Content-Length: ' . strlen($output));
}
exit($output);
}
This answer was referenced from Github issue. Also raised by Pedro Dinis, i guest.
I met this problem today and take me long hours to search for the solution. I share here with hope to help someone like me.
The key is to replace around line 430 in the library file: REST_Controller.php :
header('Content-Length: ' . strlen($output));
by
header('Content-Length: ' . strlen("'".$output."'"));
UPDATE: The problem was solved here
Or you can just comment out the code, it will run fine. :)

why qtnetworkaccessmanager don't go to authenticationRequired

I am creating an application that is mentioned to connect to an instance of on owncloud server but i can't find why it doesn't connect to the server .Instead of that the reply i get to the login screen and i get the html code for it
this is the code responsible for the connection
//the network request and reply
QNetworkAccessManager * manager = new QNetworkAccessManager();
QUrl url (url1);
manager->get(QNetworkRequest(url));
connect(manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(provideAuthenication(QNetworkReply*,QAuthenticator*)));
connect(manager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(result(QNetworkReply *)));
the reply code
void Login::result(QNetworkReply *reply)
{
reply->deleteLater();
if(reply->error() == QNetworkReply::NoError) {
// Get the http status code
int v = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (v >= 200 && v < 300) // Success
{
qDebug()<<"Here we got the final reply";
QString replyText = reply->readAll();
qDebug()<<replyText;
}
else if (v >= 300 && v < 400) // Redirection
{
qDebug()<<"Get the redirection url";
QUrl newUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
// Because the redirection url can be relative,
// we have to use the previous one to resolve it
newUrl = reply->url().resolved(newUrl);
QNetworkAccessManager *manager = reply->manager();
QNetworkRequest redirection(newUrl);
QNetworkReply *newReply = manager->get(redirection);
QString replyText = newReply->readAll();
qDebug()<<replyText;
return; // to keep the manager for the next request
}
}
else
{
// Error
qDebug()<<reply->errorString();
}
reply->manager()->deleteLater();
}
could you help me figure out why i get the login screen instead of authentication ?
Try to call connect() before calling manager->get() otherwise when the authentication required signal is triggered, there might not be any slots present to call to handle that signal.
Try this instead:
QNetworkAccessManager * manager = new QNetworkAccessManager();
QUrl url (url1);
connect(manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(provideAuthenication(QNetworkReply*,QAuthenticator*)));
connect(manager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(result(QNetworkReply *)));
manager->get(QNetworkRequest(url));