Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

Commit

Permalink
Implement server.js
Browse files Browse the repository at this point in the history
  • Loading branch information
Hiroto Shioi committed Mar 6, 2019
1 parent e6a945a commit 2a7cf91
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,5 @@ exchange-topology.yaml
launch_*
result*
cabal.config

test-state
13 changes: 13 additions & 0 deletions app/NodeIPC/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Main where

import Cardano.Prelude
import NodeIPC.Lib (Port (..), getIPCHandle, startNodeJsIPC)
import NodeIPC.Message (ReadHandle (..), WriteHandle (..))

main :: IO ()
main = do
hndl <- getIPCHandle
let readHndl = ReadHandle hndl
let writeHndl = WriteHandle hndl
let port = Port 8090
startNodeJsIPC readHndl writeHndl port
23 changes: 23 additions & 0 deletions cardano-shell.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,29 @@ executable cardano-shell-exe
-Wredundant-constraints
-Wpartial-fields

executable node-ipc
main-is: Main.hs
other-modules:
Paths_cardano_shell
hs-source-dirs:
app/NodeIPC
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends:
base >=4.7 && <5
, cardano-shell
, cardano-prelude
default-language: Haskell2010
default-extensions: NoImplicitPrelude
OverloadedStrings

ghc-options: -Wall
-Werror
-Wcompat
-Wincomplete-record-updates
-Wincomplete-uni-patterns
-Wredundant-constraints
-Wpartial-fields

test-suite cardano-shell-test
type: exitcode-stdio-1.0
main-is: Spec.hs
Expand Down
22 changes: 17 additions & 5 deletions src/NodeIPC/Lib.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module NodeIPC.Lib
, MsgIn(..)
, MsgOut(..)
, NodeIPCException(..)
, MessageSendFailure(..)
) where

import Cardano.Prelude hiding (catches)
Expand Down Expand Up @@ -46,6 +47,7 @@ data MsgIn
-- ^ Ask which port to use
| Ping
-- ^ Ping
| MessageInFailure MessageSendFailure
deriving (Show, Eq, Generic)

instance Arbitrary MsgIn where
Expand All @@ -59,9 +61,12 @@ data MsgOut
-- ^ Reply of QueryPort
| Pong
-- ^ Reply of Ping
| ParseError Text
-- ^ Message notifying the client, that the
-- incoming message could not be parsed
| MessageOutFailure MessageSendFailure
deriving (Show, Eq, Generic)

data MessageSendFailure
= ParseError Text
| GeneralFailure
deriving (Show, Eq, Generic)

instance Arbitrary MsgOut where
Expand All @@ -72,7 +77,7 @@ instance Arbitrary MsgOut where
[ Started
, ReplyPort randomPort
, Pong
, ParseError safeText
, MessageOutFailure $ ParseError safeText
]
where
genSafeText :: Gen Text
Expand All @@ -93,6 +98,12 @@ instance ToJSON MsgOut where
instance FromJSON MsgOut where
parseJSON = genericParseJSON opts

instance FromJSON MessageSendFailure where
parseJSON = genericParseJSON opts

instance ToJSON MessageSendFailure where
toEncoding = genericToEncoding opts

-- | Port that is used to communicate between Cardano-node and Daedalus
-- (e.g 8090)
newtype Port = Port
Expand Down Expand Up @@ -153,6 +164,7 @@ ipcListener readHndl@(ReadHandle rHndl) writeHndl@(WriteHandle wHndl) (Port port
Ping -> do
send Pong
shutdown
MessageInFailure _ -> shutdown

send :: MsgOut -> m ()
send = sendMessage writeHndl
Expand All @@ -170,7 +182,7 @@ ipcListener readHndl@(ReadHandle rHndl) writeHndl@(WriteHandle wHndl) (Port port
handleMsgError :: MessageException -> m ()
handleMsgError err = do
liftIO $ logError "Unexpected message"
send $ ParseError $ show err
send $ MessageOutFailure $ ParseError $ show err

-- (TODO:) Exception handling on broken handles (e.g.handle is already closed etc.)
-- Implement here
Expand Down
82 changes: 82 additions & 0 deletions src/NodeIPC/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// This process implicitly sets env varibale "NODE_CHANNEL_FD" with a fd it currently uses
const child_process = require("child_process");
const fs = require('fs');

// Filepath to resources
const testDir = "test-state";
const logPath = "test-state/cardano-node.log"

// Acquiring resources
function acquire () {
if (!fs.existsSync(testDir)) {
fs.mkdirSync(testDir);
};

let writeStream = fs.createWriteStream(logPath, { flags: "a" });
return writeStream;
}

// Clean up resources
function cleanup () {
fs.unlinkSync(logPath);
fs.rmdirSync(testDir);
};

// Return Subprocess with given writeStream and timerid
function getSubProcess(writeStream, timerid) {
const subproc = child_process.spawn("stack", [
"exec"
, "node-ipc"
], {
stdio: [ "inherit", writeStream, writeStream, "ipc" ]
});
subproc.on("message", function (msg) {
console.log(new Date(), "got reply",msg);
});
subproc.on("close", function(code, signal) {
console.log("all stdio to child has been closed", code, signal);
});
subproc.on("disconnect", function() {
console.log(new Date(), "all IPC handles closed");
});
subproc.on("error", function (err) {
console.log("error:", err);
cleanup();
});
subproc.on("exit", function (code, signal) {
console.log(new Date(), "child exited", code, signal);
cleanup();
clearTimeout(timerid);
if (code == 20) {
process.exit(0);
} else {
process.exit(-1);
}
});

return subproc;
}

// Actual process
// I honestly don't know if this can clean-up the resources if async exception occurs
console.log(new Date(), "in parent")
let logfile = acquire ();

logfile.on("open", function () {
let timerid;
let subprocess = getSubProcess(logfile, timerid);

// Action
console.log(new Date(), "log file opened");
subprocess.send({QueryPort:[]});

timerid = setTimeout(function () {
//process.exit();
console.log(new Date(), "sending disconnect");
subprocess.disconnect();
timerid = setTimeout(function () {
console.log(new Date(), "it failed to exit, killing");
subprocess.kill();
},30000);
}, 30000);
});
4 changes: 2 additions & 2 deletions test/NodeIPCSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Test.QuickCheck.Monadic (assert, monadicIO, run)
import NodeIPC.Example (exampleWithFD, exampleWithProcess,
getReadWriteHandles)
import NodeIPC.Lib (MsgIn (..), MsgOut (..), NodeIPCException (..),
Port (..), startNodeJsIPC)
Port (..), startNodeJsIPC, MessageSendFailure(..))
import NodeIPC.Message

-- | Test spec for node IPC
Expand Down Expand Up @@ -62,7 +62,7 @@ nodeIPCSpec = do
testStartNodeIPC port randomMsg
let errorMessage = "Failed to decode given blob: " <> toS (encode randomMsg)
assert $ started == Started
assert $ parseError == (ParseError errorMessage)
assert $ parseError == (MessageOutFailure $ ParseError errorMessage)

it "should throw NodeIPCException when IOError is being thrown" $ monadicIO $ do
eResult <- run $ try $ do
Expand Down

0 comments on commit 2a7cf91

Please sign in to comment.