19
19
----------------------------------------------------------------------------
20
20
module DAP.Server
21
21
( runDAPServer
22
+ , readPayload
22
23
) where
23
24
----------------------------------------------------------------------------
24
25
import Control.Concurrent.MVar ( MVar )
@@ -34,7 +35,7 @@ import Control.Exception ( SomeException
34
35
import Control.Monad ( forever , void )
35
36
import Control.Monad.State ( evalStateT , runStateT , execStateT )
36
37
import DAP.Internal ( withGlobalLock )
37
- import Data.Aeson ( decodeStrict , eitherDecode , Value )
38
+ import Data.Aeson ( decodeStrict , eitherDecode , Value , FromJSON )
38
39
import Data.Aeson.Encode.Pretty ( encodePretty )
39
40
import Data.ByteString ( ByteString )
40
41
import Data.Char ( isDigit )
@@ -60,15 +61,17 @@ runDAPServer
60
61
-- ^ A function to facilitate communication between DAP clients, debug adaptors and debuggers
61
62
-> IO ()
62
63
runDAPServer serverConfig@ ServerConfig {.. } communicate = withSocketsDo $ do
63
- putStrLn (" Running DAP server on " <> show port <> " ..." )
64
+ when debugLogging $ putStrLn (" Running DAP server on " <> show port <> " ..." )
64
65
appStore <- newTVarIO mempty
65
66
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
67
70
handle <- socketToHandle socket ReadWriteMode
68
- hSetNewlineMode handle NewlineMode { inputNL = CRLF , outputNL = CRLF }
71
+ hSetNewlineMode handle NewlineMode { inputNL = CRLF , outputNL = CRLF }
69
72
request <- getRequest handle address serverConfig
70
73
adaptorState <- initAdaptorState handle address appStore serverConfig request
71
- serviceClient communicate adaptorState `catch` exceptionHandler handle address
74
+ serviceClient communicate adaptorState `catch` exceptionHandler handle address debugLogging
72
75
73
76
-- | Initializes the Adaptor
74
77
--
@@ -81,7 +84,6 @@ initAdaptorState
81
84
-> IO (AdaptorState app )
82
85
initAdaptorState handle address appStore serverConfig request = do
83
86
handleLock <- newMVar ()
84
- seqRef <- pure 0
85
87
variablesMap <- pure mempty
86
88
sourceReferencesMap <- pure mempty
87
89
sessionId <- pure Nothing
@@ -95,17 +97,6 @@ initAdaptorState handle address appStore serverConfig request = do
95
97
, ..
96
98
}
97
99
----------------------------------------------------------------------------
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
- ----------------------------------------------------------------------------
109
100
-- | Communication loop between editor and adaptor
110
101
-- Evaluates the current 'Request' located in the 'AdaptorState'
111
102
-- Fetches, updates and recurses on the next 'Request'
@@ -117,7 +108,7 @@ serviceClient
117
108
serviceClient communicate adaptorState@ AdaptorState { address, handle, serverConfig, request } = do
118
109
nextState <- runAdaptor adaptorState $ communicate (command request)
119
110
nextRequest <- getRequest handle address serverConfig
120
- serviceClient communicate (updateAdaptorState nextState nextRequest)
111
+ serviceClient communicate nextState { request = nextRequest }
121
112
where
122
113
----------------------------------------------------------------------------
123
114
-- | Utility for evaluating a monad transformer stack
@@ -130,23 +121,24 @@ serviceClient communicate adaptorState@AdaptorState { address, handle, serverCon
130
121
131
122
----------------------------------------------------------------------------
132
123
-- | 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
135
126
let
136
127
dumpError
137
128
| Just (ParseException msg) <- fromException e
138
129
= logger ERROR address Nothing
139
130
$ withBraces
140
131
$ BL8. pack (" Parse Exception encountered: " <> msg)
141
132
| 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 "
144
135
| otherwise
145
136
= logger ERROR address Nothing
146
137
$ withBraces
147
138
$ 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" )
150
142
hClose handle
151
143
----------------------------------------------------------------------------
152
144
-- | Internal function for parsing a 'ProtocolMessage' header
@@ -174,14 +166,26 @@ getRequest handle addr ServerConfig {..} = do
174
166
throwIO (ParseException couldn'tDecodeBody)
175
167
Right request ->
176
168
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