Swift: How to use HTTPS proxy - swift

I have a paid proxy (HTTP/HTTPS/SOCKS).
I can make requests with HTTP scheme, but can't with HTTPS.
I always get en error:
Error Domain=kCFErrorDomainCFNetwork Code=311 "(null)" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <69F444DF-8C0E-4F4B-B723-C7BCD72B6C02>.<1>, _kCFStreamErrorDomainKey=4, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <69F444DF-8C0E-4F4B-B723-C7BCD72B6C02>.<1>"
), _kCFStreamErrorCodeKey=-2097}
I need to get and parse a few web pages from one concrete domain. I'm trying to use HTTP scheme, but it also fails (i guess due to redirect to HTTPS)
So, my goal is make HTTPS request via proxy server.
My code below:
Session configuration:
private func getProxySessionConfigration() -> URLSessionConfiguration {
let login = ConfigurationData.proxy.login
let password = ConfigurationData.proxy.password
let sessionConfiguration = URLSessionConfiguration.default
let userPasswordString = "\(login):\(password)"
let userPasswordData = userPasswordString.data(using: String.Encoding.utf8)
let base64EncodedCredential = userPasswordData!.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
let authString = "Basic \(base64EncodedCredential)"
sessionConfiguration.httpAdditionalHeaders = ["Authorization" : authString]
sessionConfiguration.connectionProxyDictionary = [
"HTTPEnable": true,
"HTTPPort": ConfigurationData.proxy.port,
"HTTPProxy": ConfigurationData.proxy.host,
"HTTPSEnable": true,
"HTTPSPort": ConfigurationData.proxy.port,
"HTTPSProxy": ConfigurationData.proxy.host
]
return sessionConfiguration
}
Request function:
public func getPage(_ url: URL, completion: #escaping (String?, RequestError?) -> Void) {
let session = URLSession(configuration: getProxySessionConfigration())
let task = session.dataTask(with: url) { (data, response, error) in
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
if let error = error {
completion(nil, .requestFailed(error))
} else {
completion(nil, .unknown(response))
}
return
}
if let data = data {
print(httpResponse)
if let urlContent = NSString(data: data, encoding: String.Encoding.utf8.self.rawValue) {
completion(urlContent as String, nil)
} else {
completion(nil, .dataDecodeFailed(data))
}
} else {
completion(nil, .noData)
}
}
task.resume()
}
Function call:
let requestAvito = AvitoProjectPlaygroud_Sources.RequestAvito()
let url = URL(string: "https://www.avito.ru/krasnodar/vakansii/voditel_v_yandeks_taksi_zarabotok_ot_1388074044")!
requestAvito.getPage(url) { (htmlString, error) in
if let error = error {
switch error {
case .noData: print("ERROR: no data")
case .unknown(let response):
print("ERROR: unknown error")
if let response = response { print(response) }
case .urlFailed: print("ERROR: url generating failed")
case .requestFailed(let returnedError): print("ERROR: \(returnedError)")
case .dataDecodeFailed(let data):
print("ERROR: data decoding failed")
print(data!)
}
return
}
if let htmlString = htmlString {
// print(htmlString)
print(String(htmlEncodedString: htmlString))
} else {
print("NO DATA")
}
}

For your issue first of all (Before ANYTHING else, make sure your proxy handle HTTPS proxying), if no then does not matter the code you put in.
To handle HTTPS Proxying there is 2 keys to use that it seems you are using it. One thing I can see is that you are using true as boolean, etc.
As per my experience I always used value such numeric or string lateral.
Make sure that data is passed as String / Int (true -> 1) etc. That could help.
Sometimes it can make some issues with those assumptions that true "will" be converted as 1.
Here below an example on how I use it:
let request = URLRequest(url: URL(string: url)!)
let config = URLSessionConfiguration.default
config.requestCachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData
config.connectionProxyDictionary = [AnyHashable: Any]()
config.connectionProxyDictionary?[kCFNetworkProxiesHTTPEnable as String] = 1
config.connectionProxyDictionary?[kCFNetworkProxiesHTTPProxy as String] = "xxx.yyy.zzz.bbb"
config.connectionProxyDictionary?[kCFNetworkProxiesHTTPPort as String] = 8888
config.connectionProxyDictionary?[kCFStreamPropertyHTTPSProxyHost as String] = "xxx.yyy.zzz.bbb"
config.connectionProxyDictionary?[kCFStreamPropertyHTTPSProxyPort as String] = 8888
So in short :
Verify first your proxy (making sure https proxying is handled)
and then pass data as above
it may solve your issue hopefully.

Related

cannot read data from Json hosted on GitHub

