Custom HTTP Request from iOS is rejected - ios

I am trying to send an HTTP request to a php script on iOS. I use multipart/form-data because I also need to be able to send JPEGs. The user agent is curl sending a request from curl in terminal works every time. Here is the request I end up generating from the code I have below.
POST /Folder/GetData.php HTTP/1.1
Host: website_goes_here
Content-Type: multipart/form-data; boundary=This12#
Expect: 100-continue
Content-Length: 146
Accept: */*
User-Agent: curl/7.37.1
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive
--This12#
Content-Disposition: form-data; name=uname
ThisIsTheUserNameHere
--This12#
Content-Disposition: form-data; name=pass
PasswordGoesHere
--This12#--
Here is the swift code I use to generate this request (data is a variable sent to the function with an array of dictionaries.):
var request=NSMutableURLRequest(URL: NSURL(string: “website-HERE”)!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 60)
request.timeoutInterval=NSTimeInterval(60)
request.HTTPMethod="POST";
var end="This12#";
request.addValue("multipart/form-data; boundary="+end, forHTTPHeaderField: "Content-Type")
request.setValue("*/*" , forHTTPHeaderField: "Accept")
request.setValue("close" , forHTTPHeaderField: "Connection")
request.setValue("100-continue", forHTTPHeaderField: "Expect")
request.setValue("curl/7.37.1", forHTTPHeaderField: "User-Agent")
var body=NSMutableData()
body.appendData(("\r\n--"+end+"\r\n").dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!)
var first=true
for object in data{
if !(first){
body.appendData(("\r\n--"+end+"\r\n").dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!)
}
first=false
if((object[“Content-Type”] as! String)=="text/plain"){
body.appendData(("Content-Disposition: form-data; name="+(object[“name”] as! String)+"\r\n\r\n"+(object[“TextToSend”] as! String)).dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!)
}else{
var part1="Content-Disposition: attatchment; name="+(object[“name”] as! String)
var part2="; filename="+(object[“FileName”] as! String)+";\r\nContent-Type: "
body.appendData((part1+part2+(object[“Content-Type”] as! String)+"\r\n\r\n").dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!)
body.appendData(object[“Data”] as! NSData);
}
}
body.appendData(("\r\n--"+end+"--\r\n").dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!)
request.HTTPBody=body
What happens is my GoDaddy website returns a valid response for the first few times and then the connection starts getting reset for the next minute and this repeats. Is there any reason for this? Please respond with either corrections for the code or the HTTP request. If this helps, the connection is sent with:
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response, data, error) ->
Void in
println(NSString(data: data, encoding: NSASCIIStringEncoding));
}
Thanks,

You don't show your code for actually sending the request and handling the response.
My guess though is that you're not reading to the end of the response, and so the connection is not being closed. That will leave you with lots of connections held open, until GoDaddy decides to throttle you.

It turns out that GoDaddy has a post limit per ip in a certain amount of time. Shared Hosting plans have a limit.

Related

Alamofire and Digest-Auth

