forked from nominolo/scion
/
Emacs.hs
136 lines (122 loc) · 4.26 KB
/
Emacs.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
{-# LANGUAGE BangPatterns, DeriveDataTypeable, ScopedTypeVariables,
TypeFamilies, PatternGuards #-}
-- |
-- Module : Scion.Server.Emacs
-- Copyright : (c) Thomas Schilling 2008
-- License : BSD-style
--
-- Maintainer : nominolo@gmail.com
-- Stability : experimental
-- Portability : portable
--
-- An example server that communicates with Emacs.
--
module Scion.Server.Emacs where
import Scion.Types
import Scion.Server.Protocol
import Scion.Server.Commands
import Exception
import MonadUtils
import GHC
import Control.Exception
import Control.Monad ( liftM, when )
import Data.Bits ( shiftL )
import Data.Char ( isHexDigit, digitToInt )
import Data.Data ( Typeable )
import Network ( listenOn, PortID(..) )
import Network.Socket hiding (send, sendTo, recv, recvFrom)
import Network.Socket.ByteString
import Numeric ( showHex )
import Prelude hiding ( log )
import System.IO.Error (catch, isEOFError)
import System.IO ( hSetBuffering, stdout, stderr, BufferMode(..) )
import Text.ParserCombinators.ReadP
import qualified Data.ByteString.Char8 as S
------------------------------------------------------------------------------
data SocketClosed = SocketClosed deriving (Show, Typeable)
instance Exception SocketClosed
logLevel :: Int
logLevel = 2
runServer :: ScionM ()
runServer =
reifyScionM $ \s ->
withSocketsDo $ do
liftIO $ do hSetBuffering stdout LineBuffering
hSetBuffering stderr LineBuffering
log 1 "starting up server..."
sock <- liftIO $ listenOn (PortNumber 4005)
reflectScionM (loop sock) s
where
loop sock = do
log 4 "accepting"
liftIO $ putStrLn "=== Listening on port: 4005"
(sock', _addr) <- liftIO $ accept sock
log 4 "starting to serve"
more <- eventLoop sock'
log 4 "done serving"
liftIO $ sClose sock'
log 4 "socket closed"
if more then loop sock
else return ()
eventLoop :: Socket -> ScionM Bool
eventLoop sock =
ghandle (\(_e :: SocketClosed) -> return True) $ do
(r, s) <- getRequest sock
case r of
Nothing -> do
log 1 "Could not parse request."
sendResponse (RReaderError s "no parse")
eventLoop sock
Just req
| RQuit <- req -> return False
| otherwise -> do
resp <- handleRequest req
log 4 (show resp)
dflags <- getSessionDynFlags
when (verbosity dflags > 3) $
log 3 ("*************************************************")
sendResponse resp
eventLoop sock
where
sendResponse r = do
let payload = S.pack (showResponse r)
let hdr = mkHeader (S.length payload)
liftIO $ do
send sock (S.pack hdr)
send sock payload
return ()
myrecv :: MonadIO m => Socket -> Int -> m S.ByteString
myrecv _sock 0 = return S.empty
myrecv sock len =
let handler e | isEOFError e = return S.empty
| otherwise = ioError e
in liftIO $ System.IO.Error.catch (recv sock len) handler
-- | A message is a sequence of bytes, prefixed by the message length encoded
-- as a 6 character hexadecimal number.
getRequest :: MonadIO m => Socket -> m (Maybe Request, String)
getRequest sock = do
len_as_hex <- liftM S.unpack (myrecv sock 6)
len <- case len_as_hex of
[_,_,_,_,_,_] ->
case readP_to_S parseHex len_as_hex of
[(n, "")] -> return n
_ ->
error "Could not parse message header."
_ -> liftIO $ throwIO SocketClosed
payload <- myrecv sock len
log 4 (show (len_as_hex, payload))
let s = (S.unpack payload)
return $ (parseRequest allCommands s, s)
parseHex :: ReadP Int
parseHex = munch1 isHexDigit >>= return . go 0
where
go !r [] = r
go !r (c:cs) = go (r `shiftL` 4 + digitToInt c) cs
handleRequest :: Request -> ScionM Response
handleRequest (Rex r i) = do answer <- r
return (RReturn answer i)
handleRequest r = error $ "Unimplemented request type: " ++ show r
mkHeader :: Int -> String
mkHeader len = reverse . take 6 $ reverse (showHex len "") ++ repeat '0'
log :: MonadIO m => Int -> String -> m ()
log lvl s = when (lvl <= logLevel) $ liftIO $ putStrLn s