Connect to Socket.io server from a Rails server - ruby-on-rails

[update 1]
i found out that io.connect('http://xxxxx.herokuapp.com') actually send out a request on xxxxx.herokuapp.com port 3000:
the connection works if the request is WITHOUT port number. I didn't specify a port in my io.connect, How can i get rid of that?
Request URL:http://xxxxx.herokuapp.com:3000/socket.io/1/?t=1360853265439
Request Headersview source
Cache-Control:max-age=0
Origin:http://localhost:3000
Referer:http://localhost:3000/about
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
Query String Parametersview sourceview URL encoded
t:1360853265439
[update 2]
for comparison here is the HEAD of a successful connect when i run the java script from local file system
Request URL:http://xxxxx.herokuapp.com/socket.io/1/?t=1360854705943
Request Method:GET
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Host :xxxxx.herokuapp.com
Origin:null
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
[update 3]
set port to 80 seems to be working
var socket = io.connect('http://xxxxx.herokuapp.com',{port:80});
[Question] I intend to have socket.io set up on one host to talk to a Rails app on another host. It all works fine when the client side Javascript was hosted on localhost:3000 to connect to Socket.io hosted on a node.js server on localhost:5000. It works as well when both client side Javascript and Socket.io hosted on Heroku on the same port.
socket_server.js hosted on XXXX.herokuapp.com
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
, i = 0
io.configure(function () {
io.set("origin = *");
io.set("transports", ["xhr-polling"]);
io.set("polling duration", 100);
});
var port = process.env.PORT || 5000; // Use the port that Heroku provides or default to 5000
app.listen(port, function() {
console.log(">>>>>>socket server up and running on port: "+port);
});
function handler (req, res) {
fs.readFile(__dirname + '/socket.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading socket.html');
}
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) {
console.log(">>>>>>client connected through socket");
socket.emit('news', '>>>>>>server say hello to client', i);
console.log('>>>>>>server say hello to client' +'['+i+']')
socket.on('my other event', function (data) {
socket.emit('news', i);
i++;
console.log(data +'['+i+']');
});
});
if I put client side javascript in socket.html on XXXX.herokuapp.com same as socket.io it behave as expected.
<div id='sandbox'>sandbox</div>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('window.location.hostname');
$('#sandbox').append('<div> lora </div>');
socket.on('news', function (data, index) {
$('#sandbox').append('<div>' + data + ' ' + index + '</div>');
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
However if I put client side Javascript on Rails server on YYYY.herokuapp.com and try to connect to Socket.io server on xxx.herokuapp.com, it doesnt work. it managed to retreive socket.io.js on the server but io.connect('http://xxxxx.herokuapp.com') doesnt not get any response from the server.
<div id='sandbox'>sandbox</div>
<script src="http://xxxxx.herokuapp.com/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://xxxxx.herokuapp.com');
$('#sandbox').append('<div> lora </div>');
socket.on('news', function (data, index) {
$('#sandbox').append('<div>' + data + ' ' + index + '</div>');
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
I read a few post point the solution to set io.set("origins = *") but this seems not working in this case as well.

Origins are set to * by default in socket.io. And it would be io.set("origin", "*"); but as this is the default value, you can just ignore this function.
I do notice that you also have var socket = io.connect('window.location.hostname') in your client side code, you problably ment: var socket = io.connect(window.location.hostname) without the single quotes. The heroku docs do recommend the use of io.set("polling duration", 100); But you set it to 100 instead of the advised 10.Other then that I don't see anything wrong with your code and it should just work.
If these fixes don't help then I would blame it on your hosting. Heroku is a really restrictive platform when it comes to deploying real-time applications. So it could be that they are messing something up here. I would suggest trying to deploy somewhere else like on nodejitsu.com which also supports WebSockets and does not impose any limits to polling to see if it works there for you.

Related

Cannot POST with ESP8266 (espruino)

I cannot make post request (get works fine) with espruino.
I've already checked the documentation and it seems pretty equal
here is my code:
let json = JSON.stringify({v:"1"});
let options = {
host: 'https://******,
protocol: 'https',
path: '/api/post/*****',
method: 'POST',
headers:{
"Content-Type":"application/json",
"Content-Length":json.length
}
};
let post = require("http").request(options, function(res){
res.on('data', function(data){
console.log('data: ' + data);
});
res.on('close', function(data){
console.log('Connection closed');
});
});
post.end(json);
The espruino console only return the 'connection closed' console.log.
The node.js server console (hosted on heroku and tested with postman) dont return anything.
Obv the esp8266 is connected to the network
What you're doing looks fine (an HTTP Post example is here), however Espruino doesn't support HTTPS on ESP8266 at the moment (there isn't enough memory on the chips for JS and HTTPS).
So Espruino will be ignoring the https in the URL and going via HTTP. It's possible that your server supports HTTP GET requests, but POST requests have to be made via HTTPS which is why it's not working?
If you did need to use HTTPS with Espruino then there's always the official Espruino WiFi boards, or I believe ESP32 supports it fine too.
you're using a package called "http" and then trying to send a request over https. You should also log out 'data' in the res.close so you can get some errors to work with.

How to get the full URL for a request in Hapi

In my hapijs app, given a Request object, how can I find the original, unparsed, unmodified URL?
function getRequestUrl (request) {
return ...; // What goes here?
}
I've found that I can piece it together somewhat from Request.info.host, Request.path, and Request.query, but it lacks the scheme (ie, http vs https), and is a bit of a kludge. Isn't the plain URL available somewhere?
The full URL isn't stored somewhere you can get it. You need to build it yourself from the parts:
const url = request.connection.info.protocol
+ '://'
+ request.info.host
+ request.url.path;
Even though it might seem kludgey, it makes sense if you think about it because there is no original, unparsed, unmodified URL. The HTTP request that goes over the wire doesn't contain the URL as typed into the browser address bar for instance:
GET /hello?a=1&b=2 HTTP/1.1 // request.url.path
Host: localhost:4000 // request.info.host
Connection: keep-alive
Accept-Encoding: gzip, deflate, sdch
...
And you only know the protocol based on whether the hapi server connection is in TLS mode or not (request.connection.info.protocol).
Things to be aware of
If you check either:
request.connection.info.uri or request.server.info.uri
the reported hostname will be the hostname of the actual machine that the server is running on (the output of hostname on *nix). If you want the actual host the person typed in the browser (which might be different) you need to check request.info.host which is parsed from the HTTP request's Host header)
Proxies and X-Forwarded-Proto header
If your request got passed through a proxy(ies)/load balancers/HTTPS terminators, it's possible somewhere along the line HTTPS traffic got terminated and was sent to your server on an HTTP connection, in this case you'll want use the value of the x-forwarded-proto header if it's there:
const url = (request.headers['x-forwarded-proto'] || request.connection.info.protocol)
+ '://'
+ request.info.host
+ request.url.path;
With template strings:
const url = `${request.headers['x-forwarded-proto'] || request.connection.info.protocol}://${request.info.host}${request.url.path}`;
hapi-url solves this exact problem. It is prepared to work with X-Forwarded headers to run behind a proxy. There is also an option to override the automatic resolution if the library is not able to resolve the URL correctly.
I use the following syntax now (using coffee script):
server.on 'response', (data) ->
raw = data.raw.req
url = "#{data.connection.info.protocol}://#{raw.headers.host}#{raw.url}"
console.log "Access to #{url}"
Or as javascript:
​server.on('response', function(data) {
var raw = data.raw.req;
var url = data.connection.info.protocol + "://" +
raw.headers.host + raw.url;
console.log("Access to " + url);
});
That gives you the exact URL like the user requested it.
You can't get the URL. You have to generate it. I'm using this one:
const url = request.headers['x-forwarded-proto'] + '://' +
request.headers.host +
request.url.path;
nowadays there is simply request.url:
https://hapi.dev/api?v=20.2.0#-requesturl

Alamofire http request to local webserver

I'm developing an iPhone app and the plan is to send a JSON packet every so often from the app to a local webserver. To do this, I had planned to use Alamofire. My POST method looks like this:
Alamofire.request(Alamofire.Method.POST, "http://XXX.XX.X.XX:3000/update", parameters: dictPoints, encoding: .JSON)
.responseJSON {(request, response, JSON, error) in
println(JSON)
}
The IP address is marked out, but I've made sure that this corresponds to the IPv4 wireless address of my local server. The server is set to listen to port 3000. The server configuration looks like this:
var express = require('express');
var app = express();
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function (callback) {
console.log("MongoDB connection is open.");
});
// Mongoose Schema definition
var Schema = mongoose.Schema;
var LocationSchema = new Schema({
//some schema here
});
// Mongoose Model definition
var LocationsCollection = mongoose.model('locations', LocationSchema);
// URL management
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.get('/update', function (req, res) {
console.log("Got something from the phone!");
});
// Start the server
var server = app.listen(3000, function () {
var host = server.address().address
var port = server.address().port
console.log('App listening at %s:%s',host, port)
})
So, this server seems to work ok. I can test it in my browser and type the URL: http://127.0.0.1:3000 and it will feed me the index.html file. If I type in http://127.0.0.1:3000/update... then I get the "Got something from the phone!" message. However, when I run my app (making sure my phone is on the same wireless network as the server) and the Alamofire method gets called... the response I get is nil. I also don't see the "Got something from the phone!" message. Can anyone let me know why that would be happening... or better yet, how to fix it?
A few thoughts:
You are creating a POST request in Alamofire, but you've told Express to handle GET requests for /update. Use app.post(...) if you want to handle it as a POST.
Your Alamofire code is looking for JSON response, but you don't appear to be creating a JSON response. In the short term, you could use responseString rather than responseJSON, but I presume you really want to change your web service to respond with JSON (to make it easier for the app to parse the responses).
Your Alamofire code sending a JSON request (but clearly when you send a request via the web browser, it's not JSON). Are you sure you wanted to send JSON request? (This is not to be confused with the JSON response issue.) Did you want to use the .URL encoding type parameter, rather than .JSON?
Whenever you have a request that works correctly from a web browser, but not from the app, it's useful to watch both using a tool like Charles and you can then compare how they differ and diagnose the source of the different behavior.
If you are running Express server and iOS Simulator on the same machine, try to use http://0.0.0.0:<port>/<url> instead of the actual IP, it helped in my case.
It sounds like the node server is not bound to the address you expect it to be. You should verify that when you type in the actual IP address in your "http://XXX.XX.X.XX:3000/update" in your web browser that it responds. Your question details suggest you've just been using the loopback address.

Express.IO connection keeps failing with HTTP 500

I'm using Express.IO and This iOS Client trying to create a basic socket example but I can't seem to be able to connect.
I tried everything I could possibly find on google but everytime my client tries connecting I get the following error:
2013-08-12 14:48:42.628 SocketIOTest[59835:c07] ERROR: handshake failed ... Could not connect to the server.
2013-08-12 14:48:42.628 SocketIOTest[59835:c07] OnError: Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo=0x7594570 {NSErrorFailingURLStringKey=http://localhost:7076/socket.io/1/?t=16807, NSErrorFailingURLKey=http://localhost:7076/socket.io/1/?t=16807, NSLocalizedDescription=Could not connect to the server., NSUnderlyingError=0x10967bb0 "Could not connect to the server."}
When I go to http://localhost:7076/socket.io/1/?t=16807 in my browser I get this response:
ttIwyz7Tw6uFjp00WceI:60:60:websocket,htmlfile,xhr-polling,jsonp-polling
But for some reason when checking with header tools, it actually does return an HTTP 500 error. I'm really not sure why.
Here's my Node code:
#!/usr/bin/env node
var express = require('express.io');
var app = express().http().io();
var redisStore = require('connect-redis')(express);
app.use(express.cookieParser());
app.use(express.session({
store: new redisStore({
host: '192.168.33.10',
port: 6386
}),
secret: 'A>mVt*Cx87Kq^9:3}am$q67JYtGGAl'
})) ;
app.use(function(err, req, res, next){
response.header('Access-Control-Allow-Origin', '*');
response.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
response.header('Access-Control-Allow-Credentials', 'true');
response.header('Access-Control-Allow-Headers', 'Content-Type, Accept, Origin, Cookie');
next();
});
app.set( 'origins', '*' );
app.io.on('connection', function (socket) {
console.log("connection");
socket.emit('news', { hello: 'world' });
});
app.listen(7076);
console.log('App API Server is listening on port 7076');
Would appreciate any help on this.
it seems that you are referring to localhost: NSErrorFailingURLStringKey=http://localhost:7076/socket.io/1/?t=16807 in your IOS client, while the node.js server is running on some external Linux box.
Try connecting to the server instead.

Ajax Call Not Marked as IsAjaxRequest in MVC Controller

I'm just looking at some Ajax requests in Fiddler whilst testing some exception handling classes and code in my application and I'm not sure my requests are well formed and as they should be.
My Javascript is:
$(function () {
$('#createentry').submit(function () {
e.preventDefault();
$.ajax({
url: this.action,
type: this.method,
dataType: "json",
data: $(this).serialize(),
success: function(result) {
$('#entries-list').append("<li>" + $('#newentry').val() + "</li>");
$('#newentry').val('').blur();
},
error: function (xhr)
{
try
{
var json = $.parseJSON(xhr.responseText);
alert(json.errorMessage);
}
catch (e)
{
alert('something bad happened');
}
}
});
return false;
});
});
In Fiddler the request looks like:
Host: localhost:54275
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:54275/Diary
Cookie: __RequestVerificationToken= <snipped for brevity>
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 216
I would expect the Accept: header to be set to json automatically I guess that's an incorrect assumption on my part, how is this set?
Also if I looks at the result of this in my action method the value is always false:
[HttpPost, ValidateAntiForgeryToken, JsonExceptionFilter]
public JsonResult PostNewEntry(DiaryEntryViewModel diaryEntry)
{
var req = Request.IsAjaxRequest();
req always = false so my JsonExceptionFilter isn't kicking in and taking care of the error reporting as expected.
Is there also a defined way of forcing only accepting requests correctly setup as Ajax requests in MVC?
I found this bug that is over five years old that says using .ajax() with a dataType of script or JSON would result in the header missing is a feature. But I would imagine Request.IsAjaxRequest() is looking for that exact header.
As a work around you could try doing something like:
$(document).ajaxSend(function (event, request, settings) {
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
});
which would append that header to every Ajax call jQuery sends.
After trying pretty much every solution I could find on the web with absolutely no success, I opted to start using the jQuery Form Plugin and I am now getting well formed Ajax requests on the server side.
Download the plugin from the link above and I then replaced my Ajax JavaScript with:
$(document).ready(function () {
$('#createentry').ajaxForm(function () {
alert("Thank you for your comment!");
});
});
After including this script and testing a call to:
Request.IsAjaxRequest();
Now correctly returns true.
Using Firebug to examine the requests being posted the required header that denotes the request as an Ajax request is present:
X-Requested-With XMLHttpRequest

Resources