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 () |