Unable to connect to remote RabbitMQ server using Swift - ios

I am using RabbitMQ in my app for chat module. It works fine with local server but somehow I am not able to connect to remote RabbitMQ server. I keep getting this error when I try to send a message.
Received connection: <RMQConnection: 0x6000022e2eb0>
disconnectedWithError: Error Domain=GCDAsyncSocketErrorDomain Code=7
"Socket closed by remote peer" UserInfo={NSLocalizedDescription=Socket
closed by remote peer}
My swift code looks like this:
func getRabbitMQUrl() -> String{
var components = URLComponents()
components.scheme = "amqps"
components.host = "[broker-id].mq.[region].amazon.aws.com"
components.user = "[username]"
components.password = "[passowrd]"
components.port = 5671
let url = components.url?.absoluteString ?? "-"
print("RabbitMQ URL", url)
return url
}
let uri = getRabbitMQUrl()
let conn = RMQConnection(uri: uri, delegate: RMQConnectionDelegateLogger())
conn.start()
let ch = conn.createChannel()
let q = ch.queue(UUID().uuidString, options: .durable)
let exc = ch.direct("my-exchange-name-here")
q.bind(exc, routingKey: "my-routing-key")
q.subscribe({(_ message: RMQMessage) -> Void in
print("Message received")
})
While using the local server, I set the uri "amqp://[username]:[password]#localhost:5672" and this works as expected.
PS: when I set this subscriber I do not get any error message regarding connection or anything. so I think it is connecting to the server without any issue.
But, when I send a message from the iOS app, the backend publish it and so the iOS app should receive it back. Exactly at this time, it gives me the above error!
EDIT: Though the C# backend is able to publish and subscribe messages successfully with RabbitMQ remote server. It is just the iOS client who fails!
Any help would be appreciated!

After going through a lots of links, slack channels and Github issues, finally the issue has been resolved! The solution was unexpected.
The problem was, my C# backend has set the vhost to a slash / and in my Swift code I was passing an empty string instead. I got hint from here
I made these 2 changes in my code:
In server uri I added %2f(a slash /) as the vhost at the end.
I set the options of the exchange also to .durable just like the queue
Here is the complete working code:
func getRabbitMQUrl() -> String{
var components = URLComponents()
components.scheme = "amqps"
components.host = "[broker-id].mq.[region].amazon.aws.com"
components.user = "[username]"
components.password = "[passowrd]"
components.port = 5671
components.path = "/%2f" //1st change
let url = components.url?.absoluteString ?? "-"
print("RabbitMQ URL", url)
return url
}
let uri = getRabbitMQUrl()
let conn = RMQConnection(uri: uri, delegate: RMQConnectionDelegateLogger())
conn.start()
let ch = conn.createChannel()
let q = ch.queue(UUID().uuidString, options: .durable)
let exc = ch.direct("my-exchange-name-here", options: .durable) // 2nd change
q.bind(exc, routingKey: "my-routing-key")
q.subscribe({(_ message: RMQMessage) -> Void in
print("Message received")
})

Your URI is AMQP on the local host but AMQP is the example code.
You should connect to port 5671 if you are using AMQPS (And 5672 if you are on AMQP) Try that!

Related

iOS - TunnelKit OpenVPNTunnelProvider ProviderConfigurationError

I am trying to create an ios vpn client using Tunnelkit. I am following this tutorial.
https://github.com/passepartoutvpn/tunnelkit
am able to compile and run the application, but when I try to connect, the app crashes and throwing.
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error:
TunnelKit.OpenVPNTunnelProvider.ProviderConfigurationError.credentials(details:
"keychain.set()")
Anyone who had already set up tunnel kit OpenVPN, please help to resolve this issue.
func connect() {
let server = textServer.text!
let domain = textDomain.text!
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
let port = UInt16(textPort.text!)!
let socketType: SocketType = switchTCP.isOn ? .tcp : .udp
let credentials = OpenVPN.Credentials(textUsername.text!, textPassword.text!)
let cfg = Configuration.make(hostname: hostname, port: port, socketType: socketType)
let proto = try! cfg.generatedTunnelProtocol(
withBundleIdentifier: tunnelIdentifier,
appGroup: appGroup,
credentials: credentials
)
let neCfg = NetworkExtensionVPNConfiguration(title: "new title", protocolConfiguration: proto, onDemandRules: [])
vpn.reconnect(configuration: neCfg) { (error) in
if let error = error {
print("configure error: \(error)")
return
}
}
}
You need to follow the integration steps.
https://github.com/passepartoutvpn/tunnelkit#demo
Enable App Groups and Keychain Sharing capabilities
make sure the appGroup value is the same which you set in your target/Signings & Capabilities/App Groups

GRPC-Swift Send HTTPProtocolVersion from Client

