Rename withDynamic to withEphemeralPorts and cleanup

parent 54f6e8a3
...@@ -10,8 +10,8 @@ module IHaskell.IPython.ZeroMQ ...@@ -10,8 +10,8 @@ module IHaskell.IPython.ZeroMQ
, ZeroMQStdin(..) , ZeroMQStdin(..)
, serveProfile , serveProfile
, serveStdin , serveStdin
, ZeroMQDynamicPorts(..) , ZeroMQEphemeralPorts(..)
, withDynamic , withEphemeralPorts
) where ) where
import Control.Concurrent import Control.Concurrent
...@@ -26,7 +26,8 @@ import Data.Digest.Pure.SHA as SHA ...@@ -26,7 +26,8 @@ import Data.Digest.Pure.SHA as SHA
import Data.Monoid ((<>)) import Data.Monoid ((<>))
import qualified Data.Text.Encoding as Text import qualified Data.Text.Encoding as Text
import System.IO.Unsafe import System.IO.Unsafe
import System.ZMQ4 hiding (stdin) import System.ZMQ4 as ZMQ4 hiding (stdin)
import Text.Read (readMaybe)
import IHaskell.IPython.Types import IHaskell.IPython.Types
import IHaskell.IPython.Message.Parser import IHaskell.IPython.Message.Parser
...@@ -91,44 +92,50 @@ serveProfile profile debug = do ...@@ -91,44 +92,50 @@ serveProfile profile debug = do
return channels return channels
data ZeroMQDynamicPorts data ZeroMQEphemeralPorts
= ZeroMQDynamicPorts { dynHbPort :: !Port = ZeroMQEphemeralPorts { ephHbPort :: !Port
, dynControlPort :: !Port , ephControlPort :: !Port
, dynShellPort :: !Port , ephShellPort :: !Port
, dynIOPubPort :: !Port , ephIOPubPort :: !Port
, dynSignatureKey :: !ByteString , ephSignatureKey :: !ByteString
} }
instance ToJSON ZeroMQDynamicPorts where instance ToJSON ZeroMQEphemeralPorts where
toJSON ports = toJSON ports =
object [ "ip" .= ("127.0.0.1" :: String) object [ "ip" .= ("127.0.0.1" :: String)
, "transport" .= TCP , "transport" .= TCP
, "control_port" .= dynControlPort ports , "control_port" .= ephControlPort ports
, "hb_port" .= dynHbPort ports , "hb_port" .= ephHbPort ports
, "shell_port" .= dynShellPort ports , "shell_port" .= ephShellPort ports
, "iopub_port" .= dynIOPubPort ports , "iopub_port" .= ephIOPubPort ports
, "key" .= Text.decodeUtf8 (dynSignatureKey ports) , "key" .= Text.decodeUtf8 (ephSignatureKey ports)
] ]
parsePort :: String -> Int parsePort :: String -> Maybe Int
parsePort s = read num parsePort s = readMaybe num
where num = reverse (takeWhile isNumber (reverse s)) where num = reverse (takeWhile isNumber (reverse s))
bindLocalDynamicPort :: Socket a -> IO Int bindLocalEphemeralPort :: Socket a -> IO Int
bindLocalDynamicPort socket = do bindLocalEphemeralPort socket = do
bind socket $ "tcp://127.0.0.1:*" bind socket $ "tcp://127.0.0.1:*"
parsePort <$> lastEndpoint socket endpointString <- lastEndpoint socket
case parsePort endpointString of
Nothing ->
fail $ "internalError: IHaskell.IPython.ZeroMQ.bindLocalEphemeralPort encountered a port index that could not be interpreted as an int."
Just endpointIndex ->
return endpointIndex
-- | Start responding on all ZeroMQ channels used to communicate with IPython -- | Start responding on all ZeroMQ channels used to communicate with IPython
-- | with dynamic ports. -- with ephemerally allocated ports.
-- Profide the callback with the dynamic ports chosen and a ZeroMQInterface. -- Profide the callback with the ports chosen and a ZeroMQInterface.
withDynamic :: ByteString withEphemeralPorts :: ByteString
-- ^ HMAC encryption key -- ^ HMAC encryption key
-> Bool -- ^ Print debug output -> Bool
-> (ZeroMQDynamicPorts -> ZeroMQInterface -> IO a) -- ^ Print debug output
-- ^ Callback that takes the interface to the sockets. -> (ZeroMQEphemeralPorts -> ZeroMQInterface -> IO a)
-> IO a -- ^ Callback that takes the interface to the sockets.
withDynamic key debug callback = do -> IO a
withEphemeralPorts key debug callback = do
-- Create all channels which will be used for higher level communication. -- Create all channels which will be used for higher level communication.
shellReqChan <- newChan shellReqChan <- newChan
shellRepChan <- newChan shellRepChan <- newChan
...@@ -137,31 +144,31 @@ withDynamic key debug callback = do ...@@ -137,31 +144,31 @@ withDynamic key debug callback = do
iopubChan <- newChan iopubChan <- newChan
let channels = Channels shellReqChan shellRepChan controlReqChan controlRepChan iopubChan key let channels = Channels shellReqChan shellRepChan controlReqChan controlRepChan iopubChan key
-- Create the context in a separate thread that never finishes. If withContext or withSocket -- Create the ZMQ4 context
-- complete, the context or socket become invalid.
withContext $ \context -> do withContext $ \context -> do
-- Serve on all sockets. -- Create the sockets to communicate with.
withSocket context Rep $ \heartbeat_socket -> do withSocket context Rep $ \heartbeatSocket -> do
withSocket context Router $ \controlport_socket -> do withSocket context Router $ \controlportSocket -> do
withSocket context Router $ \shellport_socket -> do withSocket context Router $ \shellportSocket -> do
withSocket context Pub $ \iopub_socket -> do withSocket context Pub $ \iopubSocket -> do
dyn_hb_port <- bindLocalDynamicPort heartbeat_socket -- Bind each socket to a local port, getting the port chosen.
dyn_control_port <- bindLocalDynamicPort controlport_socket hbPort <- bindLocalEphemeralPort heartbeatSocket
dyn_shell_port <- bindLocalDynamicPort shellport_socket controlPort <- bindLocalEphemeralPort controlportSocket
dyn_iopub_port <- bindLocalDynamicPort iopub_socket shellPort <- bindLocalEphemeralPort shellportSocket
iopubPort <- bindLocalEphemeralPort iopubSocket
_ <- forkIO $ forever $ heartbeat channels heartbeat_socket
_ <- forkIO $ forever $ control debug channels controlport_socket _ <- forkIO $ forever $ heartbeat channels heartbeatSocket
_ <- forkIO $ forever $ shell debug channels shellport_socket _ <- forkIO $ forever $ control debug channels controlportSocket
_ <- forkIO $ forever $ checked_iopub debug channels iopub_socket _ <- forkIO $ forever $ shell debug channels shellportSocket
_ <- forkIO $ forever $ checkedIOpub debug channels iopubSocket
let ports = ZeroMQDynamicPorts { dynHbPort = dyn_hb_port
, dynControlPort = dyn_control_port let ports = ZeroMQEphemeralPorts { ephHbPort = hbPort
, dynShellPort = dyn_shell_port , ephControlPort = controlPort
, dynIOPubPort = dyn_iopub_port , ephShellPort = shellPort
, dynSignatureKey = key , ephIOPubPort = iopubPort
} , ephSignatureKey = key
}
callback ports channels callback ports channels
serveStdin :: Profile -> IO ZeroMQStdin serveStdin :: Profile -> IO ZeroMQStdin
...@@ -237,17 +244,17 @@ iopub debug channels socket = ...@@ -237,17 +244,17 @@ iopub debug channels socket =
-- | Send messages via the iopub channel. | This reads messages from the ZeroMQ iopub interface -- | Send messages via the iopub channel. | This reads messages from the ZeroMQ iopub interface
-- channel | and then writes the messages to the socket. -- channel | and then writes the messages to the socket.
checked_iopub :: Bool -> ZeroMQInterface -> Socket Pub -> IO () checkedIOpub :: Bool -> ZeroMQInterface -> Socket Pub -> IO ()
checked_iopub debug channels socket = do checkedIOpub debug channels socket = do
msg <- readChan (iopubChannel channels) msg <- readChan (iopubChannel channels)
let error_handler :: ZMQError -> IO () let errorHandler :: ZMQError -> IO ()
error_handler e errorHandler e
-- Ignore errors if we cannot send. -- Ignore errors if we cannot send.
-- We may want to cascade this back to channel. -- We may want to cascade this back to channel.
| errno e == 38 = return () | errno e == 38 = return ()
| otherwise = throwIO e | otherwise = throwIO e
catch (sendMessage debug (hmacKey channels) socket msg) catch (sendMessage debug (hmacKey channels) socket msg)
error_handler errorHandler
-- | Receive and parse a message from a socket. -- | Receive and parse a message from a socket.
receiveMessage :: Receiver a => Bool -> Socket a -> IO Message receiveMessage :: Receiver a => Bool -> Socket a -> IO Message
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment