Chromecast won't load my receiver app. Request on stick is pending - google-cast

I have taken the cast-chrome-sender-helloworld sample app from https://github.com/googlecast/cast-chrome-sender-helloworld and inserted my APP ID. The APP ID is associated with a URL that points to http://<server>/<path>/chromecast and please notice the lack of ending "/" or ".html" which I did on purpose because it's basically a servlet that handles the receiver.
My sender app sends the request to the chromecast stick alright. Using the debugger in Chrome I can see that the request to launch a specific app associated with my APP ID is received and the stick tries to load the receiving app from the above mentioned URL. However, I see the HTTP GET request is executed but it's state is "pending" for a while before (I guess) it times out.
Could this failure to load the receiver app be due to the whitelisted URL is without ".html"?
** Edited on the 27th of September
I still see the Chromecast browser issuing the correct request URL but it "hangs" with the network status message "pending". No requests hits my apache server. If I use a laptop to issue the same request on the same wifi network, then I get the following correct answer (using CURL):
curl -vvv http://server.domain.com/path/device/chromecast
* About to connect() to server.domain.com port 80 (#0)
* Trying 192.168.1.42...
* connected
* Connected to server.domain.com (192.168.1.42) port 80 (#0)
> GET /path/device/chromecast HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5
> Host: server.domain.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 27 Sep 2013 06:46:23 GMT
< Server: Apache-Coyote/1.1
< Content-Type: text/html;charset=ISO-8859-1
< Content-Length: 1850
< Set-Cookie: JSESSIONID=81A3A6184F014A7FBEC60C0B6375E8B0; Path=/client-portal/; HttpOnly
<
<!DOCTYPE html>
<html>
<head>
<script src="https://www.gstatic.com/cast/js/receiver/1.0/cast_receiver.js">
</script>
<script type="text/javascript">
cast.receiver.logger.setLevelValue(0);
// Initialize and start the media playing receiver
var receiver = new cast.receiver.Receiver(
'REPLACED_WITH_MY_APPID',
[cast.receiver.RemoteMedia.NAMESPACE],
"",
5);
var remoteMedia = new cast.receiver.RemoteMedia();
remoteMedia.addChannelFactory(
receiver.createChannelFactory(cast.receiver.RemoteMedia.NAMESPACE));
receiver.start();
window.addEventListener('load', function() {
var elem = document.getElementById('vid');
remoteMedia.setMediaElement(elem);
var checkStatus = function() {
var status = document.getElementById('status');
var st = remoteMedia.getStatus()['state'];
if( st == 0 || remoteMedia.getStatus()['current_time'] == 0 ) {
status.style.display = 'block';
}
else {
if( st == 1 && remoteMedia.getStatus()['current_time'] > 0 ) {
status.innerHTML = 'Paused...';
status.style.display = 'block';
}
else {
status.innerHTML = remoteMedia.getStatus()['current_time'];
status.style.display = 'none';
elem.style.display = 'block';
}
}
}
setInterval(checkStatus, 1000);
});
</script>
<title>Media Player App</title>
</head>
<body>
<video id="vid"
style="position:absolute;top:0;left:0;height:100%;width:100%"></video>
<div id="status" style="display:none; font-size:300%; position:absolute;top:40%;left:40%;">
<img src="/images/chrome_loading.gif" width="60%">
</div>
</body>
* Connection #0 to host server.domain.com left intact
</html>* Closing connection #0f
The debugger just shows the following before reloading the "brain freeze" screen:

Turns out that the Chromecast stick does not use the local wifi routers DNS, but rely on an external one. My problem was that the host I was contacting had a different ip address on my local lan. (Thanks Google for the help with debugging this).
The documentation on https://developers.google.com/cast/whitelisting states that it is okay to whitelist NAT'ed addresses however, I don't see how this could work since the external DNS typically are unaware of that:
Two (2) URLs for the location of your receiver app, typically one for QA and one for production, on your server. A Receiver is a HTML5 web page that hosts your content on the Chromecast devices. Good Receiver URL's usually look like https://website.com/rcvr/myreceiver.html or https://website.com/qarcvr/myreceiver.html (It is ok for your receiver to be on an internal (NAT'd) IP address, but not localhost. They are rarely a top level domain.
I solved this by using pagekite.net, which allows me to have a public address reachable by the external DNS and still access my local development machines web server.

Related

How to load WebWorker from local in WKWebView

I rebuild an iOS app from a Vue.js app. It uses a WebWorker to execute a script in the background.
I confirmed that npm run serve on the local could run the app.
So the following process is to run the app in a WKWebView.
The app loads the files from a local device. The files such as index.html and main.js could be loaded and shown correctly.
let url = Bundle.main.url(forResource: "index", withExtension: "html")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
However, the worker seems not to be loaded. The worker script is located in the same directory to index.html.
// in my script
this.worker = new Worker('worker.js');
I also tried running a quite simple HTTP Web server to load worker.js.
guard let listener = try? NWListener(using: .tcp, on: NWEndpoint.Port(integerLiteral: 52525)) else {
print("### Error.")
self.listener = nil
return
}
listener.newConnectionHandler = { connection in
connection.start(queue: .main)
connection.stateUpdateHandler = { state in
switch state {
case .ready:
connection.send(content:
("""
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 86810
Content-Type: application/javascript
""".replacingOccurrences(of: "\n", with: "\r\n") + worker).data(using: .utf8), completion: .idempotent)
default:
break
}
}
}
listener.start(queue: .main)
self.listener = listener
Safari in iOS Simulator could get worker.js and show its content.
// the app can send a response by HTTP.
this.worker = new Worker('http://localhost:52525/worker.js');
However, the error message said SecurityError: The operation is insecure. How can I use a WebWorker in WKWebView? Is any configuration needed?
According to MDN page, a script tag with type="text/js-worker" can be used for the alternative of worker.js file.
<script id="worker" type="text/js-worker">
<!-- the contents of worker.js -->
</script>
const worker = new Worker(window.URL.createObjectURL(
new Blob([document.getElementById("worker").textContent], {
type: "application/javascript",
})
));
I tried this approach, and then the worker could run.

Google Analytics Via Node.js Proxy

I am trying to impliment a proxy for google analytics into my current site. I started by following this guide.
After following all the steps I get a 400 error returned from google with no further information.
I downloaded google's analytics.js file and put it on my server, and replaced all instances of www.google-analytics.com with "+location.host+"/analytics. See the file here: https://pastebin.com/wimHim7x
I edited my tracking code from google to replace 'https://www.google-analytics.com/analytics.js' with '/analytics.js'.
Here is the proxy info from my app.js file
function getIpFromReq (req) { // get the client's IP address
var bareIP = ":" + ((req.connection.socket && req.connection.socket.remoteAddress)
|| req.headers["x-forwarded-for"] || req.connection.remoteAddress || "");
return (bareIP.match(/:([^:]+)$/) || [])[1] || "127.0.0.1";
}
// proxying requests from /analytics to www.google-analytics.com.
app.use("/analytics", proxy("www.google-analytics.com", {
proxyReqPathResolver: function (req) {
var path = req.url + (req.url.indexOf("?") === -1 ? "?" : "&")
+ "uip=" + encodeURIComponent(getIpFromReq(req));
console.log(path)
return path;
}
}));
This is the page I get returned from google:
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 400 (Bad Request)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}#media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}#media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}#media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>400.</b> <ins>That’s an error.</ins>
<p>Your client has issued a malformed or illegal request. <ins>That’s all we know.</ins>

