// Map of tab identifers to tab-specific connection ports.
var browserPorts = {};
-
+
+const HATCH_EXT_NAME = 'org.evergreen_ils.hatch';
+const HATCH_RECONNECT_TIME = 5; // seconds
+
+var hatchHostUnavailable = false;
+function connectToHatch() {
+ console.debug("Connecting to native messaging host: " + HATCH_EXT_NAME);
+ try {
+ hatchPort = chrome.runtime.connectNative(HATCH_EXT_NAME);
+ hatchPort.onMessage.addListener(onNativeMessage);
+ hatchPort.onDisconnect.addListener(onDisconnected);
+ } catch (E) {
+ console.warn("Hatch host connection failed: " + E);
+ hatchHostUnavailable = true;
+ }
+}
/**
- * Handle response messages received from Hatch.
+ * Called when the connection to Hatch goes away.
*/
-function onNativeMessage(message) {
- var tabId = message.clientid;
+function onDisconnected() {
+ console.warn("Hatch disconnected: " + chrome.runtime.lastError.message);
+ hatchPort = null;
- if (tabId) {
- if (browserPorts[tabId]) {
- message.from = 'extension';
- browserPorts[tabId].postMessage(message);
+ if (hatchHostUnavailable) return;
- } else {
- console.warn(
- "Hatch message contains port ID " + tabId +
- " which was not found in the browser tab map. " +
- "Unable to deliver response to browser");
- }
+ // If we can reasonablly assume a connection to the Hatch host
+ // is possible, attempt to reconnect after a failure.
+ setTimeout(connectToHatch, HATCH_RECONNECT_TIME * 1000);
- } else {
- console.warn("Hatch response does not contain a 'clientid' value. " +
- "Unable to deliver response to browser");
- }
+ console.debug("Reconnecting to Hatch after connection failure in " +
+ HATCH_RECONNECT_TIME + " seconds...");
}
+
/**
- * Called when the connection to Hatch goes away.
+ * Handle response messages received from Hatch.
*/
-function onDisconnected() {
- console.warn("Hatch connection failed: " + chrome.runtime.lastError.message);
- hatchPort = null;
- browserPorts = {};
+function onNativeMessage(message) {
+ var tabId = message.clientid;
+
+ if (tabId && browserPorts[tabId]) {
+ message.from = 'extension';
+ browserPorts[tabId].postMessage(message);
+ } else {
+ // if browserPorts[tabId] is empty, it generally means the
+ // user navigated away before receiving the response.
+ }
}
port.onMessage.addListener(function(msg) {
console.debug("Received message from browser on port " + tabId);
- if (!msg) { // belt+suspenders
- console.warn("Received NULL message");
+ if (!hatchPort) {
+ // TODO: we could queue failed messages for redelivery
+ // after a reconnect. Not sure yet if that level of
+ // sophistication is necessary.
+ console.debug("Cannot send message " +
+ msg.msgid + " - no Hatch connection present");
return;
}
// tag the message with the browser tab ID for response routing.
msg.clientid = tabId;
- if (msg.action == 'init') {
- // "init" messages require origin info.
- // Extract just the protocol + host
- msg.origin = port.sender.url.match(/https?:\/\/[^\/]+/)[0];
- console.debug("Init'ing message with origin: " + msg.origin);
- }
+ // Stamp the origin (protocol + host) on every request.
+ msg.origin = port.sender.url.match(/https?:\/\/[^\/]+/)[0];
hatchPort.postMessage(msg);
});
});
-/**
- * Link the page action icon to loading the content script
- */
+// Link the page action icon to loading the content script
chrome.runtime.onInstalled.addListener(setPageActionRules);
-/**
- * Connect to Hatch on startup.
- */
-var hostName = "org.evergreen_ils.hatch";
-console.debug("Connecting to native messaging host: " + hostName);
-hatchPort = chrome.runtime.connectNative(hostName);
-hatchPort.onMessage.addListener(onNativeMessage);
-hatchPort.onDisconnect.addListener(onDisconnected);
+// Connect to Hatch on startup.
+connectToHatch();
# log files go to $SYSTEM_TMP/hatch.log
java.util.logging.FileHandler.pattern = %h/.evergreen/hatch.log
-java.util.logging.FileHandler.limit = 50000
+java.util.logging.FileHandler.limit = 50000000
# Log everything everywhre
org.evergreen_ils.hatch.level=ALL
*/
public void writeOneMessage(String message) throws IOException {
logger.finest("MessageWriter sending: " + message);
- System.out.write(intToBytes(message.length()));
+ System.out.write(intToBytes(message.getBytes("UTF-8").length));
System.out.write(message.getBytes("UTF-8"));
System.out.flush();
}
/** Root directory for all FileIO operations */
private static String profileDirectory = null;
- /** Origin host/domain used for segregating files by browser host */
- private static String origin = null;
-
private void configure() {
// Find the profile directory.
JSONObject request, JSONObject response) throws JSONException {
String action = request.getString("action");
+ String origin = request.getString("origin");
logger.info("Received message id=" +
response.get("msgid") + " action=" + action);
- // init must be called first to set the origin info. Init may be
- // called multiple times. Each call will update the origin info.
- if (action.equals("init")) {
- origin = request.getString("origin");
- return false;
- }
-
- if (origin == null) {
- response.put("status", 400);
- response.put("message", "'init' action must be called first!");
+ if ("".equals(origin)) {
+ response.put("status", 404);
+ response.put("message", "'origin' parameter required");
return false;
}
import java.util.logging.Logger;
import org.json.*;
+
public class TestHatch {
static MessageIO io;
static final Logger logger = Logger.getLogger("org.evergreen_ils.hatch");
+ static final String origin = "https://test.hatch.evergreen-ils.org";
public static void pause() {
try {
public static void doSends() {
int msgid = 1;
int clientid = 1;
-
- // initialize connection to set origin info
- JSONObject obj = new JSONObject();
- obj.put("msgid", msgid++);
- obj.put("clientid", clientid);
- obj.put("action", "init");
- obj.put("origin", "https://test.hatch.evergreen-ils.org");
- io.sendMessage(obj);
-
- pause();
+ JSONObject obj;
// get a list of stored keys
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "keys");
io.sendMessage(obj);
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "set");
obj.put("key", "eg.hatch.test.key1");
- obj.put("content", "Rando content, now with cheese");
+ obj.put("content", "Rando content, now with cheese and AljamĂa");
io.sendMessage(obj);
pause();
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "get");
obj.put("key", "eg.hatch.test.key1");
io.sendMessage(obj);
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "set");
obj.put("key", "eg.hatch.test.key2");
JSONArray arr = new JSONArray();
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "get");
obj.put("key", "eg.hatch.test.key2");
io.sendMessage(obj);
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "keys");
io.sendMessage(obj);
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "printers");
io.sendMessage(obj);
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "printer-options");
io.sendMessage(obj);
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "print");
obj.put("contentType", "text/plain");
obj.put("content", "Hello, World!");
obj = new JSONObject();
obj.put("msgid", msgid++);
obj.put("clientid", clientid);
+ obj.put("origin", origin);
obj.put("action", "print");
obj.put("contentType", "text/html");
obj.put("content", "<html><body><b>HELLO WORLD</b><img src='" +