when try to read data from a json on my GitHub space, I get an error
nw_protocol_get_quic_image_block_invoke dlopen libquic failed
Invalid response from the server. Please try again.
am I using wrong url or there is an issue in the way I'm parsing data?
my repo url
https://github.com/stark226/stark226.github.io.git
where there is a simple json file at this url
https://github.com/stark226/stark226.github.io/blob/3b2bebb4a3d85524732c7e7ec302b24f8d3e66ae/testjson.json
in my viewDidload
getDataFromJsonOnGithub(completed: { [weak self] result in
guard let self = self else {return}
switch result {
case .success(let expected):
print(expected)
case .failure(let error):
print(error.rawValue)
}
})
my struct
struct RemoteData: Codable, Hashable {
var tokenDuration: Int
}
my func
func getDataFromJsonOnGithub(completed: #escaping (Result<RemoteData, ListOfErrors>) -> Void) {
let endpoint = "https://github.com/stark226/stark226.github.io/stark226/testjson.json"
guard let url = URL(string: endpoint) else {
completed(.failure(.invalidUsername))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let expectedRrsult = try decoder.decode(RemoteData.self, from: data)
completed(.success(expectedRrsult))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
enum ListOfErrors: String, Error {
case invalidUsername = "This username created an invalid request. Please try again."
case unableToComplete = "Unable to complete your request. Please check your internet connection"
case invalidResponse = "Invalid response from the server. Please try again."
case invalidData = "The data received from the server was invalid. Please try again."
case unableToFavorite = "There was an error favoriting this user. Please try again."
case alreadyInFavorites = "You've already favorited this user. You must REALLY like them!"
}
You have to access the raw file. The URL you are accessing renders an HTML page. Try the following url (click on "Raw" button in GitHub):
https://raw.githubusercontent.com/stark226/stark226.github.io/3b2bebb4a3d85524732c7e7ec302b24f8d3e66ae/testjson.json

Passing JSON Decode error into something actionable

I'm trying to improve my error handling when I don't get back any data in my JSON. The function below works fine without any issues except if I catch an error. I'm not sure of how to pass the error to prompt the user or is the way to handle the error if googlebusinessinfo is nil I likely have no data and pass a generic error?
Here's my function:
func getBusinessReviews(googleUrl: String, completion: #escaping (WelcomeReview?) -> ()) {
// Create URL
let url = URL(string: googleUrl)
guard let requestUrl = url else { fatalError() }
// Create URL Request
var request = URLRequest(url: requestUrl)
// Specify HTTP Method to use
request.httpMethod = "GET"
//Set HTTP Header
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Specify HTTP Method to use
request.httpMethod = "GET"
print("(Google)Do we have any data: \(String(describing: data))")
// Check if Error took place
if let error = error {
print("Error took place \(error)")
return
}
// Read HTTP Response Status code
if let response = response as? HTTPURLResponse {
print("(Google)Response HTTP Status code: \(response.statusCode)")
}
// Convert HTTP Response Data to a simple String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Googledata Response data string:\n \(dataString)")
}
if let googlebusinessdata = data {
do {
let googlebusinessinfo = try JSONDecoder().decode(WelcomeReview.self, from: googlebusinessdata)
print("googledata \(googlebusinessinfo)")
completion(googlebusinessinfo)
} catch
{
print("googledata error \(error.localizedDescription)")
let googebusinessinfoerror = error.localizedDescription
}
}
}
task.resume()
}
First of all you can improve the error handling immensely if you print error instead of error.localizedDescription in a Decoding catch block. The former shows the real error, the latter a meaningless generic message.
To answer your question use the generic Result type
func getBusinessReviews(googleUrl: String, completion: #escaping (Result<WelcomeReview,Error>) -> ()) {
// Create URL
let url = URL(string: googleUrl)
guard let requestUrl = url else { fatalError() }
// Create URL Request
var request = URLRequest(url: requestUrl)
// Specify HTTP Method to use
request.httpMethod = "GET"
//Set HTTP Header
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Specify HTTP Method to use
request.httpMethod = "GET"
print("(Google)Do we have any data: \(String(describing: data))")
// Check if Error took place
if let error = error {
print("Error took place \(error)")
completion(.failure(error))
return
}
// Read HTTP Response Status code
if let response = response as? HTTPURLResponse {
print("(Google)Response HTTP Status code: \(response.statusCode)")
}
// Convert HTTP Response Data to a simple String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Googledata Response data string:\n \(dataString)")
do {
let googlebusinessinfo = try JSONDecoder().decode(WelcomeReview.self, from: data)
print("googledata \(googlebusinessinfo)")
completion(.success(googlebusinessinfo))
} catch
{
print("googledata error:", error)
let googebusinessinfoerror = error.localizedDescription
completion(.failure(error))
}
}
}
task.resume()
}
and call it
getBusinessReviews(googleUrl: "Foo") { result in
switch result {
case .success(let info): print(info)
case .failure(let error): print(error)
}
}