Socket.io: Namespace & WS protocol

Step 1: server
I've created a simple server with Node & Socket.io which declares a namespace under /my-namespace. Once somebody connects, emit a confirmation msg as CONNECT_ACK and emit 3 seconds later another event (for example SOME_EVENT) with a payload:
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
let app = express();
let server = http.createServer(app);
server.listen(3000, () => {
console.log('server listening on port 3000');
let io = new socketIO(server);
io.of('/my-namespace').on('connection', (socket) => {
console.log('welcome!');
socket.emit('CONNECT_ACK');
setTimeout(() => {
io.of('/my-namespace').emit('SOME_EVENT', { a: 4 });
}, 3000);
});
});
Step 2: client
Then, I created the smallest client side which just connects to the namespace and logs when it receives CONNECT_ACK or SOME_EVENT
<!doctype html>
<html>
<head>
<title>example</title>
<script src="./node_modules/socket.io-client/socket.io.js"></script>
<script>
const endPoint = "http://localhost:3000/my-namespace";
io(endPoint)
.on('CONNECT_ACK', () => { console.log("I've connected"); })
.on('SOME_EVENT', (data) => { console.dir(data); });
</script>
</head>
<body>
</body>
</html>
Step 3: Checking everything is awesome
Running the client node index.js and serving the html (I use Python Simple Server) I got the desired in both consoles:
Step 4. Understanding whats going on here
Now, when I opened the Network Chrome tab I started writing this long post. These are the requests:
[WebSocket Protocol]: GET to /socket.io (not to /my-channel) receiving some confirmation bits; POST again to /socket.io including those confirmation bits. OK.
[I don't get this]: again a GET to /socket.io including the confirmation bits which now resolves to the CONNECT_ACK event: ÿ40ÿ40/my-namespaceÿ42/my-namespace,["CONNECT_ACK"]. This is the only event I'm going to receive this way.
[WS]: A GET to /socket.io indicating it's a websoket returns me a 101 (Switching Protocols) and I can recieve the msgs as: 42/my-namespace,["SOME_EVENT",{"a":4}]
which is the event I send from the server & some 2s or 3s periodically
[I don't get this too]: again a GET to /socket.io including the confirmation bits which now resolves to this thing: ÿ6
Why does the client asks for socket.io instead of /my-channel?
Why there is a GET after the WS handshake which receives CONNECT_ACK msg?
I understand that the "inmortal" request which resolves in 101 is the socket itself, and those 2s and 3s are just periodical checks.
Why does all the events start by 42 (I've checked this does not change)
What is that final GET? is it part of the WS protocol?
Why does the client asks for socket.io instead of /my-channel?
When setting up socket.io-server, socket.io will set itself to intercept any request to /socket.io in order to work. Namespaces use the same notation as paths in HTTP, but mean completely different things, and connecting to a namespace performs the same HTTP request to /socket.io, but with a different namespace argument in it.
Why there is a GET after the WS handshake which receives CONNECT_ACK msg?
I can't be sure of this one, but this probably arrived to the server before the WS request, and sent the CONNECT_ACK via polling instead.
Why does all the events start by 42 (I've checked this does not change)
According to this GitHub issue, it defines the packet as a message (4) of type event (2). Personally, I suspect the 4 is actually the protocol version, currently 4, as it's the only reference to that number in the docs except in packet types (which must then be 2).
What is that final GET? is it part of the WS protocol?
Not sure again, but possibly a confirmation that the WS connection has been established and a way for socket.io to confirm that it should switch from polling to WS and start sending the events there.

Using socket.io from inside and outside LAN

I am using socket.io for a project that requires me to stream live data to a browser. Now the server part is fine, but on socket.io client io.connect() call, I have had a few problems in the past, without proper resolution. I have tried the usual suspects, but I am not getting through.
My client call to looks like this:
var socket = io.connect('http://10.95.xx.xx:5002');
where 10.95. .. is the IP address of the server on the LAN. Using this IP I can access the server from inside the LAN, on any machine in the LAN, but this doesn't work from outside the LAN. Problem being, most probably, that this IP is for LAN only and not the public IP. As per university's network settings, the public IP is 128.95 ...and if I change it to this IP, then it will be accessible from outside, but not from inside.
One way to resolve this, is that I try to read the client's IP address and based on that decide the parameter IP. But this doesn't feel robust. Also, I tried using io.connect("/") . It had worked for me in previous circumstance, but it did not this time. Also, the empty call did not work io.connect() did not work.
As you said you can use the clients IP address, this here always worked for me:
var port = window.location.port,
host = window.location.hostname,
protocol = window.location.protocol,
path = '/',
url,
options = { };
if( protocol.indexOf( 'https' ) > -1 ) {
protocol = 'wss:';
} else {
protocol = 'ws:'
}
url = protocol + "//" + host + ":" + port + path;
options = { };
/*
// If you wanted to add an access token, "Session" is where I store this
if( Session.token ) {
options.query = 'access_token=' + Session.token;
}
*/
socket = io( url, options );

Non-http TCP connection on Cloudfoundry

I'm a nooby mobile developer trying to take advantage of cloudfoundry's service to run my server to handle some chats and character movements.
I'm using Noobhub to achieve this (TCP connection between server and client using Node.js and Corona SDK's TCP connection API)
So basically I'm trying a non-http TCP connection between Cloudfoundry(Node.js) and my machine(lua).
Link to Noobhub(There is a github repo with server AND client side implementation.
I am doing
Client
...
socket.connect("myappname.cloudfoundry.com", 45234)
...
(45234 is from server's process.env.VCAP_APP_PORT value I retrieved from console output I got through "vmc logs myappname" after running the application.)
Server
...
server.listen(process.env.VCAP_APP_PORT)
When I try to connect, it just times out.
On my local machine, doing
Client
...
socket.connect("localhost",8989)
Server
...
server.listen(8989)
works as expected. It's just on cloudfoundry that it doesn't work.
I tried a bunch of other ways of doing this such as setting the client's port connection to 80 and a bunch of others. I saw a few resources but none of them solved it.
I usually blow at asking questions so if you need more information, please ask me!
P.S.
Before you throw this link at me with an angry face D:< , here's a question that shows a similar problem that another person posted.
cannot connect to TCP server on CloudFoundry (localhost node.js works fine)
From here, I can see that this guy was trying to do a similar thing I was doing.
Does the selected answer mean that I MUST use host header (i.e. use http protocol) to connect? Does that also mean cloudfoundry will not support a "TRUE" TCP socket much like heroku or app fog?
Actually, process.env.VCAP_APP_PORT environment variable provides you the port, to which your HTTP traffic is redirected by the Cloud Foundry L7 router (nginx) based on the your apps route (e.g. nodejsapp.vcap.me:80 is redirected to the process.env.VCAP_APP_PORT port on the virtual machine), so you definitely should not use it for the TCP connection. This port should be used to listen HTTP traffic. That is why you example do work locally and do not work on Cloud Foundry.
The approach that worked for me is to listen to the port provided by CF with an HTTP server and then attach Websocket server (websocket.io in my example below) to it. I've created sample echo server that works both locally and in the CF. The content of my Node.js file named example.js is
var host = process.env.VCAP_APP_HOST || "localhost";
var port = process.env.VCAP_APP_PORT || 1245;
var webServerApp = require("http").createServer(webServerHandler);
var websocket = require("websocket.io");
var http = webServerApp.listen(port, host);
var webSocketServer = websocket.attach(http);
function webServerHandler (req, res) {
res.writeHead(200);
res.end("Node.js websockets.");
}
console.log("Web server running at " + host + ":" + port);
//Web Socket part
webSocketServer.on("connection", function (socket) {
console.log("Connection established.");
socket.send("Hi from webSocketServer on connect");
socket.on("message", function (message) {
console.log("Message to echo: " + message);
//Echo back
socket.send(message);
});
socket.on("error", function(error){
console.log("Error: " + error);
});
socket.on("close", function () { console.log("Connection closed."); });
});
The dependency lib websocket.io could be installed running npm install websocket.io command in the same directory. Also there is a manifest.yml file which describes CF deploy arguments:
---
applications:
- name: websocket
command: node example.js
memory: 128M
instances: 1
host: websocket
domain: vcap.me
path: .
So, running cf push from this directory deployed app to my local CFv2 instance (set up with the help of cf_nise_installer)
To test this echo websocket server, I used simple index.html file, which connects to server and sends messages (everything is logged into the console):
<!DOCTYPE html>
<head>
<script>
var socket = null;
var pingData = 1;
var prefix = "ws://";
function connect(){
socket = new WebSocket(prefix + document.getElementById("websocket_url").value);
socket.onopen = function() {
console.log("Connection established");
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log("Connection closed clean");
} else {
console.log("Connection aborted (e.g. server process killed)");
}
console.log("Code: " + event.code + " reason: " + event.reason);
};
socket.onmessage = function(event) {
console.log("Data received: " + event.data);
};
socket.onerror = function(error) {
console.log("Error: " + error.message);
};
}
function ping(){
if( !socket || (socket.readyState != WebSocket.OPEN)){
console.log("Websocket connection not establihed");
return;
}
socket.send(pingData++);
}
</script>
</head>
<body>
ws://<input id="websocket_url">
<button onclick="connect()">connect</button>
<button onclick="ping()">ping</button>
</body>
</html>
Only thing to do left is to enter server address into the textbox of the Index page (websocket.vcap.me in my case), press Connect button and we have working Websocket connection over TCP which could be tested by sending Ping and receiving echo. That worked well in Chrome, however there were some issues with IE 10 and Firefox.
What about "TRUE" TCP socket, there is no exact info: according to the last paragraph here you cannot use any port except 80 and 443 (HTTP and HTTPS) to communicate with your app from outside of Cloud Foundry, which makes me think TCP socket cannot be implemented. However, according to this answer, you can actually use any other port... It seems that some deep investigation on this question is required...
"Cloud Foundry uses an L7 router (ngnix) between clients and apps. The router needs to parse HTTP before it can route requests to apps. This approach does not work for non-HTTP protocols like WebSockets. Folks running node.js are going to run into this issue but there are no easy fixes in the current architecture of Cloud Foundry."
- http://www.subbu.org/blog/2012/03/my-gripes-with-cloud-foundry
I decided to go with pubnub for all my messaging needs.

Resources