1919----------------------------------------------------------------------------
2020module DAP.Server
2121 ( runDAPServer
22+ , readPayload
2223 ) where
2324----------------------------------------------------------------------------
2425import Control.Concurrent.MVar ( MVar )
@@ -34,7 +35,7 @@ import Control.Exception ( SomeException
3435import Control.Monad ( forever , void )
3536import Control.Monad.State ( evalStateT , runStateT , execStateT )
3637import DAP.Internal ( withGlobalLock )
37- import Data.Aeson ( decodeStrict , eitherDecode , Value )
38+ import Data.Aeson ( decodeStrict , eitherDecode , Value , FromJSON )
3839import Data.Aeson.Encode.Pretty ( encodePretty )
3940import Data.ByteString ( ByteString )
4041import Data.Char ( isDigit )
@@ -60,15 +61,17 @@ runDAPServer
6061 -- ^ A function to facilitate communication between DAP clients, debug adaptors and debuggers
6162 -> IO ()
6263runDAPServer serverConfig@ ServerConfig {.. } communicate = withSocketsDo $ do
63- putStrLn (" Running DAP server on " <> show port <> " ..." )
64+ when debugLogging $ putStrLn (" Running DAP server on " <> show port <> " ..." )
6465 appStore <- newTVarIO mempty
6566 serve (Host host) (show port) $ \ (socket, address) -> do
66- withGlobalLock (putStrLn $ " TCP connection established from " ++ show address)
67+ when debugLogging $ do
68+ withGlobalLock $ do
69+ putStrLn $ " TCP connection established from " ++ show address
6770 handle <- socketToHandle socket ReadWriteMode
68- hSetNewlineMode handle NewlineMode { inputNL = CRLF , outputNL = CRLF }
71+ hSetNewlineMode handle NewlineMode { inputNL = CRLF , outputNL = CRLF }
6972 request <- getRequest handle address serverConfig
7073 adaptorState <- initAdaptorState handle address appStore serverConfig request
71- serviceClient communicate adaptorState `catch` exceptionHandler handle address
74+ serviceClient communicate adaptorState `catch` exceptionHandler handle address debugLogging
7275
7376-- | Initializes the Adaptor
7477--
@@ -81,7 +84,6 @@ initAdaptorState
8184 -> IO (AdaptorState app )
8285initAdaptorState handle address appStore serverConfig request = do
8386 handleLock <- newMVar ()
84- seqRef <- pure 0
8587 variablesMap <- pure mempty
8688 sourceReferencesMap <- pure mempty
8789 sessionId <- pure Nothing
@@ -95,17 +97,6 @@ initAdaptorState handle address appStore serverConfig request = do
9597 , ..
9698 }
9799----------------------------------------------------------------------------
98- -- | Updates sequence number, puts the new request into the AdaptorState
99- --
100- updateAdaptorState
101- :: AdaptorState app
102- -> Request
103- -> AdaptorState app
104- updateAdaptorState state request = do
105- state { request = request
106- , seqRef = requestSeqNum request
107- }
108- ----------------------------------------------------------------------------
109100-- | Communication loop between editor and adaptor
110101-- Evaluates the current 'Request' located in the 'AdaptorState'
111102-- Fetches, updates and recurses on the next 'Request'
@@ -117,7 +108,7 @@ serviceClient
117108serviceClient communicate adaptorState@ AdaptorState { address, handle, serverConfig, request } = do
118109 nextState <- runAdaptor adaptorState $ communicate (command request)
119110 nextRequest <- getRequest handle address serverConfig
120- serviceClient communicate (updateAdaptorState nextState nextRequest)
111+ serviceClient communicate nextState { request = nextRequest }
121112 where
122113 ----------------------------------------------------------------------------
123114 -- | Utility for evaluating a monad transformer stack
@@ -130,23 +121,24 @@ serviceClient communicate adaptorState@AdaptorState { address, handle, serverCon
130121
131122----------------------------------------------------------------------------
132123-- | Handle exceptions from client threads, parse and log accordingly
133- exceptionHandler :: Handle -> SockAddr -> SomeException -> IO ()
134- exceptionHandler handle address (e :: SomeException ) = do
124+ exceptionHandler :: Handle -> SockAddr -> Bool -> SomeException -> IO ()
125+ exceptionHandler handle address shouldLog (e :: SomeException ) = do
135126 let
136127 dumpError
137128 | Just (ParseException msg) <- fromException e
138129 = logger ERROR address Nothing
139130 $ withBraces
140131 $ BL8. pack (" Parse Exception encountered: " <> msg)
141132 | Just (err :: IOException ) <- fromException e, isEOFError err
142- = logger ERROR address Nothing
143- $ withBraces " Empty payload received "
133+ = logger INFO address ( Just SENT )
134+ $ withBraces " Client has ended its connection "
144135 | otherwise
145136 = logger ERROR address Nothing
146137 $ withBraces
147138 $ BL8. pack (" Unknown Exception: " <> show e)
148- dumpError
149- logger ERROR address Nothing (withBraces " Closing Connection" )
139+ when shouldLog $ do
140+ dumpError
141+ logger INFO address (Just SENT ) (withBraces " Closing Connection" )
150142 hClose handle
151143----------------------------------------------------------------------------
152144-- | Internal function for parsing a 'ProtocolMessage' header
@@ -174,14 +166,26 @@ getRequest handle addr ServerConfig {..} = do
174166 throwIO (ParseException couldn'tDecodeBody)
175167 Right request ->
176168 pure request
177- where
178- ----------------------------------------------------------------------------
179- -- | Parses the HeaderPart of all ProtocolMessages
180- parseHeader :: ByteString -> IO (Either String PayloadSize )
181- parseHeader bytes = do
182- let byteSize = BS. takeWhile isDigit (BS. drop (BS. length " Content-Length: " ) bytes)
183- case readMaybe (BS. unpack byteSize) of
184- Just contentLength ->
185- pure (Right contentLength)
186- Nothing ->
187- pure $ Left (" Invalid payload: " <> BS. unpack bytes)
169+ ----------------------------------------------------------------------------
170+ -- | Parses the HeaderPart of all ProtocolMessages
171+ parseHeader :: ByteString -> IO (Either String PayloadSize )
172+ parseHeader bytes = do
173+ let byteSize = BS. takeWhile isDigit (BS. drop (BS. length " Content-Length: " ) bytes)
174+ case readMaybe (BS. unpack byteSize) of
175+ Just contentLength ->
176+ pure (Right contentLength)
177+ Nothing ->
178+ pure $ Left (" Invalid payload: " <> BS. unpack bytes)
179+
180+ -- | Helper function to parse a 'ProtocolMessage', extracting it's body.
181+ -- used for testing.
182+ --
183+ readPayload :: FromJSON json => Handle -> IO (Either String json )
184+ readPayload handle = do
185+ headerBytes <- BS. hGetLine handle
186+ void (BS. hGetLine handle)
187+ parseHeader headerBytes >>= \ case
188+ Left e -> pure (Left e)
189+ Right count -> do
190+ body <- BS. hGet handle count
191+ pure $ eitherDecode (BL8. fromStrict body)
0 commit comments