qzind / qz-print

Archive for legacy qz-print versions (1.8, 1.9). See https://github.com/qzind/tray for modern versions.
Other
141 stars 101 forks source link

Node.js support #184

Closed tresf closed 8 years ago

tresf commented 8 years ago

Work-in-progress. Not ready for merge.

image

@bberenz I'm going to need some help, specifically on the isActive FIXME's.

To test:

/*

sudo apt-get install curl
curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -
sudo apt-get install -y nodejs
npm config set registry http://registry.npmjs.org/
sudo npm install -g ws q

*/

var MODULES    = '/usr/local/lib/node_modules/';     // or '/usr/lib/node_modules/';
var INSTALL    = '/opt/qz-tray/demo/js/';

var WebSocket  = require(MODULES + 'ws');
var Q          = require(MODULES + 'q');
var crypto     = require('crypto');
var fs         = require("fs");
var qz         = require(INSTALL + 'qz-tray.js');

qz.api.showDebug(false);
qz.api.setWebSocketType(WebSocket);
qz.api.setPromiseType(Q.Promise);
qz.api.setSha256Type(function(data) {
    return crypto.createHash('sha256').update(data).digest("hex");
});
qz.security.setCertificatePromise(function(resolve, reject){
    resolve(fs.readFileSync('./digital-certificate.txt').toString());
});

qz.websocket.connect().then(qz.api.getVersion).then(function(ver) {
    console.log("\n\n");
    console.log("  ##############################");
    console.log("  #                            #");
    console.log("  #  Hello from QZ Tray " + ver + "  #");
    console.log("  #                            #");
    console.log("  ##############################\n\n");
}).then(
    qz.websocket.disconnect
).catch(console.error).then(process.exit);

-Tres

akberenz commented 8 years ago

Looks like it was a problem with the config, qz.websocket is a predefined collection of calls in our api, and should never come back as undefined (_qz.websocket.connection is the literal websocket). As I can't edit your fork, here are my proposed changes to your current commit. I've made qz self-executing so that all the internal references resolve correctly.

tresf commented 8 years ago

@bberenz, won't this new logic break the defined(qz) line?

I realized the only thing referencing the global qz object was the isActive, so I started this approach... c15ec79. Quite similar to yours.