I'm trying GRPC-Swift for Client-Server application.
I'm using GRPC-Swift for both Client and Server
Client is an iPhone application, which I tried with iPhone Simulator.
I followed this link for Client-side streaming RPC.
When I send message to Server from Client, I got the following error message in the console from Server,
error io.grpc.server_channel_call : unable to determine http version
From the Server in the
HTTPProtocolSwitcher.swift
inside the function func channelRead(context: ChannelHandlerContext, data: NIOAny), it is checking for HTTPProtocolVersion, and it is missing.
How to send the HTTPVersion from the Client code?
Update:
Client Code
import GRPC
import NIO
class HTTPClient {
private let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
private var channel: ClientConnection?
private var client: ChatGuide_ChatGuideClient?
private var clientCall: ClientStreamingCall<ChatGuide_TextMessage, ChatGuide_TextMessage>?
func connect(host: String, port: Int) throws {
let channel = ClientConnection.secure(group: self.group)
.connect(host: host, port: port)
self.channel = channel
self.client = ChatGuide_ChatGuideClient(channel: channel)
}
func disconnect() {
do {
self.clientCall?.sendEnd(promise: nil)
_ = try self.clientCall?.status.wait()
try self.group.syncShutdownGracefully()
} catch let error {
print("\(type(of: self)): Could not shutdown gracefully -", error.localizedDescription)
}
}
func initiateClient() {
let timeAmount = TimeAmount.minutes(1)
let timeLimit = TimeLimit.timeout(timeAmount)
let options = CallOptions(timeLimit: timeLimit)
let call = self.client?.chat(callOptions: options)
call?.response.whenSuccess { (message) in
print("\(type(of: self)): Message from server -", message.text)
}
call?.response.whenFailure { (error) in
print("\(type(of: self)): Response error -", error.localizedDescription)
}
self.clientCall = call
}
func send(text: String) {
if self.clientCall == nil {
self.initiateClient()
}
let message = ChatGuide_TextMessage.with {
$0.text = text
}
self.clientCall?.sendMessage(message, promise: nil)
}
}
Hey Vignesh,
I am currently learning gRPC-Swift myself, so I hope I will be of service and not muck things further.
However, it looks to me that you are not configuring the HTTP/1.x layer in order to transfer Protobuf packets, if you take a look at the HTTP1ToGRPCServerCodec.swift file Here
I think you will have a much clearer idea of how to adjust your code, I am sorry I can't provide more details, however not being too sure myself without further testing and reviewing the codebase.
Best regards and keep me posted if indeed i was helpful,
cheers
From the Server I have initiated insecure Server as,
let server = Server.insecure(group: self.group)
From the Client I have initiated secure ClientConnection as,
let channel = ClientConnection.secure(group: self.group)
And I got this clarification from here
So I made the ClientConnection also insecure as,
let channel = ClientConnection.insecure(group: self.group)
And after this it is working now.

SFSpeechRecognizer online: at which url is sent the request?

I'm developing an iOS application in Swift that uses SFSpeechRecognizer (package: Speech).
This application is developed for an organization that uses VPN and blocks every request from/to the external network.
This is partial code:
let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: self.userLanguageExt))
let recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
let recognitionTask = self.speechRecognizer?.recognitionTask(with: self.recognitionRequest!,resultHandler: { (result, error) in
if let result = result{
...
}
else if error == nil {
...
}
})
I need to know from/to which url is received/sent the RecognizerRequest so I can comunicate it to network team of the organization and they will open the connection to those url. On docs I couldn't find a lot.
Answer
17.250.13.5 (if you check the ip lookup on https://www.iplocation.net/ you find that is an Apple domain)
How
I've done tcpdump on my device following this documentation and catched the packets traffic: https://developer.apple.com/documentation/network/recording_a_packet_trace

How to set AWS Appsync request timeout limit || AWSAppSync Client not giving callback

I'm using AWS Appsync for the current App I'm developing and facing a serious issue that is Whenever I fire queries in Appsync client, when there is slow internet connection the request never end with a callback. I checked over internet there is limited source of information on this topic and also found this issue that is still open.
This is the code I used to get the response
func getAllApi(completion:#escaping DataCallback){
guard isInternetAvailabele() else {
completion(nil)
return
}
// AppSyncManager.Client() is AWSAppSyncClient Object
AppSyncManager.Client().fetch(query: GetlAllPostQuery(input: allInputs), cachePolicy:.fetchIgnoringCacheData) {
(result, error) in
var haveError:Bool = error != nil
if let _ = result?.data?.getAllPostings?.responseCode {haveError = false} else {haveError = true}
if haveError {
print(error?.localizedDescription ?? "")
completion(nil)
return
}
if result != nil{
completion(result)
}else{
completion(nil)
}
}
}
The code works fine with internet connection and I have already checked at the top if there is no internet but when there is slow internet connection or the wifi is connected to a hotspot that I created with my mobile with internet data disabled the request doesn't return any callback it should give failed alert like we get in other apis when the request time out.
Is there any support for request for request time out or did I miss something?
Note : I recieved these logs in Terminal
Task <06E9BBF4-5731-471B-9B7D-19E5E504E57F>.<45> HTTP load failed (error code: -1001 [1:60])
Task <D91CA952-DBB5-4DBD-9A90-98E2069DBE2D>.<46> HTTP load failed (error code: -1001 [1:60])
Task <06E9BBF4-5731-471B-9B7D-19E5E504E57F>.<45> finished with error - code: -1001
Task <D91CA952-DBB5-4DBD-9A90-98E2069DBE2D>.<46> finished with error - code: -1001
Actually there could be two possible ways to fix the issue,
1) While configuring AWSAppSyncClientConfiguration, provide a custom URLSessionConfiguration and set the request timeout to your needs,
extension URLSessionConfiguration {
/// A `URLSessionConfiguration` to have a request timeout of 1 minutes.
static let customDelayed: URLSessionConfiguration = {
let secondsInOneMinute = 60
let numberOfMinutesForTimeout = 1
let timoutInterval = TimeInterval(numberOfMinutesForTimeout * secondsInOneMinute)
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = timoutInterval
configuration.timeoutIntervalForResource = timoutInterval
return configuration
}()
}
And pass this session configuration i.e URLSessionConfiguration.customDelayed when initializing AWSAppSyncClientConfiguration as it accepts the URLSessionConfiguration in the below constructor,
public convenience init(url: URL,
serviceRegion: AWSRegionType,
credentialsProvider: AWSCredentialsProvider,
urlSessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default,
databaseURL: URL? = nil,
connectionStateChangeHandler: ConnectionStateChangeHandler? = nil,
s3ObjectManager: AWSS3ObjectManager? = nil,
presignedURLClient: AWSS3ObjectPresignedURLGenerator? = nil) throws {
2) If the first doesn't work then you have another option to edit/unlock the pod files directly. There is a class AWSAppSyncRetryHandler where you can change the logic for retrying request. If you are able to fix the issue then you can fork the original repo, clone your repo, make changes in your repo and in pods file point this pod to use your repository. This should be done as changing the pod files directly is absolutely wrong until you are really stuck and want to find some solution.
Update: This issue has been fixed with AppSync SDK 2.7.0

