URL Encoding issue in swift ios - ios

I am getting url from my server response as:
https://baseURL/The+Man+in+the+High+Castle+Official+Trailer+%E2%80%93+2+th.m3u8
and i am doing encoding as :
videoURL = videoURL.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
but avplayer not able to play this particular url .
seems like some issue in encoding URL

Your URL is already percent-encoded.
If you encode it again, the percent parts will be encoded twice, giving an invalid URL.
You can see that by removing the percent encoding from your URL and setting it again:
let base = "https://baseURL/The+Man+in+the+High+Castle+Official+Trailer+%E2%80%93+2+th.m3u8"
let decoded = base.stringByRemovingPercentEncoding!
print(decoded)
let encoded = decoded.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLFragmentAllowedCharacterSet())!
print(encoded)

Related

How to handle '€' in a password in a HTTP post request

I have an iOS app which sends a HTTP request for the login to our Webserver. The login basically works fine, but as soon as someone got a '€' in his password the login fails.
This bug only happens in the app. We also have a web application, which sends the same login request to the same webserver and I can perfectly log in when I do that in my browser, even if there is a '€' in my password.
Here's the function that generates the request:
func SignOn() {
var request = Helper.getURLRequest(str: str, method: "POST")
guard let httpBody = try? JSONEncoder().encode(Helper.Logon.init(domain: String(userDomain[0]), user: String(userDomain[1]), p: ""))else { return }
request.httpBody = httpBody
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
urlSession.dataTask(with: request) { (data, response, error) in
do {
guard let data = data else { throw Helper.MyError.NoConnection }
Helper.isAuthenticated = try JSONDecoder().decode(Helper.Authentication.self, from: data)
task.leave()
} catch {
[...]
}
static func getURLRequest(str: String, method: String) -> URLRequest {
let url = URL(string: str)
var request = URLRequest(url: url!)
let loginString = "\(Helper.loggedOnUserWithDomain):\(Helper.loggedOnUserPassword)"
let loginData = loginString.data(using: String.Encoding.utf8)
let base64LoginString = loginData!.base64EncodedString()
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
request.httpMethod = method
request.setValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")
return request
}
SignOn() gets called as soon as the user presses the "login" button in the app. Username and password are stored in two variables in my Helper class.
SignOn() will then call a function that generates the request - also in my Helper class.
I double checked every step in getURLRequest(). loginString and loginData both keep the € and they are perfectly displaying the character when I let Xcode print the variables.
I then checked the base64 string. Let's say someone enters "t€stpassword". The encoded base64 string should be VOKCrHN0cGFzc3dvcmQ=, which the function got right. I then let the function decode the base64 string again and checked if "t€stpassword" was the result, which again was true.
Then I checked the request with HTTP interception, but it also had the '€' in his body.
I already tried to percent escape the '€' character but that does also not work. The '€' gets percent escaped correctly, but I think the web server can't handle it then, I don't really know tbh. I used this method: how to http post special chars in swift
I'm out of ideas what I'm doing wrong here. I'm pretty new to Swift so I don't want to rule out, that I'm missing something obvious. Could the web server be the issue? But as I said, the login is working when doing it in a browser, so the server cannot be the issue, right?
According "The 'Basic' HTTP Authentication Scheme" in RFC 7617, section 3:
3. Internationalization Consideration
User-ids or passwords containing characters outside the US-ASCII
character repertoire will cause interoperability issues, unless both
communication partners agree on what character encoding scheme is to
be used. Servers can use the new 'charset' parameter (Section 2.1)
to indicate a preference of "UTF-8", increasing the probability that
clients will switch to that encoding.
Furthermore,
For the user-id, recipients MUST support all characters defined in
the "UsernameCasePreserved" profile defined in Section 3.3 of
RFC7613, with the exception of the colon (":") character.
For the password, recipients MUST support all characters defined in
the "OpaqueString" profile defined in Section 4.2 of RFC7613.
The "recipient" here is the backend. The referenced RFCs in the cited paragraphs clearly describe how the backend should process the Unicode characters and how to perform the comparison operator. You might test the server against the specification to figure out whether the server behaves correctly.
The client however, should at least check for a semicolen in either the password or user-id which would be an invalid credential for Basic HTTP Authentication.
So, your code should work, unless the backend does not want to handle Unicode. If this is the case, only allow ASCII on the client side.
When the authentication fails, a server might message the expected charset in the response in the Authenticate header:
WWW-Authenticate: Basic realm="foo", charset="UTF-8"
However, specifying a charset parameter is "purely advisory". We can't rely on the server sending this.
Basic HTTP is what the name suggests: a basic authentication scheme. It has been deprecated for a while now.
If possible, use a more secure and a more resilient authentication scheme.

Why base64 string sent as param changing length after decoding it on server side?

On the client side I am reading an image file and encoding it in base64, sending it to as an URL param.
img = open("file.png", "rb").read()
print len(img)
img = img.encode("base64")
print len(img)
print len(img.decode("base64"))
Prints 252235, 340742 and 252235.
On server side decoding the received str couldn't yield the same result. I am posting the encoded base64 as "http://url.com/test?image=img_str".
img = flask.request.args["image"]
print len(img)
img = img.decode("base64")
print len(img)
Prints 340742 which is perfectly fine and 248176 which should actually be the original length. Is image param modifying during the post request? How to do this without using files param in requests or any other solution.
So, I figured this out!
While sending the encoded string as an URL parameter, "+" in the string are converting into " ". So, had to encoded_base64.replace(" ", "+") before decoding. And it worked!

Why is one version of my http POST body not working?

I am trying to send something to an API using POST. The post body is made up of x 2 properties.
If I create the post body as one long string:
let postBody = "ministryId=nameOfMinistryHere&personId=1005" and then encode the string as follows urlRequest.httpBody = postBody.data(using: String.Encoding.utf8) it works perfectly.
But I am trying to create the post as a dictionary and then pass it to the API, but can't get it to work.
let postBody = ["ministryId":"nameOfMinistry", "personId":"1005"]
do {
try urlRequest.httpBody = JSONSerialization.data(withJSONObject: postBody, options: .prettyPrinted)
} catch {
print("problems serializing data")
}
When I use the latter option I am getting a 400 error from the server.
What am I missing?
Thanks in advance.
URLComponents is the class for dealing with multiple parameters. Code snippet:
let postBody = ["ministryId":"nameOfMinistry", "personId":"1005"]
let urlComponents = URLComponents(string: myURL)
let urlRequest = URLRequest(url: urlComponents.url!)
// transform the dictionary into queryItems
urlComponents.queryItems = postBody.map { URLQueryItem(name: $0, value: $1) }
urlRequest.httpBody = urlComponents.percentEncodedQuery?.data(using: String.Encoding.utf8)
thecloud_of_unKnowing answer to your comment as it was long i am posting it here -:
HTTP headers can be mainly classified into two types: HTTP Request Header Whenever you type a URL into the address bar and try to access it, your browser sends an HTTP request to the server. The HTTP request header contains information in a text-record form, which includes particulars such as the type, capabilities and version of the browser that generates the request, the operating system used by the client, the page that was requested, the various types of outputs accepted by the browser, and so on. HTTP Response Header Upon receiving the request header, the Web server will send an HTTP response header back to the client. An HTTP response header includes information in a text-record form that a Web server transmits back to the client's browser. The response header contains particulars such as the type, date and size of the file sent back by the server, as well as information regarding the server.SO you are just sending extra information to your server to let it know what kind of request it will accept.
Content-type: application/json; charset=utf-8 designates the content to be in JSON format, encoded in the UTF-8 character encoding. Designating the encoding is somewhat redundant for JSON, since the default (only?) encoding for JSON is UTF-8. So in this case the receiving server apparently is happy knowing that it's dealing with JSON and assumes that the encoding is UTF-8 by default, that's why it works with or without the header.
Simply make a dictionary as follows:
let jsonBody = ["username": email, "password": password]
Then you can do something like this:
let request = NSMutableURLRequest(url: NSURL(string: "YOUR URL") as URL)
request.httpBody = try! JSONSerialization.data(withJSONObject: jsonBody, options: .prettyPrinted)
Hope that helps!

How to decide if an URL should be encoded or not?

I am using URLs downloaded from the Internet, so I don't know their format in advance and they are dynamic.
How to know if I should encode an URL at run time?
// `url` is retrieved from the Internet at run time
// When should I call following line?
let urlString = url!.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLFragmentAllowedCharacterSet())
If it's already encoded, calling stringByAddingPercentEncodingWithAllowedCharacters will make the URL invalid.
Use:NSString *unencodedUrlString = [originalURL stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; to decode the URL. Now compare the decoded version with the original. If they are the same, the url is not encoded, otherwise it is already encoded.

Bad url exception when using accented characters in url

I am using AFNetworking to fetch data from the server.
When there is an accented character in my URL I get an error like this:
userInfo={"NSUnderlyingError"=>#<__NSCFError:0xfd3aa70,
description="bad URL", code=-1000, domain="kCFErrorDomainCFNetwork",
userInfo={"NSLocalizedDescription"=>"bad URL"}>,
"NSLocalizedDescription"=>"bad URL"}>
However, when I try the URL from a browser (chrome), my backend API returns the results fine.
Here is a sample URL I'm trying: http://localhost:9000/my/Jalapeños
A URL requires to be properly encoded. Given your example is a string representing a URI, it's definitely wrong.
You may take a look at NSURLComponents (available for OSX >= 10.9 and iOS >= 7.0) and RFC 3986.

Resources