else if (!_qz.tools.ws || !"WebSocket" in window ||

This can be misleading. I'd rather we bomb out elsewhere versus telling someone the browser doesn't support WS when the API was used incorrectly. :smile:

akberenz commented 8 years ago

I don't know. I've not used AMD to know how modules are setup for it. However, given that node was setup incorrectly, I strongly believe that it was too. It may have been that the examples I looked at used direct variables and not an enclosing function like we have, and I missed needing to execute it.

tresf commented 8 years ago

@bberenz I think this is ready for review/merge.

tresf commented 8 years ago

@bberenz on a side note, with this logic I seem to be getting this intermittently.

Do you see any race conditions in my Node.js code above? I don't know why this happens some times and not others, help appreciated.

Established connection with QZ Tray on ws://localhost:8182
[Error: Blocking message pending 10000 for BLOCKING]
akberenz commented 8 years ago

Is there anything in the QZ logs when you get that error message?

tresf commented 8 years ago

I ran QZ Tray and node from the same terminal so that the output from both races over itself... here's what I get with debug off..

Unfortunately with debug on, it won't reproduce... I'll keep trying.

[INFO] 2016-03-09 13:10:34,037 @ qz.common.TrayManager:?
    Allowed An anonymous request to connect to QZ
[DEBUG] 2016-03-09 13:10:34,079 @ qz.ws.PrintSocketClient:?
    Message: {"call":"getVersion","promise":{},"timestamp":1457547034073,"uid":"ocgc3h"}
[ERROR] 2016-03-09 13:10:34,091 @ qz.ws.PrintSocketClient:?
    Problem processing message
java.lang.IllegalStateException: Blocking message pending 10000 for BLOCKING
    at org.eclipse.jetty.websocket.common.WebSocketRemoteEndpoint.lockMsg(WebSocketRemoteEndpoint.java:130)
    at org.eclipse.jetty.websocket.common.WebSocketRemoteEndpoint.sendString(WebSocketRemoteEndpoint.java:379)
    at qz.ws.PrintSocketClient.send(Unknown Source)
    at qz.ws.PrintSocketClient.sendResult(Unknown Source)
    at qz.ws.PrintSocketClient.processMessage(Unknown Source)
    at qz.ws.PrintSocketClient.onMessage(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:70)
    at org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod.call(OptionalSessionCallableMethod.java:68)
    at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver$2.run(JettyAnnotatedEventDriver.java:210)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
    at java.lang.Thread.run(Thread.java:745)
[Error: Blocking message pending 10000 for BLOCKING]
ubuntu@ubuntu-1204:~/qz-print/js$ [INFO] 2016-03-09 13:10:34,122 @ qz.ws.PrintSocketClient:?
    Connection closed: 1006 - WebSocket Read EOF
[INFO] 2016-03-09 13:10:34,124 @ qz.ws.SocketConnection:?
    Closing all communication channels for An anonymous request
tresf commented 8 years ago

Updated Node.js example, with color...

In regards to the PR, @bberenz a sanity check before merging is appreciated.

image

-Tres

/*

sudo apt-get install curl
curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -
sudo apt-get install -y nodejs
npm config set registry http://registry.npmjs.org/
sudo npm install -g ws q colors

*/

var MODULES    = '/usr/lib/node_modules/';  // or '/usr/lib/node_modules/';
var INSTALL    = './';
var ASSETS     = '../assets';

var WebSocket  = require(MODULES + 'ws');
var Q          = require(MODULES + 'q');
var unused     = require(MODULES + 'colors');
var crypto     = require('crypto');
var fs         = require("fs");
var qz         = require(INSTALL + 'qz-tray.js');

qz.api.showDebug(false);
qz.api.setWebSocketType(WebSocket);
qz.api.setPromiseType(Q.Promise);
qz.api.setSha256Type(function(data) {
    return crypto.createHash('sha256').update(data).digest("hex");
});
qz.security.setCertificatePromise(function(resolve, reject){
    resolve(fs.readFileSync('./digital-certificate.txt').toString());
});

console.log("\n\n\nConnecting to QZ Tray...");
qz
    .websocket.connect()
    .then(function() {
        success("Connected");

        console.log("Checking version...");
        return qz.api.getVersion();
    })
    .then(function(ver) {
        success("Found version: " + ver);

        console.log("Searching for Pixel printer...");
        return qz.printers.find("PDF");
    })
    .then(function(printer) {
        success("Found printer: " + printer);

        var data = [{ type: 'html', format: 'file', data: ASSETS + '/html_sample.html' }];
        console.log("Disconnecting from socket...");
        return qz.websocket.disconnect;
    })
    .catch(console.error)
    .then(function() {
        success("Disconnected");
        process.exit();
});

var success = function(msg) { return status("success", msg); }
var warn = function(msg) { return status("warn", msg); }
var error = function(msg) { return status("error", msg); }
function status(type, msg) {
    switch(type) {
        case "error" : type = type.red; break;
        case "warn" : type = type.yellow; break;
        default: type = type.green;
    }
    console.log("  [%s] %s", type, msg);
}
akberenz commented 8 years ago

The code looks solid. In regards to the error, it looks like it's occurring when trying to send a reply back to node. Not sure why though. You didn't have multiple instances of node running, did you?

tresf commented 8 years ago

You didn't have multiple instances of node running, did you?

Not at all, just a single terminal window and I added the exit function at the end.

I'm afraid this sporadic hanging will break our unit testing and likely any client relying on Node-QZ usage. I was hoping you'd find a race condition with my promise logic, but since you haven't, I'm a bit worried... Is there a chance we're not raising a reject somewhere? Once this happens once, it continue to happen for quite a few tries. I'll see if the error is related to the ws library too, perhaps there are known issues with the node plugin.

tresf commented 8 years ago

The code looks solid.

Thanks for the review, merging.

tresf commented 8 years ago

@bberenz:

It appears that using Q, there's is a scenario where getVersion may be racing over connect and it's trying to send a message before the previous request is complete. Can you think of a scenario where this would happen?

tresf commented 8 years ago

@bberenz is this a typo? https://github.com/qzind/qz-print/blob/2.0/js/qz-tray.js#L366

akberenz commented 8 years ago

Typo as in it resolves without any processing? No. Developers are expected to override it with qz.security.setSignaturePromise if they want any signing, otherwise the signature variable on the passed json is set as undefined.

tresf commented 8 years ago

otherwise the signature variable on the passed json is set as undefined.

Ok, I just wasn't sure that calling undefined() was legal. :+1:

tresf commented 8 years ago

@bberenz my node version was very outdated. The technique I was using to update wasn't working. I've upgraded and haven't had a single instance of this [Error: Blocking message pending 10000 for BLOCKING] message.