Ktor-websocket library do nothing when trying to receive data on client - kotlin-multiplatform

I am currently trying to connect our Kotlin Multiplatform Project to websockets. I would like to use ktor-websockets library to receive some updates from our backend but onfortunately when I run this code, nothing happens:
client.webSocket(
port = 80,
method = HttpMethod.Get,
host = "https://uat-betws.sts.pl",
path = "/ticket?token=eyJzdWIiOiI0ZmY5Y2E1Mi02ZmEwLTRiYWYtODlhYS0wODM1NGE2MTU0YjYiLCJpYXQiOjE2MTk4MDAwNzgsImV4cCI6MTYxOTgwMzY3OH0.oIaXH-nFDpMklp4FSJWMtsM7ECSIfuNF99tTQxiEALM"
)
{
for (message in incoming) {
message as? Frame.Text ?: continue
val receivedText = message.readText()
println(receivedText)
}
// Consume all incoming websockets on this url
this.incoming.consumeAsFlow().collect {
logger.d("Received ticket status websocket of type ${it.frameType.name}")
if (it is Frame.Text) {
Json.decodeFromString<TicketStatusResponse>(it.readText())
}
}
}
Does somebody have any experience with ktor-websockets library? There is almost no documentation so maybe I am doing something wrong.
Thank you

As the documentation says
Ktor provides Websocket client support for the following engines: CIO, OkHttp, Js.
This means that it works only on JVM/JS, you're probably targeting iOS. It's not yet supported, you can follow issue KTOR-363 for updates
For sure the team is working on it, but for now you had to implement it by yourself using expect/actual, you can check out official example
An other possible problem in your code: host shouldn't include https://, if you're using ssl, you should add an other parameter:
request = {
url.protocol = URLProtocol.WSS
}
Or use client.wss(...) - which is just a short form for the same operation

Related

(iOS Cordova) Accessing local files from remote - WKWebview

Having the following variables:
remote web presented in cordova
local files that the remote web asks for with the following code:
var url = "https://cdvfile/localhost/" + localFolder + "/www/cordova.js";
var element = document.createElement('script');
element.id = "cordova";
element.type = "text/javascript";
element.onerror = function () {
//error
}
element.onload = function () {
//success - code to be executed upon success
}
element.src = url;
document.body.appendChild(el);
This fails in WKWebView with the obvious error
[Error] Failed to load resource: A server with the specified hostname
could not be found. (cordova.js, line 0)
As you know, WKUrlSchemeHandler doesn't intercept http/https requests. An alternative is to use the dangerous [NSURLProtocol wk_registerScheme:#"https"]; private API trick (and it works but then it somehow screws up the request to load the page that includes the code above (doesn't add some cookies and some weird behavior).
I do have another alternative to inject via [userContentController addUserScript:script] but this requires modifying the remote web part in order to execute the code that follows the success of the script injection request.
I know it was previously possible to do all this with cdvfile:// in UIWebView but I am looking for a way to do all this WITHOUT modifying the remote (meaning, the url has to stay as you see it above. I've racked my brains for a few months now with this but can't come up with a solution. Please don't ask why I'm doing this or say that this is stupid etc, I have no choice, it's what I gotta do and it doesn't depend on me.
Please send help, thoughts, prayers etc
Thanks

Swift-NIO secured websocket server

