I am creating an iOS/macOS app that uses remote control functionality via the Multipeer Connectivity Framework. Since the device to be remotely monitored and controlled will run over an extended period of time, it's not viable to use the automatic view controller methods since the monitoring device may be locked or go to sleep and then disconnect the connection. So I'm using the programatic approach so that when the monitoring devices lose connection, they will automatically pair up when they are unlocked/woken up and the app is started again. My connection works fine using the ViewController method but not the programatic delegate approach. The advertising, browsing and inviting works fine, but when the invitation is accepted on the remote side I get several errors and then a failed connection. What's weird is that several of the errors are GCKSession errors.
So why is it trying to use the GameCenter framework? And why is it failing after accepting the invitation? Could it just be a bug in the Xcode 8 / Swift 3 /iOS 10 / macOS Sierra Beta SDKs?
[ViceroyTrace] [ICE][ERROR] ICEStopConnectivityCheck() found no ICE check with call id (2008493930)
[GCKSession] Wrong connection data. Participant ID from remote connection data = 6FBBAE66, local participant ID = 3A4C626C
[MCSession] GCKSessionEstablishConnection failed (FFFFFFFF801A0020)
Peer Changing
Failed
[GCKSession] Not in connected state, so giving up for participant [77B72F6A] on channel [0]
Here is the code from my connection class
func startAdvertisingWithoutUI () {
if advertiserService == nil {
advertiserService = MCNearbyServiceAdvertiser (peer: LMConnectivity.peerID, discoveryInfo: nil, serviceType: "mlm-timers")
advertiserService?.delegate = self
session.delegate = self
}
advertiserService?.startAdvertisingPeer()
}
func browserForNearbyDevices () {
if browserService == nil {
browserService = MCNearbyServiceBrowser (peer: LMConnectivity.peerID, serviceType: "mlm-timers")
browserService?.delegate = self
session.delegate = self
}
browserService?.startBrowsingForPeers()
}
func sendInvitation(to peer: MCPeerID) {
browserService?.invitePeer(peer, to: session, withContext: nil, timeout: 60)
}
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: (Bool, MCSession?) -> Void) {
let trustedNames = GetPreferences.trustedRemoteDevices
for name in trustedNames {
if name == peerID.displayName {
invitationHandler(true,session)
return
}
}
invitationHandler (false, session)
}
None has worked for me.
I've resolved only disabling encryption...
let session = MCSession(peer:myPeerId, securityIdentity: nil, encryptionPreference: MCEncryptionPreference.none)
When the peerID used to make the session and the peerID used to make the advertiser or browser do not match, I get this part of the error.
[GCKSession] Wrong connection data. Participant ID from remote connection data = 6FBBAE66, local participant ID = 3A4C626C
Once peerIDs match, that part of the error goes away.
There might still be some other connection problems though.
I found out what was wrong. The MCPeerID object that I was passing into the MCSession instances, I was vending it as a Computed Class Property instead of storing it as a Stored Property. So I changed it to a Stored Instance Property and everything started working! Thanks Tanya for pointing me in the direction of the MCPeerID object.
Old Code
// Class Properties
static var localPeer : MCPeerID { return MCPeerID(displayName: GetPreferences.deviceName!) }
New Code
// Instance Properties
let localPeer = MCPeerID (displayName: GetPreferences.deviceName!)
The problem for me was that I never set the delegate of MCSession. I got all the same error messages that the OP mentioned, which made me think the connection was broken, but really I just forgot to set the delegate. After setting the delegate, all the error messages still printed, but otherwise my delegate methods got called normally upon receiving a message!
I've inflicted this problem on myself twice. Hopefully this helps someone reading along!
I got to work with TViOS 10.0 beta with this ...
peerID = MCPeerID(displayName: UIDevice.current.name)
Although I am still seeing this error...
2016-09-08 10:13:43.016600 PeerCodeATV[208:51135] [ViceroyTrace] [ICE][ERROR] ICEStopConnectivityCheck() found no ICE check with call id (847172408)
2016-09-08 10:13:47.577645 PeerCodeATV[208:51155] [GCKSession] SSLHandshake returned with error [-9819].
Same problem for me with an app I've had on the itunes store for years.
The latest 10.1 beta update now seems to fix the bluetooth issue with my app without any change to my code.
Related
We have a Play app, currently using version 2.6. We are trying to prevent dictionary attacks against our login by delaying a "failed login" message back to our users when they provide a failed password. We currently hash and salt and have all the best practices, but we are not sure if we are delaying correctly. So we have in our Controller:
public Result login() { return ok(loginHtml) }
and we have a:
public Result loginAction()
{
// Check for user in database
User user = User.find.query()...
// Was the user found?
if (user == null) {
// Wrong password! Delay and redirect
Thread.sleep(10000); <<-- how do delay correctly?
return redirect(routes.Controller.login())
}
// User is not null, so all good!
...
}
We are not sure if Thread.sleep(10000) is the best way to delay a response since this might hang other requests that come in, or use too many thread from the default pool. We have noticed that under 80+ hits per second the Play Framework does not route our HTTP calls to the Routes. That is, if we receive a HTTP POST request, our app will not even send that request to the Controller until 20+ seconds later, HOWEVER, in the SAME time period if we get a HTTP GET request, our app will process that GET instantly!
Currently we have 300 threads as the min/max in our Akka settings for the default fork pool. Any insights would be appreciated. We run a t2.xlarge AWS EC2 instance running Ubuntu.
Thank you.
Thread.sleep causes current thread blocking, please, try to avoid using it in production code as much as possible.
What you need to use, is CompletionStage / CompletableFuture or any abstraction for deeling with async programming and asynchronous action.
Please, take a look for more details about asynchronios actions: https://www.playframework.com/documentation/2.8.x/JavaAsync
In your case solution would look like something too (excuse me, please, this might have mistakes - I'm Scala engineer primary):
import play.libs.concurrent.HttpExecutionContext;
import play.mvc.*;
import javax.inject.Inject;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class LoginController extends Controller {
private HttpExecutionContext httpExecutionContext;
// Create and inject separate ScheduledExecutorService
private ScheduledExecutorService executor;
#Inject
public LoginController(HttpExecutionContext ec,
ScheduledExecutorService executor) {
this.httpExecutionContext = ec;
this.executor = executor;
}
public CompletionStage<Result> loginAction() {
User user = User.find.query()...
if (user == null) {
return executor.schedule(() -> {redirect(routes.Controller.login());}, 10, TimeUnit.SECONDS);
} else {
// return another response
}
}
}
Hope this helps!
I don't like this approach at all. This hogs threads for no reason and can probably cause your entire system to lock up if someone finds out you are doing this and they have malicious ideas. Let me propose a better approach:
In the User table store a nullable LocalDateTime of the last login attempt time.
When you fetch the user from the DB check the last attempt time (compare to LocalDateTime.now()), if 10 secs have passed since last attempt perform the password comparison.
If passwords don't match store the last attempt time as now.
This can also be handled gracefully on the front end if you provide good error responses.
EDIT: If you want to delay login attempts NOT based on the user, you could create an attempt table and store last attempt by IP address.
If you really want to do your way which I don't recommend you need to read up on this first: https://www.playframework.com/documentation/2.8.x/ThreadPools
I am using libwidevinecdm.so from chrome to handle DRM protected data. I am currently successfully setting the widevine server certificate I get from the license server. I can also create a session with the pssh box of the media im trying to decode. So far everything is successful (all promises resolve fine).
(session is created like this: _cdm->CreateSessionAndGenerateRequest(promise_id, cdm::SessionType::kTemporary, cdm::InitDataType::kCenc, pssh_box.data(), static_cast<uint32_t>(pssh_box.size()));)
I am then getting a session message of type kLicenseRequest which I am forwarding to the respective license server. The license server responds with a valid response and the same amount of data as I can see in the browser when using Chrome. I am then passing this to my session like this:
_cdm->UpdateSession(promise_id, session_id.data(), static_cast<uint32_t>(session_id.size()),
license_response.data(), static_cast<uint32_t>(license_response.size()));
The problem now is that this promise never resolves. It keeps posting the kLicenseRequest message over and over again to my session without ever returning. Does this mean my response is wrong? Or is this something else?
Br
Yanick
The issue is caused by the fact, that everything in CreateSessionAndGenerateRequest is done synchronous - that means by the time CreateSessionAndGenerateRequest returns your promise will always be resolved.
The CDM will emit the kLicenseRequest inside CreateSessionAndGenerateRequest and it doesn't do so in a "fire & forget" fashion, but the function waits there until you have returned from the cdm::Host_10::OnSessionMessage. Since my implementation of OnSessionMessage was creating a synchronous HTTP Request to the license server before - also synchronously - calling the UpdateSession the entire chain ended up to be blocking.
So ultimately I was calling UpdateSession while still being inside CreateSessionAndGenerateRequest and I assume the CDM cannot handle this and reacts by creating a new session with the given ID and generating a request again, which of course triggered another UpdateSession and so on.
Ultimately the simplest way to break the cycle was to make something asynchronous. I decided to launch a separate thread when receiving kLicenseRequest, wait for a few milliseconds to make sure that CreateSessionAndGenerateRequest has time to finish (not sure if that is really required) and then issue the request to the license server.
The only change I had to do was adding the surrounding std::thread:
void WidevineSession::forward_license_request(const std::vector<uint8_t> &data) {
std::thread{
[=]() {
std::this_thread::sleep_for(std::chrono::milliseconds{100});
net::HttpRequest request{"POST", _license_server_url};
request.add_header("Authorization", fmt::format("Bearer {}", _access_token))
.byte_body(data);
const auto response = _client.execute(request);
if (response.status_code() != 200) {
log->error("Widevine license request not accepted by license server: {} {} ({})", response.status_code(), response.status_text(), utils::bytes_to_utf8(response.body()));
throw std::runtime_error{"Error requesting widevine license"};
}
log->info("Successfully requested widevine license from license server");
_adapter->update_session(this, _session_id, response.body());
}
}.detach();
}
According to other asked questions like this one, I did many doings to prevent this request expired message but there is no solution for my issue.
In the long run I recognized that the message appears when I call a service method inside a controller which run on form action!
Here is my codes samples with some descriptions:
My route:
Route::post('Material/{id}', 'MaterialController#updateMaterial')->name('updateMaterial');
Material Controller Constructor:
public function __construct(CustomService $srv)
{
$this->middleware('admin')->only(['updateMaterial']);
$this->srv= $srv;
}
srv is a protected attribute in MaterialController class.
updateMaterial Method:
public function updateMaterial($id,Request $request)
{
$this->validate($request, [...]);
$material = $this->srv->updateMaterial($request, $id);
if ($material)
return view('panel._materials.edit-material')
->with('material', $material)
->with('success', 1);
}
I also have a provider for CustomService with name CustomServiceProvider and here is the register method of the provider:
public function register()
{
$this->app->bind(CustomService::class,function($app){
return new CustomService();
});
}
and I registered it as a provider in config/app.php.
So when I return something before calling service updateMaterial method, it's OK. but when the method runs, the issue appears!
I don'n have any idea about!
Update:
And here is updateMaterial of CustomService:
public function updateMaterial($request, $id)
{
$material = Material::find($id);
if (!$material)
return false;
if ($request->has('unit'))
$material->unit = $request['unit'];
if ($request->has('price'))
$material->price = $request['price'];
if ($request->has('type'))
$material->type = $request['type'];
if ($request->has('is_active'))
$material->is_active = $request['is_active'];
$material->updated_at = Carbon::now();
$material->save();
return $material;
}
I also create a new project with Laravel 5.5.0 and without adding any complexity I just added a post route and call it in form action, but nothing changed!
This is just an issue for Windows users on Local Environment. I suffered a lot with this also when on Windows. Once you deploy to your production server, you won't have any issue at all.
It's important to note that this is not an issue with Laravel 5.5 version only. I first saw this issue in version 5.2.
I think a good fix for this would maybe be using something like Homestead or Vessel from Fideloper. Honestly I only suffered this problem when using Windows.
I'm using the libPusher pod in a Ruby Motion project but running into an issue where my code works when used in the REPL but not in the app itself.
When I try this code in a viewDidAppear method it connects successfully and then disconnects during the channel subscription call.
When I try it in the console, it connects and subscribes perfectly. (same code)
I'm trying to figure out:
Why this is happening
What should I change to alleviate the issue?
I'm using v 1.5 of the pod v2.31 of Ruby Motion
For reference, I'm also using ProMotion framework but I doubt that has anything to do with the issue.
Here's my code:
client = PTPusher.pusherWithKey("my_pusher_key_here", delegate:self, encrypted:true)
client.connect
channel = client.subscribeToChannelNamed("test_channel_1")
channel.bindToEventNamed('status', target: self, action: 'test_method:')
Well I got it working by separating the connection and subscription calls into separate lifecycle methods.
I put:
client = PTPusher.pusherWithKey("my_pusher_key_here", delegate:self, encrypted:true)
client.connect
into the viewDidLoad method
and:
channel = client.subscribeToChannelNamed("test_channel_1")
channel.bindToEventNamed('status', target: self, action: 'test_method:')
into the viewDidAppear method.
I can't say I know exactly why this worked but I assume it has to do with the time between the calls. The connection process must need a little time to complete.
My capstone team has decided to use Qooxdoo as the front end for our project. We're developing apps for OpenFlow controllers using NOX, so we're using the NOX webservices framework. I'm having trouble getting data from the service; I know the service is running because if I go to the URL using Firefox the right data shows up. Here's the relevant portion of my code:
var req = new qx.io.remote.Request("http://localhost/ws.v1/hello/world",
"GET", "text/plain");
req.addListener("complete", function(e) {
this.debug(e.getContent());
});
var get = new qx.ui.form.Button("get");
get.addListener("execute", function() {
alert("The button has been pressed");
req.send();
}, this);
form.addButton(get);
In the firebug console I get this message after I click through the alert:
008402 qx.io.remote.Exchange: Unknown status code: 0 (4)
And if I press the Get button again I get this error:
027033 qx.io.remote.transport.XmlHttp[56]: Failed with exception: [Exception... "Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIXMLHttpRequest.open]" nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)" location: "JS frame :: file:///home/user/qooxdoo-1.0-sdk/framework/source/class/qx/io/remote/transport/XmlHttp.js :: anonymous :: line 279" data: no]
I've also looked at the Twitter Client tutorial, however the "dataChange" event I set up in place of the "tweetsChanged" event never fired. Any help is appreciated, thank you.
This sound like a cross domain request issue. qx.io.remote.Request uses XHR for transporting the data which may not work in every case due to the browser restriction. Switching the crossDomain flag on the request to true will change from XHR to a dynamically inserted script tag doesn't have the cross domain restriction (but other restrictions).
req.setCrossDomain(true);
Maybe that solves your problem.
Additionally, you can take a look at the documentation of the remote package to get some further details on cross domain requests:
http://demo.qooxdoo.org/current/apiviewer/#qx.io.remote
Also take care not to use a request object twice. The only work once.