I am trying to implement in my Apps Digest-auth but i am struggling, or is not working properly.
I have setup my request as you describe in your AuthenticationTestCase and looks like the following code:
let userName = "***********"
let password = "***********"
let qop = "auth"
let xmlStr: String = "<?xml version=\"1.0\" encoding=\"utf-8\"?><methodCall><methodName>authenticate.login</methodName></methodCall>"
let postData:Data = xmlStr.data(using: String.Encoding.utf8, allowLossyConversion: true)!
let url = URL(string: "https://app.**********.co.uk/service/mobile/digest-auth/\(qop)/\(userName)/\(password)")
var request = URLRequest(url: url!)
request.httpShouldHandleCookies = true
request.setValue("\(String(describing: xmlStr))", forHTTPHeaderField: "Content-Length")
request.setValue("application/xml", forHTTPHeaderField: "Content-Type")
request.setValue("IOS133928234892nil", forHTTPHeaderField: "User-Agent")
request.setValue("application/xml", forHTTPHeaderField: "Accept")
request.httpMethod = "POST"
request.httpBody = postData
AF.request(request)
.authenticate(username: userName, password: password)
.response { response in ........
When I run the above code, I am receiving the following response from the remote server:
Response XML Error:
You must be authenticated to access this resource
Response Error Code: 401
Response Headers:
Optional([AnyHashable("X-Powered-By"): PHP/7.1.33, AnyHashable("Pragma"): no-cache, AnyHashable("Content-Length"): 310, AnyHashable("Date"): Fri, 22 May 2020 09:15:48 GMT, AnyHashable("Server"): Apache/2.4.41 () OpenSSL/1.0.2k-fips PHP/7.1.33, AnyHashable("Cache-Control"): no-store, no-cache, must-revalidate, AnyHashable("Content-Type"): Content-Type: application/xml, AnyHashable("Www-Authenticate"): Digest realm="Mobile",nonce="31JEmMdeSVfXWQ:OT/ndHY6ch/PjqFwA6uutg",opaque="c81e728d9d4c2f636f067f89cc14864c",qop="auth",algorithm="MD5", Digest realm="Mobile",nonce="31JEmMdeSVfXWQ:OT/ndHY6ch/PjqFwA6uutg",opaque="c81e728d9d4c2f636f067f89cc14864c",qop="auth",algorithm="SHA-512-256", Digest realm="Mobile",nonce="31JEmMdeSVfXWQ:OT/ndHY6ch/PjqFwA6uutg",opaque="c81e728d9d4c2f636f067f89cc14864c",qop="auth",algorithm="SHA-256", AnyHashable("Connection"): Keep-Alive, AnyHashable("Keep-Alive"): timeout=5, max=100, AnyHashable("Expires"): Thu, 19 Nov 1981 08:52:00 GMT])
Note: If I do the same request via Postman, it works properly.
It looks like the Alamofire is not properly handling the Digest challenge.
Please could you help me on this issue?

using multipart format data in swift to send image with some parameters

I'm trying to send a picture which will be taken from camera or photo gallery in my app. I searched the whole site and found some code which helped. I then customized it a bit.
let imageData = UIImageJPEGRepresentation(myImageView.image!, 1)
if imageData != nil{
let request = NSMutableURLRequest(url: NSURL(string:"http://app.avatejaratsaba1.com/api/Ticket/SendTicket")! as URL)
var session = URLSession.shared
request.httpMethod = "POST"
let boundary = NSString(format: "---------------------------14737809831466499882746641449")
let contentType = NSString(format: "multipart/form-data; boundary=%#",boundary)
// println("Content Type \(contentType)")
request.addValue(contentType as String, forHTTPHeaderField: "Content-Type")
let body = NSMutableData()
// Title
body.append(NSString(format: "\r\n--%#\r\n",boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format:"Content-Disposition: form-data; name=\"title\"\r\n\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append("Hello World".data(using: String.Encoding.utf8, allowLossyConversion: true)!)
// Image
body.append(NSString(format: "\r\n--%#\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format:"Content-Disposition: form-data; name=\"profile_img\"; filename=\"img.jpg\"\\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "Content-Type: application/octet-stream\r\n\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append(imageData!)
body.append(NSString(format: "\r\n--%#\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
request.httpBody = body as Data
do{
let returnData = try NSURLConnection.sendSynchronousRequest(request as URLRequest, returning: nil)
let returnString = NSString(data: returnData, encoding: String.Encoding.utf8.rawValue)
print("returnString \(returnString)")
}
catch{print("gg")
}
}
Right now as I run this function, the server returns :
502 - Web server received an invalid response while acting as a
gateway or proxy server.
There is a problem with the page you are looking for, and it
cannot be displayed. When the Web server (while acting as a gateway or
proxy) contacted the upstream content server, it received an invalid
response from the content server.
This is because I'm not send 3 parameters. I wanted to know how should I add parameters (2 String, 1 Integer - in total: 2 String, 1 integer , 1 Image)
Any suggestion on how to add parameters?
Api
For better or worse, I've had to write a number of multipart mime encoders for iOS - though all in Objective-C. You can find all of the gory details about the format in the RFC. I really can't stand the format, but it's challenging to send binary data to HTTP servers without it.
It looks to me like you're almost there. You just need to add some more multipart parts to your message.
You can see some examples of what that looks like in this question.

Send body in PUT request in swift

I tried to send body in PUT request but the data is not received on backend
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "PUT"
let putBody = "bucket=\(bucket)&day=\(day)"
request.HTTPBody = putBody.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {(data, response, error) -> Void in
dispatch_async(dispatch_get_main_queue(), {() -> Void in
completion(data: data, response: response as! NSHTTPURLResponse, error: error)
})
})
task.resume()
The value of bucket (Int) is 2015040 and day (String) is day27. I tried making same request in Postman, the server received body data so there is nothing wrong with the server.
Is there any other way to set body of a request?
EDIT :
It's working perfectly if I change request method to POST in my request and server as well. So the question comes down to how to set body in PUT request?
I've just been dealing with this. Had the exact same issue where it was working perfectly fine with POST but not with PUT or PATCH requests. It seems that for PUT and PATCH requests, Swift is picky about the Content-Type header.
So try adding this:
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
This will work as long as your body is url style, so like this:
key=value&key=value&key=value
For those of you using JSON, just use application/json instead for the Content-Type.

Put request IOS

I try to update a object in the database trough a API call. I do this action with a PUT request. A Post request was already made and is working fine. I thought it is just copy paste...
The API part works fine because if I execute the following curl command than it updates the row I wanted in the database
$ curl -H 'Accept: application/json' -i -X PUT -d 'UserLogin=Ub0D&EndDate=2014-01-17 00:00:00.0000000&Sport=Fietsen&Distance=1000&Duration=10&Achieved=true&DateCreated=2015-05-09 12:01:00.000000' http://localhost:8089/api/goal/ub0d -k -u ub0d:test123
But now the part in swift gives me a bad request error (400)...
This is my code
var url : NSURL
url = NSURL(string: _apiUrl + connectToApi(apiCall) + String("\(AccountRepository.Instance.getCurrentUserName())"))!
let request = NSMutableURLRequest(URL: url)
if postString != nil {
let postData : NSData = ("UserLogin=Ub0D&EndDate=2014-01-1700:00:00.0000000&Sport=Fietsen&Distance=1000&Duration=10&Achieved=true&DateCreated=2014-01-1700:00:00.0000000").dataUsingEncoding(NSUTF8StringEncoding)!
// create the request
request.HTTPMethod = "PUT"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request.setValue("application/json", forHTTPHeaderField: "Content-type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = postData
}
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: true)!
I'm really stuck on it:(
You request can't have two different content types at the same time:
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request.setValue("application/json", forHTTPHeaderField: "Content-type")
The data you are sending are obviously not JSON, so I guess you want only
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
and remove the "application/json" line.
There is also a difference between your date formats:
CURL
EndDate=2014-01-17 00:00:00.0000000
Swift
EndDate=2014-01-1700:00:00.0000000
Note the missing space between date and time, probably should be URL encoded (+):
EndDate=2014-01-17+00:00:00.0000000
In general, most REST APIs send error messages in response body, so it is always good to log the response for failed requests.

what's the equivalent of this curl request in swift

I've tried lots of different things to recreate this curl request, and I'm going insane. If someone could help, I'd be very much appreciative.
curl -H "Content-Type: application/json" -X POST --data '[{"a": 4, "b": 5, "c": 6}]' \ -u username:password \ https://www.mywebsite.com
My latest attempt is:
let loginString = NSString(format: "%#:%#", username!, password!)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(nil)
var url: NSURL = NSURL(string: "www.mywebsite.com")!
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
request.addValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let bodyData = JSON(data!).rawString(encoding: NSUTF8StringEncoding, options: NSJSONWritingOptions.allZeros)
request.HTTPBody = bodyData?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
if let tabBarVC : TabBarController = self.tabBarController as? TabBarController {
println(NSString(data: data, encoding: NSUTF8StringEncoding))
}
}
In this example, data is a dictionary ([String:String]) and JSON is form SwiftyJSON.
The above sends NSData to the server, which is encoded and the server doesn't like. The request is getting through, but the server can't read the data right. What do I need to do to exactly replicate the curl request above? I'm stuck!
Updated to reflect good responses below.
I did attempt to use application/json for content-type, but this returns the message:
{"status": "Internal error JSON uploads must be formatted as an array of objects", "code": 400, "version": "v1"}
It seems like progress if I use text/plain (as above) I get:
{"status": "Column a is missing", "code": 400, "version": "v1"}
Which I can't seem to figure out.
A couple of thoughts:
If nothing else, the line that says:
request.addValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
Should be:
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
Your curl clearly states that the content type header was supposed to be JSON, so I'd set that accordingly.
Also, the line that says:
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Accept-encoding")
That's clearly not correct, either. I think you meant Accept not Accept-encoding, plus the server is undoubtedly not responding with a x-www-form-urlencoded response, anyway.
I would simply remove that line.
Your URL is missing the scheme (the http://), but I assume that was removed when editing your question, because I don't think it would work without that, but you assured us that the server did receive the request.
I'd suggest you consider a tool like Charles. Watch the request from curl and then again from your code. Compare and contrast and identify how they differ. Using this, you should be able to diagnose precisely what's going on and where the discrepancy rests.
You say:
The request is getting through, but the server can't read the data right.
If the problem persists, you should share with us how you know the request is getting through and what you mean by "can't read the data right."

Resources