Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / UDSServer.hs @ c7003a76

History | View | Annotate | Download (11.3 kB)

1 71a4c605 Petr Pudlak
{-# LANGUAGE TemplateHaskell #-}
2 71a4c605 Petr Pudlak
3 71a4c605 Petr Pudlak
{-| Implementation of the Ganeti Unix Domain Socket JSON server interface.
4 71a4c605 Petr Pudlak
5 71a4c605 Petr Pudlak
-}
6 71a4c605 Petr Pudlak
7 71a4c605 Petr Pudlak
{-
8 71a4c605 Petr Pudlak
9 71a4c605 Petr Pudlak
Copyright (C) 2013 Google Inc.
10 71a4c605 Petr Pudlak
11 71a4c605 Petr Pudlak
This program is free software; you can redistribute it and/or modify
12 71a4c605 Petr Pudlak
it under the terms of the GNU General Public License as published by
13 71a4c605 Petr Pudlak
the Free Software Foundation; either version 2 of the License, or
14 71a4c605 Petr Pudlak
(at your option) any later version.
15 71a4c605 Petr Pudlak
16 71a4c605 Petr Pudlak
This program is distributed in the hope that it will be useful, but
17 71a4c605 Petr Pudlak
WITHOUT ANY WARRANTY; without even the implied warranty of
18 71a4c605 Petr Pudlak
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 71a4c605 Petr Pudlak
General Public License for more details.
20 71a4c605 Petr Pudlak
21 71a4c605 Petr Pudlak
You should have received a copy of the GNU General Public License
22 71a4c605 Petr Pudlak
along with this program; if not, write to the Free Software
23 71a4c605 Petr Pudlak
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 71a4c605 Petr Pudlak
02110-1301, USA.
25 71a4c605 Petr Pudlak
26 71a4c605 Petr Pudlak
-}
27 71a4c605 Petr Pudlak
28 71a4c605 Petr Pudlak
module Ganeti.UDSServer
29 0fbc8447 Petr Pudlak
  ( ConnectConfig(..)
30 0fbc8447 Petr Pudlak
  , Client
31 0fbc8447 Petr Pudlak
  , Server
32 71a4c605 Petr Pudlak
  , RecvResult(..)
33 71a4c605 Petr Pudlak
  , MsgKeys(..)
34 71a4c605 Petr Pudlak
  , strOfKey
35 0fbc8447 Petr Pudlak
  , connectClient
36 0fbc8447 Petr Pudlak
  , connectServer
37 71a4c605 Petr Pudlak
  , acceptClient
38 71a4c605 Petr Pudlak
  , closeClient
39 71a4c605 Petr Pudlak
  , closeServer
40 71a4c605 Petr Pudlak
  , buildResponse
41 6e47412f Petr Pudlak
  , parseCall
42 71a4c605 Petr Pudlak
  , recvMsg
43 71a4c605 Petr Pudlak
  , recvMsgExt
44 71a4c605 Petr Pudlak
  , sendMsg
45 d79a6502 Petr Pudlak
  -- * Client handler
46 d79a6502 Petr Pudlak
  , Handler(..)
47 d79a6502 Petr Pudlak
  , HandlerResult
48 c7003a76 Petr Pudlak
  , listener
49 71a4c605 Petr Pudlak
  ) where
50 71a4c605 Petr Pudlak
51 6e47412f Petr Pudlak
import Control.Applicative
52 c7003a76 Petr Pudlak
import Control.Concurrent (forkIO)
53 71a4c605 Petr Pudlak
import Control.Exception (catch)
54 71a4c605 Petr Pudlak
import Data.IORef
55 71a4c605 Petr Pudlak
import qualified Data.ByteString as B
56 71a4c605 Petr Pudlak
import qualified Data.ByteString.Lazy as BL
57 71a4c605 Petr Pudlak
import qualified Data.ByteString.UTF8 as UTF8
58 71a4c605 Petr Pudlak
import qualified Data.ByteString.Lazy.UTF8 as UTF8L
59 71a4c605 Petr Pudlak
import Data.Word (Word8)
60 71a4c605 Petr Pudlak
import Control.Monad
61 71a4c605 Petr Pudlak
import qualified Network.Socket as S
62 71a4c605 Petr Pudlak
import System.Directory (removeFile)
63 71a4c605 Petr Pudlak
import System.IO (hClose, hFlush, hWaitForInput, Handle, IOMode(..))
64 71a4c605 Petr Pudlak
import System.IO.Error (isEOFError)
65 71a4c605 Petr Pudlak
import System.Timeout
66 6e47412f Petr Pudlak
import Text.JSON (encodeStrict, decodeStrict)
67 6e47412f Petr Pudlak
import qualified Text.JSON as J
68 71a4c605 Petr Pudlak
import Text.JSON.Types
69 71a4c605 Petr Pudlak
70 6e47412f Petr Pudlak
import Ganeti.BasicTypes
71 d79a6502 Petr Pudlak
import Ganeti.Errors (GanetiException)
72 6e47412f Petr Pudlak
import Ganeti.JSON
73 c7003a76 Petr Pudlak
import Ganeti.Logging
74 71a4c605 Petr Pudlak
import Ganeti.Runtime (GanetiDaemon(..), MiscGroup(..), GanetiGroup(..))
75 71a4c605 Petr Pudlak
import Ganeti.THH
76 71a4c605 Petr Pudlak
import Ganeti.Utils
77 71a4c605 Petr Pudlak
78 71a4c605 Petr Pudlak
79 71a4c605 Petr Pudlak
-- * Utility functions
80 71a4c605 Petr Pudlak
81 71a4c605 Petr Pudlak
-- | Wrapper over System.Timeout.timeout that fails in the IO monad.
82 71a4c605 Petr Pudlak
withTimeout :: Int -> String -> IO a -> IO a
83 71a4c605 Petr Pudlak
withTimeout secs descr action = do
84 71a4c605 Petr Pudlak
  result <- timeout (secs * 1000000) action
85 71a4c605 Petr Pudlak
  case result of
86 71a4c605 Petr Pudlak
    Nothing -> fail $ "Timeout in " ++ descr
87 71a4c605 Petr Pudlak
    Just v -> return v
88 71a4c605 Petr Pudlak
89 71a4c605 Petr Pudlak
90 71a4c605 Petr Pudlak
-- * Generic protocol functionality
91 71a4c605 Petr Pudlak
92 71a4c605 Petr Pudlak
-- | Result of receiving a message from the socket.
93 71a4c605 Petr Pudlak
data RecvResult = RecvConnClosed    -- ^ Connection closed
94 71a4c605 Petr Pudlak
                | RecvError String  -- ^ Any other error
95 71a4c605 Petr Pudlak
                | RecvOk String     -- ^ Successfull receive
96 71a4c605 Petr Pudlak
                  deriving (Show, Eq)
97 71a4c605 Petr Pudlak
98 71a4c605 Petr Pudlak
99 71a4c605 Petr Pudlak
-- | The end-of-message separator.
100 71a4c605 Petr Pudlak
eOM :: Word8
101 71a4c605 Petr Pudlak
eOM = 3
102 71a4c605 Petr Pudlak
103 71a4c605 Petr Pudlak
-- | The end-of-message encoded as a ByteString.
104 71a4c605 Petr Pudlak
bEOM :: B.ByteString
105 71a4c605 Petr Pudlak
bEOM = B.singleton eOM
106 71a4c605 Petr Pudlak
107 71a4c605 Petr Pudlak
-- | Valid keys in the requests and responses.
108 71a4c605 Petr Pudlak
data MsgKeys = Method
109 71a4c605 Petr Pudlak
             | Args
110 71a4c605 Petr Pudlak
             | Success
111 71a4c605 Petr Pudlak
             | Result
112 71a4c605 Petr Pudlak
113 71a4c605 Petr Pudlak
-- | The serialisation of MsgKeys into strings in messages.
114 71a4c605 Petr Pudlak
$(genStrOfKey ''MsgKeys "strOfKey")
115 71a4c605 Petr Pudlak
116 71a4c605 Petr Pudlak
117 0fbc8447 Petr Pudlak
data ConnectConfig = ConnectConfig
118 0fbc8447 Petr Pudlak
                     { connDaemon :: GanetiDaemon
119 0fbc8447 Petr Pudlak
                     , recvTmo :: Int
120 0fbc8447 Petr Pudlak
                     , sendTmo :: Int
121 0fbc8447 Petr Pudlak
                     }
122 0fbc8447 Petr Pudlak
123 0fbc8447 Petr Pudlak
-- | A client encapsulation.
124 71a4c605 Petr Pudlak
data Client = Client { socket :: Handle           -- ^ The socket of the client
125 71a4c605 Petr Pudlak
                     , rbuf :: IORef B.ByteString -- ^ Already received buffer
126 0fbc8447 Petr Pudlak
                     , clientConfig :: ConnectConfig
127 71a4c605 Petr Pudlak
                     }
128 71a4c605 Petr Pudlak
129 0fbc8447 Petr Pudlak
-- | A server encapsulation.
130 0fbc8447 Petr Pudlak
data Server = Server { sSocket :: S.Socket        -- ^ The bound server socket
131 5e671e0e Petr Pudlak
                     , sPath :: FilePath          -- ^ The scoket's path
132 0fbc8447 Petr Pudlak
                     , serverConfig :: ConnectConfig
133 0fbc8447 Petr Pudlak
                     }
134 0fbc8447 Petr Pudlak
135 0fbc8447 Petr Pudlak
136 0fbc8447 Petr Pudlak
-- | Connects to the master daemon and returns a Client.
137 0fbc8447 Petr Pudlak
connectClient
138 0fbc8447 Petr Pudlak
  :: ConnectConfig    -- ^ configuration for the client
139 0fbc8447 Petr Pudlak
  -> Int              -- ^ connection timeout
140 0fbc8447 Petr Pudlak
  -> FilePath         -- ^ socket path
141 0fbc8447 Petr Pudlak
  -> IO Client
142 0fbc8447 Petr Pudlak
connectClient conf tmo path = do
143 71a4c605 Petr Pudlak
  s <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
144 0fbc8447 Petr Pudlak
  withTimeout tmo "creating a connection" $
145 71a4c605 Petr Pudlak
              S.connect s (S.SockAddrUnix path)
146 71a4c605 Petr Pudlak
  rf <- newIORef B.empty
147 71a4c605 Petr Pudlak
  h <- S.socketToHandle s ReadWriteMode
148 0fbc8447 Petr Pudlak
  return Client { socket=h, rbuf=rf, clientConfig=conf }
149 71a4c605 Petr Pudlak
150 71a4c605 Petr Pudlak
-- | Creates and returns a server endpoint.
151 0fbc8447 Petr Pudlak
connectServer :: ConnectConfig -> Bool -> FilePath -> IO Server
152 0fbc8447 Petr Pudlak
connectServer conf setOwner path = do
153 71a4c605 Petr Pudlak
  s <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
154 71a4c605 Petr Pudlak
  S.bindSocket s (S.SockAddrUnix path)
155 0fbc8447 Petr Pudlak
  when setOwner . setOwnerAndGroupFromNames path (connDaemon conf) $
156 71a4c605 Petr Pudlak
    ExtraGroup DaemonsGroup
157 71a4c605 Petr Pudlak
  S.listen s 5 -- 5 is the max backlog
158 5e671e0e Petr Pudlak
  return Server { sSocket=s, sPath=path, serverConfig=conf }
159 71a4c605 Petr Pudlak
160 71a4c605 Petr Pudlak
-- | Closes a server endpoint.
161 71a4c605 Petr Pudlak
-- FIXME: this should be encapsulated into a nicer type.
162 5e671e0e Petr Pudlak
closeServer :: Server -> IO ()
163 5e671e0e Petr Pudlak
closeServer server = do
164 0fbc8447 Petr Pudlak
  S.sClose (sSocket server)
165 5e671e0e Petr Pudlak
  removeFile (sPath server)
166 71a4c605 Petr Pudlak
167 71a4c605 Petr Pudlak
-- | Accepts a client
168 0fbc8447 Petr Pudlak
acceptClient :: Server -> IO Client
169 71a4c605 Petr Pudlak
acceptClient s = do
170 71a4c605 Petr Pudlak
  -- second return is the address of the client, which we ignore here
171 0fbc8447 Petr Pudlak
  (client_socket, _) <- S.accept (sSocket s)
172 71a4c605 Petr Pudlak
  new_buffer <- newIORef B.empty
173 71a4c605 Petr Pudlak
  handle <- S.socketToHandle client_socket ReadWriteMode
174 0fbc8447 Petr Pudlak
  return Client { socket=handle
175 0fbc8447 Petr Pudlak
                , rbuf=new_buffer
176 0fbc8447 Petr Pudlak
                , clientConfig=serverConfig s
177 0fbc8447 Petr Pudlak
                }
178 71a4c605 Petr Pudlak
179 71a4c605 Petr Pudlak
-- | Closes the client socket.
180 71a4c605 Petr Pudlak
closeClient :: Client -> IO ()
181 71a4c605 Petr Pudlak
closeClient = hClose . socket
182 71a4c605 Petr Pudlak
183 0fbc8447 Petr Pudlak
-- | Sends a message over a transport.
184 71a4c605 Petr Pudlak
sendMsg :: Client -> String -> IO ()
185 0fbc8447 Petr Pudlak
sendMsg s buf = withTimeout (sendTmo $ clientConfig s) "sending a message" $ do
186 71a4c605 Petr Pudlak
  let encoded = UTF8L.fromString buf
187 71a4c605 Petr Pudlak
      handle = socket s
188 71a4c605 Petr Pudlak
  BL.hPut handle encoded
189 71a4c605 Petr Pudlak
  B.hPut handle bEOM
190 71a4c605 Petr Pudlak
  hFlush handle
191 71a4c605 Petr Pudlak
192 71a4c605 Petr Pudlak
-- | Given a current buffer and the handle, it will read from the
193 71a4c605 Petr Pudlak
-- network until we get a full message, and it will return that
194 71a4c605 Petr Pudlak
-- message and the leftover buffer contents.
195 0fbc8447 Petr Pudlak
recvUpdate :: ConnectConfig -> Handle -> B.ByteString
196 0fbc8447 Petr Pudlak
           -> IO (B.ByteString, B.ByteString)
197 0fbc8447 Petr Pudlak
recvUpdate conf handle obuf = do
198 0fbc8447 Petr Pudlak
  nbuf <- withTimeout (recvTmo conf) "reading a response" $ do
199 71a4c605 Petr Pudlak
            _ <- hWaitForInput handle (-1)
200 71a4c605 Petr Pudlak
            B.hGetNonBlocking handle 4096
201 71a4c605 Petr Pudlak
  let (msg, remaining) = B.break (eOM ==) nbuf
202 71a4c605 Petr Pudlak
      newbuf = B.append obuf msg
203 71a4c605 Petr Pudlak
  if B.null remaining
204 0fbc8447 Petr Pudlak
    then recvUpdate conf handle newbuf
205 71a4c605 Petr Pudlak
    else return (newbuf, B.tail remaining)
206 71a4c605 Petr Pudlak
207 0fbc8447 Petr Pudlak
-- | Waits for a message over a transport.
208 71a4c605 Petr Pudlak
recvMsg :: Client -> IO String
209 71a4c605 Petr Pudlak
recvMsg s = do
210 71a4c605 Petr Pudlak
  cbuf <- readIORef $ rbuf s
211 71a4c605 Petr Pudlak
  let (imsg, ibuf) = B.break (eOM ==) cbuf
212 71a4c605 Petr Pudlak
  (msg, nbuf) <-
213 71a4c605 Petr Pudlak
    if B.null ibuf      -- if old buffer didn't contain a full message
214 0fbc8447 Petr Pudlak
                        -- then we read from network:
215 0fbc8447 Petr Pudlak
      then recvUpdate (clientConfig s) (socket s) cbuf
216 71a4c605 Petr Pudlak
      else return (imsg, B.tail ibuf)   -- else we return data from our buffer
217 71a4c605 Petr Pudlak
  writeIORef (rbuf s) nbuf
218 71a4c605 Petr Pudlak
  return $ UTF8.toString msg
219 71a4c605 Petr Pudlak
220 71a4c605 Petr Pudlak
-- | Extended wrapper over recvMsg.
221 71a4c605 Petr Pudlak
recvMsgExt :: Client -> IO RecvResult
222 71a4c605 Petr Pudlak
recvMsgExt s =
223 71a4c605 Petr Pudlak
  Control.Exception.catch (liftM RecvOk (recvMsg s)) $ \e ->
224 71a4c605 Petr Pudlak
    return $ if isEOFError e
225 71a4c605 Petr Pudlak
               then RecvConnClosed
226 71a4c605 Petr Pudlak
               else RecvError (show e)
227 71a4c605 Petr Pudlak
228 71a4c605 Petr Pudlak
229 6e47412f Petr Pudlak
-- | Parse the required keys out of a call.
230 d79a6502 Petr Pudlak
parseCall :: (J.JSON mth, J.JSON args) => String -> Result (mth, args)
231 6e47412f Petr Pudlak
parseCall s = do
232 6e47412f Petr Pudlak
  arr <- fromJResult "parsing top-level JSON message" $
233 6e47412f Petr Pudlak
           decodeStrict s :: Result (JSObject JSValue)
234 6e47412f Petr Pudlak
  let keyFromObj :: (J.JSON a) => MsgKeys -> Result a
235 6e47412f Petr Pudlak
      keyFromObj = fromObj (fromJSObject arr) . strOfKey
236 6e47412f Petr Pudlak
  (,) <$> keyFromObj Method <*> keyFromObj Args
237 6e47412f Petr Pudlak
238 6e47412f Petr Pudlak
239 71a4c605 Petr Pudlak
-- | Serialize the response to String.
240 71a4c605 Petr Pudlak
buildResponse :: Bool    -- ^ Success
241 71a4c605 Petr Pudlak
              -> JSValue -- ^ The arguments
242 71a4c605 Petr Pudlak
              -> String  -- ^ The serialized form
243 71a4c605 Petr Pudlak
buildResponse success args =
244 71a4c605 Petr Pudlak
  let ja = [ (strOfKey Success, JSBool success)
245 71a4c605 Petr Pudlak
           , (strOfKey Result, args)]
246 71a4c605 Petr Pudlak
      jo = toJSObject ja
247 71a4c605 Petr Pudlak
  in encodeStrict jo
248 d79a6502 Petr Pudlak
249 c7003a76 Petr Pudlak
-- | Logs an outgoing message.
250 c7003a76 Petr Pudlak
logMsg
251 c7003a76 Petr Pudlak
    :: (Show e, J.JSON e, MonadLog m)
252 c7003a76 Petr Pudlak
    => Handler i o
253 c7003a76 Petr Pudlak
    -> i                          -- ^ the received request (used for logging)
254 c7003a76 Petr Pudlak
    -> GenericResult e J.JSValue  -- ^ A message to be sent
255 c7003a76 Petr Pudlak
    -> m ()
256 c7003a76 Petr Pudlak
logMsg handler req (Bad err) =
257 c7003a76 Petr Pudlak
  logWarning $ "Failed to execute request " ++ hInputLogLong handler req ++ ": "
258 c7003a76 Petr Pudlak
               ++ show err
259 c7003a76 Petr Pudlak
logMsg handler req (Ok result) = do
260 c7003a76 Petr Pudlak
  -- only log the first 2,000 chars of the result
261 c7003a76 Petr Pudlak
  logDebug $ "Result (truncated): " ++ take 2000 (J.encode result)
262 c7003a76 Petr Pudlak
  logInfo $ "Successfully handled " ++ hInputLogShort handler req
263 c7003a76 Petr Pudlak
264 c7003a76 Petr Pudlak
-- | Prepares an outgoing message.
265 c7003a76 Petr Pudlak
prepareMsg
266 c7003a76 Petr Pudlak
    :: (J.JSON e)
267 c7003a76 Petr Pudlak
    => GenericResult e J.JSValue  -- ^ A message to be sent
268 c7003a76 Petr Pudlak
    -> (Bool, J.JSValue)
269 c7003a76 Petr Pudlak
prepareMsg (Bad err)   = (False, J.showJSON err)
270 c7003a76 Petr Pudlak
prepareMsg (Ok result) = (True, result)
271 d79a6502 Petr Pudlak
272 d79a6502 Petr Pudlak
273 d79a6502 Petr Pudlak
-- * Processing client requests
274 d79a6502 Petr Pudlak
275 d79a6502 Petr Pudlak
type HandlerResult o = IO (Bool, GenericResult GanetiException o)
276 d79a6502 Petr Pudlak
277 d79a6502 Petr Pudlak
data Handler i o = Handler
278 d79a6502 Petr Pudlak
  { hParse         :: J.JSValue -> J.JSValue -> Result i
279 d79a6502 Petr Pudlak
    -- ^ parses method and its arguments into the input type
280 d79a6502 Petr Pudlak
  , hInputLogShort :: i -> String
281 d79a6502 Petr Pudlak
    -- ^ short description of an input, for the INFO logging level
282 d79a6502 Petr Pudlak
  , hInputLogLong  :: i -> String
283 d79a6502 Petr Pudlak
    -- ^ long description of an input, for the DEBUG logging level
284 d79a6502 Petr Pudlak
  , hExec          :: i -> HandlerResult o
285 d79a6502 Petr Pudlak
    -- ^ executes the handler on an input
286 d79a6502 Petr Pudlak
  }
287 c7003a76 Petr Pudlak
288 c7003a76 Petr Pudlak
289 c7003a76 Petr Pudlak
handleJsonMessage
290 c7003a76 Petr Pudlak
    :: (J.JSON o)
291 c7003a76 Petr Pudlak
    => Handler i o              -- ^ handler
292 c7003a76 Petr Pudlak
    -> i                        -- ^ parsed input
293 c7003a76 Petr Pudlak
    -> HandlerResult J.JSValue
294 c7003a76 Petr Pudlak
handleJsonMessage handler req = do
295 c7003a76 Petr Pudlak
  (close, call_result) <- hExec handler req
296 c7003a76 Petr Pudlak
  return (close, fmap J.showJSON call_result)
297 c7003a76 Petr Pudlak
298 c7003a76 Petr Pudlak
-- | Takes a request as a 'String', parses it, passes it to a handler and
299 c7003a76 Petr Pudlak
-- formats its response.
300 c7003a76 Petr Pudlak
handleRawMessage
301 c7003a76 Petr Pudlak
    :: (J.JSON o)
302 c7003a76 Petr Pudlak
    => Handler i o              -- ^ handler
303 c7003a76 Petr Pudlak
    -> String                   -- ^ raw unparsed input
304 c7003a76 Petr Pudlak
    -> IO (Bool, String)
305 c7003a76 Petr Pudlak
handleRawMessage handler payload =
306 c7003a76 Petr Pudlak
  case parseCall payload >>= uncurry (hParse handler) of
307 c7003a76 Petr Pudlak
    Bad err -> do
308 c7003a76 Petr Pudlak
         let errmsg = "Failed to parse request: " ++ err
309 c7003a76 Petr Pudlak
         logWarning errmsg
310 c7003a76 Petr Pudlak
         return (False, buildResponse False (J.showJSON errmsg))
311 c7003a76 Petr Pudlak
    Ok req -> do
312 c7003a76 Petr Pudlak
        logDebug $ "Request: " ++ hInputLogLong handler req
313 c7003a76 Petr Pudlak
        (close, call_result_json) <- handleJsonMessage handler req
314 c7003a76 Petr Pudlak
        logMsg handler req call_result_json
315 c7003a76 Petr Pudlak
        let (status, response) = prepareMsg call_result_json
316 c7003a76 Petr Pudlak
        return (close, buildResponse status response)
317 c7003a76 Petr Pudlak
318 c7003a76 Petr Pudlak
-- | Reads a request, passes it to a handler and sends a response back to the
319 c7003a76 Petr Pudlak
-- client.
320 c7003a76 Petr Pudlak
handleClient
321 c7003a76 Petr Pudlak
    :: (J.JSON o)
322 c7003a76 Petr Pudlak
    => Handler i o
323 c7003a76 Petr Pudlak
    -> Client
324 c7003a76 Petr Pudlak
    -> IO Bool
325 c7003a76 Petr Pudlak
handleClient handler client = do
326 c7003a76 Petr Pudlak
  msg <- recvMsgExt client
327 c7003a76 Petr Pudlak
  logDebug $ "Received message: " ++ show msg
328 c7003a76 Petr Pudlak
  case msg of
329 c7003a76 Petr Pudlak
    RecvConnClosed -> logDebug "Connection closed" >>
330 c7003a76 Petr Pudlak
                      return False
331 c7003a76 Petr Pudlak
    RecvError err -> logWarning ("Error during message receiving: " ++ err) >>
332 c7003a76 Petr Pudlak
                     return False
333 c7003a76 Petr Pudlak
    RecvOk payload -> do
334 c7003a76 Petr Pudlak
      (close, outMsg) <- handleRawMessage handler payload
335 c7003a76 Petr Pudlak
      sendMsg client outMsg
336 c7003a76 Petr Pudlak
      return close
337 c7003a76 Petr Pudlak
338 c7003a76 Petr Pudlak
-- | Main client loop: runs one loop of 'handleClient', and if that
339 c7003a76 Petr Pudlak
-- doesn't report a finished (closed) connection, restarts itself.
340 c7003a76 Petr Pudlak
clientLoop
341 c7003a76 Petr Pudlak
    :: (J.JSON o)
342 c7003a76 Petr Pudlak
    => Handler i o
343 c7003a76 Petr Pudlak
    -> Client
344 c7003a76 Petr Pudlak
    -> IO ()
345 c7003a76 Petr Pudlak
clientLoop handler client = do
346 c7003a76 Petr Pudlak
  result <- handleClient handler client
347 c7003a76 Petr Pudlak
  if result
348 c7003a76 Petr Pudlak
    then clientLoop handler client
349 c7003a76 Petr Pudlak
    else closeClient client
350 c7003a76 Petr Pudlak
351 c7003a76 Petr Pudlak
-- | Main listener loop: accepts clients, forks an I/O thread to handle
352 c7003a76 Petr Pudlak
-- that client.
353 c7003a76 Petr Pudlak
listener
354 c7003a76 Petr Pudlak
    :: (J.JSON o)
355 c7003a76 Petr Pudlak
    => Handler i o
356 c7003a76 Petr Pudlak
    -> Server
357 c7003a76 Petr Pudlak
    -> IO ()
358 c7003a76 Petr Pudlak
listener handler server = do
359 c7003a76 Petr Pudlak
  client <- acceptClient server
360 c7003a76 Petr Pudlak
  _ <- forkIO $ clientLoop handler client
361 c7003a76 Petr Pudlak
  return ()