I am trying to create websocket server and client in my iOS app, which i successfully managed to do with the help of sample implementation here. (https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer) - so current working situation is, i run the websocket server when app launches and then I load the client in a webview which can connect to it.
Now my problem is I want my server to secured websocket server (Basically connect to the websocket server from a HTTPS html page)
I am new to network programming and Swift-nio documentation is lacking to say the least. As far as I understand I could use (https://github.com/apple/swift-nio-transport-services)
I found this thread which is exactly what I need - https://github.com/apple/swift-nio-transport-services/issues/39 - I could disable the TLS authentication as I dont care in my usecase as long as I could get the websocket connected.
So my question is how to I extend my client (https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketClient) and server (https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer) to use swift-nio-transport-service.
I could add the NIOSSLContext and stuff but I think I need to add the EventLoopGroup and new bootstrap methods. I know the answers is right there.... but I just cannot seem to pinpoint it.
Any pointer would be appreciated.
Thanks.
To translate a simple NIO Server to a NIOTransportServices one, you need to make the following changes:
Add a dependency on NIOTransportServices to your server.
Change MultiThreadedEventLoopGroup to NIOTSEventLoopGroup.
Change ClientBootstrap to NIOTSConnectionBootstrap.
Change ServerBootstrap to NIOTSListenerBootstrap.
Build and run your code.
Some ChannelOptions don’t work in NIOTransportServices, but most do: the easiest way to confirm that things are behaving properly is to quickly test the common flow.
This doesn’t add any extra functionality to your application, but it does give you the same functionality using the iOS APIs.
To add TLS to either NIOTSConnectionBootstrap or NIOTSListenerBootstrap, you use the .tlsOptions function. For example:
NIOTSListenerBootstrap(group: group)
.tlsOptions(myTLSOptions())
Configuring a NWProtocolTLS.Options is a somewhat tricky thing to do. You need to obtain a SecIdentity, which requires interacting with the keychain. Quinn has discussed this somewhat here.
Once you have a SecIdentity, you can use it like so:
func myTLSOptions() -> NWProtocolTLS.Options {
let options = NWProtocolTLS.Options()
let yourSecIdentity = // you have to implement something here
sec_protocol_options_set_local_identity(options.securityProtocolOptions, sec_identity_create(yourSecIdentity)
return options
}
Once you have that code written, everything should go smoothly!
As an extension, if you wanted to secure a NIO server on Linux, you can do so using swift-nio-ssl. This has separate configuration as the keychain APIs are not available, and so you do a lot more loading of keys and certificates from files.
I needed a secure websocket without using SecIdentity or NIOTransportServices, so based on #Lukasa's hint about swift-nio-ssl I cobbled together an example that appears to work correctly.
I dunno if it's correct, but I'm putting it here in case someone else can benefit. Error-handling and aborting when the try's fail is left out for brevity.
let configuration = TLSConfiguration.forServer(certificateChain: try! NIOSSLCertificate.fromPEMFile("/path/to/your/tlsCert.pem").map { .certificate($0) }, privateKey: .file("/path/to/your/tlsKey.pem"))
let sslContext = try! NIOSSLContext(configuration: configuration)
let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture<Void> = { channel, req in
WebSocket.server(on: channel) { ws in
ws.send("You have connected to WebSocket")
ws.onText { ws, string in
print("Received text: \(string)")
}
ws.onBinary { ws, buffer in
// We don't accept any Binary data
}
ws.onClose.whenSuccess { value in
print("onClose")
}
}
}
self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let port: Int = 5759
let promise = self.eventLoopGroup!.next().makePromise(of: String.self)
_ = try? ServerBootstrap(group: self.eventLoopGroup!)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelInitializer { channel in
let handler = NIOSSLServerHandler(context: sslContext)
_ = channel.pipeline.addHandler(handler)
let webSocket = NIOWebSocketServerUpgrader(
shouldUpgrade: { channel, req in
return channel.eventLoop.makeSucceededFuture([:])
},
upgradePipelineHandler: upgradePipelineHandler
)
return channel.pipeline.configureHTTPServerPipeline(
withServerUpgrade: (
upgraders: [webSocket],
completionHandler: { ctx in
// complete
})
)
}.bind(host: "0.0.0.0", port: port).wait()
_ = try! promise.futureResult.wait()
try! server.close(mode: .all).wait()

MQTT on Parse platform (Open source parse Server)

Hi I am using the hosted version of the open source parse platform (hosed version on Back4app) for my IoT project. Am using HTTP (REST) Api to communicate with the parse server and upload data. Does anyone know if it is possible to use the MQTT protocol instead of HTTP for the same with the parseplatform. I couldn't find any relevant doc for this. Apparently there's a way to install the MQTTjs on cloud code section of the platform but do not know if this really works ... Thanks in advance
Yes, it's possible, I just tested it now and it worked for me. Here are the steps that you need to follow:
1 - You only need to install this npm module as you can see at this guide.
Here is my package.json:
{
"dependencies": {
"mqtt": "2.18.8"
}
}
2 - After that, on Back4app, you need to upload the code in your cloud code and check your Server System Logs at Server Settings > Logs > Settings.
Here's a simple code that you can use to test it. I put this code in my main.js:
var mqtt = require('mqtt')
var client = mqtt.connect('mqtt://test.mosquitto.org')
client.on('connect', function () {
client.subscribe('presence', function (err) {
if (!err) {
client.publish('presence', 'Hello mqtt')
}
})
})
client.on('message', function (topic, message) {
// message is Buffer
console.log(message.toString())
client.end()
});

Xamarin PCL that makes requests to REST service and returns models

So as recommended I'd like to use RestSharp to handle REST web service. I am developing iOS and Android application and I would love to make PCL that makes requests to the service and just returns parsed results (eg. array of User objects).
So how do I get RestSharp in my PCL, tried NuGet, components are not for PCLs and seriously bad would be to just download source files and copy them in the project, I want to keep some dependency management in place.
What is the best practice? Am I looking at this problem at wrong angle?
RestSharp doesn't support PCLs. I'd suggest checking out PortableRest, or just using a combination of HttpClient and Json.NET.
I use dependency injection so I can support non-PCL JSON parsers. I also plan to give the native HttpClient wrappers from the component store a try. By using non-PCL code you will gain quite a lot in performance compared to Json.NET etc.
Link to source code
Text library has serializer interfaces, Web has the IRestClient.
Modern HTTP Client from the component store.
Below modifications worked for me and will be glad if it works out to you.
Try using modernhttpclient in your PCL. And inAndroid project ensure you have the below packages.
Microsoft.Bcl.Build
Microsoft.Bcl
Microsoft.Net.Http
modernhttpclient
Along with that in application manifest under required permissions give permissions to the below.
Access_Network_State
Access_wifi_state
Internet
Ideally when you try to add Microsoft.Bcl into your Android project targettting monoandroid it will throw out error, so try to add the nuget refrence in the above order.
I developed a really simple REST client to perform Http requests easily. You can check it on my Github repo. The API is really simple:
await new Request<T>()
.SetHttpMethod(HttpMethod.[Post|Put|Get|Delete].Method) //Obligatory
.SetEndpoint("http://www.yourserver.com/profilepic/") //Obligatory
.SetJsonPayload(someJsonObject) //Optional if you're using Get or Delete, Obligatory if you're using Put or Post
.OnSuccess((serverResponse) => {
//Optional action triggered when you have a succesful 200 response from the server
//serverResponse is of type T
})
.OnNoInternetConnection(() =>
{
// Optional action triggered when you try to make a request without internet connetion
})
.OnRequestStarted(() =>
{
// Optional action triggered always as soon as we start making the request i.e. very useful when
// We want to start an UI related action such as showing a ProgressBar or a Spinner.
})
.OnRequestCompleted(() =>
{
// Optional action triggered always when a request finishes, no matter if it finished successufully or
// It failed. It's useful for when you need to finish some UI related action such as hiding a ProgressBar or
// a Spinner.
})
.OnError((exception) =>
{
// Optional action triggered always when something went wrong it can be caused by a server-side error, for
// example a internal server error or for something in the callbacks, for example a NullPointerException.
})
.OnHttpError((httpErrorStatus) =>
{
// Optional action triggered when something when sending a request, for example, the server returned a internal
// server error, a bad request error, an unauthorize error, etc. The httpErrorStatus variable is the error code.
})
.OnBadRequest(() =>
{
// Optional action triggered when the server returned a bad request error.
})
.OnUnauthorize(() =>
{
// Optional action triggered when the server returned an unauthorize error.
})
.OnInternalServerError(() =>
{
// Optional action triggered when the server returned an internal server error.
})
//AND THERE'S A LOT MORE OF CALLBACKS THAT YOU CAN HOOK OF, CHECK THE REQUEST CLASS TO MORE INFO.
.Start();

rails responding to cross domain request developing locally, spotify app development

So Im messing around with developing a spotify app, trying to get it to talk to my local rails application API. I cant get anything other than a req.status 0 when I try it.
I think its either a problem with the spotify manifest.json file, not allowing the port:3000 to go on the url you set in required permissions, but it also says the following in their documentation.
https://developer.spotify.com/technologies/apps/tutorial/
If you need to talk to an outside web API you're welcome to, as long as you abide by the rules set in the Integration Guidelines. Please note that when talking with a web API, the requests will come from the origin sp://$APPNAME (so sp://tutorial for our example) - make sure the service you are talking to accepts requests from such an origin.
So, Im not sure if rails is set to not allow this sort of thing, or if its an issue with the putting the port into the required permissions, but my request
var req = new XMLHttpRequest();
req.open("GET", "http://127.0.0.1:3000/api/spotify/track/1.json", true);
console.log(req);
req.onreadystatechange = function() {
console.log(req.status);
console.log(req.readyState);
if (req.readyState == 4) {
if (req.status == 200) {
console.log("Search complete!");
console.log(req.responseText);
}
}
};
req.send();
Always returns status 0 where as their example:
var req = new XMLHttpRequest();
req.open("GET", "http://ws.audioscrobbler.com/2.0/?method=geo.getevents&location=" + city + "&api_key=YOUR_KEY_HERE", true);
req.onreadystatechange = function() {
console.log(req.status);
if (req.readyState == 4) {
console.log(req);
if (req.status == 200) {
console.log("Search complete!");
console.log(req.responseText);
}
}
};
req.send();
Will return a 403 response at least. its like the request is not being made or something?
Anyone have any idea what might be going on?
Much appreciated!
When talking to external services from a Spotify App, even if they're running on your local machine, you need to make sure that two things are in place correctly:
The URL (or at least the host) is in the RequiredPermissions section of your manifest. Port doesn't matter. http://127.0.0.1 should be fine for your case.
The server is allowing the sp://your-app-id origin for requests, as noted in the documentation you pasted in your question. This is done by setting the Access-Control-Allow-Origin header in your service's HTTP response. People often set it to Access-Control-Allow-Origin: * to allow anything to make requests to their service.
Thanks for help, I got it figured out, I think it was multiple things, with one main Im an idiot moment for not trying that earlier
First off, I had to run rails on port 80, as obviously if Im accessing my site from 127.0.0.1:3000, thats not going to work if spotify app is requesting 127.0.0.1 unless I can load that directly in the browser, which you cannot unless you run on 80. That is done via
rvmsudo rails server -p 80
Need to use rvmsudo because changing port requires permissions.
Next I had to set access controll allow origin as noted above, that can be done in rails 3 by adding before filter to your app controller as follows.
class ApplicationController < ActionController::Base
logger.info "I SEE REQUEST"
before_filter :cor
def cor
headers["Access-Control-Allow-Origin"] = "*"
headers["Access-Control-Allow-Methods"] = %w{GET POST PUT DELETE}.join(",")
headers["Access-Control-Allow-Headers"] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token}.join(",")
head(:ok) if request.request_method == "OPTIONS"
end
end
Finally, and most importantly (sigh), you cant just righclick and reload your spotify app when you make changes to your manifest file, exit spotify completely and restart it!

Resources