Stripe: Registered webhook but still getting "Not a Valid URL" error

I seem to be running into an error each time I try to make a payment transaction where Stripe declines the operation by saying "Not a valid URL". The client-side is in Swift, while the server is in Python, deployed on Heroku.
A suggestion I saw on another post was to register my server webhook with my Stripe account, which I did, but it doesn't seem to solve the problem.
For reference, here is my createPaymentIntent function:
func createPaymentIntent(dict: [String:Any]) {
let url = self.baseURL.appendingPathComponent("create-payment-intent")
let params = dict
let jsondata = try? JSONSerialization.data(withJSONObject: params)
var clientSecretOut = ""
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsondata
let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in
guard let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
let clientSecret = json["clientSecret"] as? String else {
let message = error?.localizedDescription ?? "Failed to decode response from server."
print("Error: ", message)
return
}
clientSecretOut = clientSecret
print("client out inside: ", clientSecretOut)
self?.clientSecretFinal = clientSecret
})
task.resume()
}
And here is the place where the error seems to be called– my didCreatePaymentResultFunction:
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPPaymentStatusBlock) {
let paymentIntentParams = STPPaymentIntentParams(clientSecret: self.clientSecretFinal)
paymentIntentParams.configure(with: paymentResult)
paymentIntentParams.returnURL = "meURLStripeTest://"
STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: paymentContext) { status, paymentIntent, error in
switch status {
case .succeeded:
print("success")
completion(.success, nil)
case .failed:
print("cant", error)
completion(.error, error)
case .canceled:
completion(.userCancellation, nil)
#unknown default:
completion(.error, nil)
}
}
}
Thanks for your help!
The problem is in this line:
paymentIntentParams.returnURL = "meURLStripeTest://"
The returnURL has to be a valid URL or application URI scheme, in your case it's neither. See here on how to create an application URI scheme for your app.

unable to have a result while calling a rest API with Swift

I try to make a small command line program in swift 5 to call a Rest Web service
func callRest() {
URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
let todoEndpoint: String = "http://myURL"
print (todoEndpoint)
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
exit(1)
}
// private let apiKey = "my API Key"
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Accept": "application/json",
"API-Key": "my API Key"
]
var session = URLSession.shared
session = URLSession(configuration: config)
let urlRequest = URLRequest(url: url)
let task = session.dataTask(with: urlRequest) { (data, response, error) in
print ("task")
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// check the status code
guard let httpResponse = response as? HTTPURLResponse else {
print("Error: It's not a HTTP URL response")
return
}
print (httpResponse)
}
task.resume()
// Response status
}
print ("debut")
callRest()
print("fin")
And I never see a result...
the line print ("task") is never display
Any help appreciate.
I think I don't really understand the task.resume usage...
(same code work in a playground)

Error in POST Request in swift

I wanted to be a type of variable to send to the dictionary server but But on the line I was faced with the problem let task = session.dataTaskWithRequest(todosUrlRequest) error : Cannot convert value of type 'NSURL' to expected argument type 'NSURLRequest'
I had two questions
1) What is this error?
2)Is there a procedure that I used for POST is that right? doesn't need anything else. ??
thank you for help
func data_request (){
let url = "http://sample.com/api/Flight/GetTicketInformation"
guard let todosUrlRequest = NSURL(string: url) else {
print("Error: cannot create URL")
return
}
let request = NSMutableURLRequest(URL: todosUrlRequest)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let newTodo = ["Roundtrip": roundTrip,
"OneWay": oneWay,
"MultiWay": multiWay,
"Adult": numberAdults,
"Child": numberchild,
"Baby": numberinfant,
"SourceCityId": cityIDOrigin,
"DestinationCityId": cityIDPurpose,
"DepartingDate": raftDate,
"ReturningDate": bargashtDate ]
let jsonTodo: NSData
do {
jsonTodo = try NSJSONSerialization.dataWithJSONObject(newTodo, options: [])
request.HTTPBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
request.HTTPBody = jsonTodo
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(todosUrlRequest) {
(data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("error calling POST on /todos/1")
print(error)
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try NSJSONSerialization.JSONObjectWithData(responseData,
options: []) as? [String: AnyObject] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
request instead of todosUrlRequest on the line let task = session.dataTaskWithRequest(todosUrlRequest)
for the second question, no idea . sorry
I can recommend you Alamofire for all requests, instead of writing all code on your own.
https://github.com/Alamofire/Alamofire