Apache + Symfony2 + HTTPS + Node.js + Socket.io: s

2019-06-23 18:36发布

问题:

I've been spending hours upon hours on this problem, but to no avail.

EDIT: solution found (see my answer)

Project background

I'm building a project in Symfony2, which requires a module for uploading large files. I've opted for Node.js and Socket.IO (I had to learn it from scratch, so I might be missing something basic). I'm combining these with the HTML5 File and FileReader API's to send the file in slices from the client to the server. Preliminary tests showed this approach working great as a standalone app, where everything was handled and served by Node.js, but integration with Apache and Symfony2 seems problematic.

The application has an unsecured and secured section. My goal is to use Apache on ports 80 and 443 for serving the bulk of the app built in Symfony2, and Node.js with Socket.io on port 8080 for file uploads. The client-side page connecting to the socket will be served by Apache, but the socket will run via Node.js. The upload module has to run over HTTPS, as the page resides in a secured environment with an authenticated user.

The problem is events using socket.emit or socket.send don't seem to work. Client to server, or server to client, it makes no difference. Nothing happens and there are no errors.

The code

The code shown is a simplified version of my code, without the clutter and sensitive data.

Server

var httpsModule = require('https'),
    fs = require('fs'),
    io = require('socket.io');

var httpsOptions = 
{
    key: fs.readFileSync('path/to/key'),
    cert: fs.readFileSync('/path/to/cert'),
    passphrase: "1234lol"
}

var httpsServer = httpsModule.createServer(httpsOptions);
var ioServer = io.listen(httpsServer);

httpsServer.listen(8080);

ioServer.sockets.on('connection', function(socket)
{
    // This event gets bound, but never fires
    socket.on('NewFile', function(data)
    {
        // To make sure something is happening
        console.log(data);

        // Process the new file...
    });

    // Oddly, this one does fire
    socket.on('disconnect', function()
    {
        console.log("Disconnected");
    });
});

Client

// This is a Twig template, so I'll give an excerpt
{% block javascripts %}
{{ parent() }}

<script type="text/javascript" src="https://my.server:8080/socket.io/socket.io.js"></script>
<script type="text/javascript">

    var socket = io.connect("my.server:8080",
                {
                    secure: true,
                    port: 8080
                });


    // Imagine this is the function initiating the file upload
    // File is an object containing metadata about the file like, filename, size, etc.
    function uploadNewFile(file)
    {
         socket.emit("NewFile", item);
    }

</script>
{% endblock %}

So the problem is...

Of course there's much more to the application than this, but this is where I'm stuck. The page loads perfectly without errors, but the emit events never fire or reach the server (except for the disconnect event). I've tried with the message event on both client and server to check if it was a problem with only custom events, but that didn't work either. I'm guessing something is blocking client-server communication (it isn't the firewall, I've checked).

I'm completely at a loss here, so please help.

EDIT: solution found (see my answer)

回答1:

After some painstaking debugging, I've found what was wrong with my setup. Might as well share my findings, although they are (I think) unrelated to Node.js, Socket.IO or Apache.

As I mentioned, my question had simplified code to show you my setup without clutter. I was, however, setting up the client through an object, using the properties to configure the socket connection. Like so:

var MyProject = {};

MyProject.Uploader =
{
    location: 'my.server:8080',
    socket: io.connect(location,
            {
                secure: true,
                port: 8080,
                query: "token=blabla"
            }),
    // ...lots of extra properties and methods
}

The problem lay in the use of location as a property name. It is a reserved word in Javascript and makes for some strange behaviour in this case. I found it strange that an object's property name can't be a reserved word, so I decided to test. I had also noticed I was referencing the property incorrectly, I forgot to use this.location when connection to the socket. So I changed it to this, just as a test.

var MyProject = {};

MyProject.Uploader =
{
    location: 'my.server:8080',
    socket: io.connect(this.location,
            {
                secure: true,
                port: 8080,
                query: "token=blabla"
            }),
    // ...lots of extra properties and methods
}

But to no avail. I was still not getting data over the socket. So the next step seemed logical in my frustration-driven debugging rage. Changing up the property name fixed everything!

var MyProject = {};

MyProject.Uploader =
{
    socketLocation: 'my.server:8080',
    socket: io.connect(this.socketLocation,
            {
                secure: true,
                port: 8080,
                query: "token=blabla"
            }),
    // ...lots of extra properties and methods
}

This approach worked perfectly, I was getting loads of debug messages. SUCCESS!! Whether it is expected behaviour in Javascript to override (or whatever is happening here, "to misuse" feels like a better way of putting it to me right now) object properties if you happen to use a reserved word, I don't know. I only know I'm steering clear of them from now on!

Hope it helps anyone out there!