TIC TCP Conn Failed when running Swift project on my phone

I am new to Swift. I built a simple application which works fine on the simulator. I am running the same on my device (iPhone 6s with iOS 11.0.2) and it's failing to connect to server.
getting these errors:
2017-10-26 18:16:02.489134-0400 myproj[1451:206438] TIC TCP Conn Failed [1:0x1c0176800]: 1:61 Err(61)
2017-10-26 18:16:02.489771-0400 myproj[1451:206438] Task <0C30ADDC-4A0E-4815-A701-2EF0A7CF5F04>.<1> HTTP load failed (error code: -1004 [1:61])
2017-10-26 18:16:02.490293-0400 myproj[1451:206440] Task <0C30ADDC-4A0E-4815-A701-2EF0A7CF5F04>.<1> finished with error - code: -1004
Please help me understand this error.
EDIT:
Here is the code making that call to the server:
func postRequest(postData: NSDictionary, postHeaders: NSDictionary, endPoint: String,
onComplete: #escaping ((NSDictionary)->Void), callbackParams: NSDictionary = NSMutableDictionary()) {
let url:URL = baseUrl.appendingPathComponent(endPoint)
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
var paramString = ""
for (key, value) in postData{
paramString = paramString + (key as! String) + "=" + (value as! String) + "&"
}
request.allHTTPHeaderFields = postHeaders as? [String : String]
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return}
let json: Any?
do {
json = try JSONSerialization.jsonObject(with: data!, options: [])
}
catch {
return
}
var serverResponse = json as? NSDictionary
DispatchQueue.main.async{
for (key, value) in serverResponse!{
callbackParams.setValue(value, forKey: key as! String)
}
onComplete(callbackParams)
}
})
task.resume()
}
EDIT:
Thanks!
Error -1004 is URLError.cannotConnectToHost. It cannot connect to the server for some reason.
In comments, you say the URL is http://127.0.0.1. That is localhost, the current machine. If you use that URL on a physical phone, it's going to look for the web server on the phone. Lol. It works on the simulator, because the simulator is your computer, the localhost.
You need a URL that your iPhone can resolve to the machine running your web service. For example, find out what the IP for the computer running the web service on your local network, make sure your iPhone is on wifi on the same network, and then use that unique IP number for your computer on the LAN (likely something more like 192.168.0.x).
I was getting the same problem when i was sending/ receiving socket messages! I am using Socket.io and Firebase for push and the error was at the node.js backend. The error was that the mongoose version was deprecated. Whenever this error arises at the iOS End, ask the node.js backend team to look for Red color crash logs. They will surely find the problem causing this error on frontend. Hope this helps !!
In my case i'm not changes the Local host IP address.
Example : #"http://Localhost/Host name/index.pnp/apiname?";
initially i'm used above URL, so i'm got the same error...
Later i changed in to Localhost to 190.177.0.22
Example : #"http://190.177.0.22/Host name/index.pnp/apiname?";
Another corner case if you using something like ngrok and it's still not working is the phone isn't connected to a service provider network or wifi.
In my case my main phone was working the second out of service phone was not.

Resources