Skip to content

Commit 9be07ef

Browse files
committed
Support for receiving reverse request responses
Extends the entrypoint runDAPWithLogger function to accept a function which is called on `ReverseRequestResponse`s, a response to a reverse request. This allows the DAP server to e.g. capture the PID of the process invoked via `runInTerminal`.
1 parent 9457d40 commit 9be07ef

3 files changed

Lines changed: 77 additions & 12 deletions

File tree

src/DAP/Adaptor.hs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module DAP.Adaptor
3535
-- * Request Arguments
3636
, getArguments
3737
, getRequestSeqNum
38+
, getReverseRequestResponseBody
3839
-- * Debug Session
3940
, registerNewDebugSession
4041
, updateDebugSession
@@ -443,6 +444,25 @@ getArguments = do
443444
logError (T.pack reason)
444445
liftIO $ throwIO (ParseException reason)
445446
----------------------------------------------------------------------------
447+
-- | Attempt to parse arguments from a ReverseRequestResponse (not in env)
448+
----------------------------------------------------------------------------
449+
getReverseRequestResponseBody
450+
:: (Show value, FromJSON value)
451+
=> ReverseRequestResponse -> Adaptor app r value
452+
getReverseRequestResponseBody resp = do
453+
let maybeArgs = body resp
454+
let msg = "No args found for this message"
455+
case maybeArgs of
456+
Nothing -> do
457+
logError msg
458+
liftIO $ throwIO (ExpectedArguments msg)
459+
Just val ->
460+
case fromJSON val of
461+
Success r -> pure r
462+
Error reason -> do
463+
logError (T.pack reason)
464+
liftIO $ throwIO (ParseException reason)
465+
----------------------------------------------------------------------------
446466
-- | Evaluates Adaptor action by using and updating the state in the MVar
447467
runAdaptorWith :: AdaptorLocal app request -> AdaptorState -> Adaptor app request () -> IO ()
448468
runAdaptorWith lcl st (Adaptor action) = do

src/DAP/Server.hs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,24 @@ stdoutLogger = do
7676
data TerminateServer = TerminateServer
7777
deriving (Show, Exception)
7878

79+
-- | Simpler version of 'runDAPServerWithLogger'.
80+
--
81+
-- If you don't need a custom logger or to observe reverse request responses.
7982
runDAPServer :: ServerConfig -> (Command -> Adaptor app Request ()) -> IO ()
8083
runDAPServer config communicate = do
8184
l <- stdoutLogger
82-
runDAPServerWithLogger (cmap renderDAPLog l) config communicate
85+
runDAPServerWithLogger (cmap renderDAPLog l) config communicate (const (pure ()))
8386

8487
runDAPServerWithLogger
8588
:: LogAction IO DAPLog
8689
-> ServerConfig
8790
-- ^ Top-level Server configuration, global across all debug sessions
8891
-> (Command -> Adaptor app Request ())
8992
-- ^ A function to facilitate communication between DAP clients, debug adaptors and debuggers
93+
-> (ReverseRequestResponse -> Adaptor app () ())
94+
-- ^ A function to receive reverse-request-responses from DAP clients
9095
-> IO ()
91-
runDAPServerWithLogger rawLogAction serverConfig@ServerConfig {..} communicate = withSocketsDo $ do
96+
runDAPServerWithLogger rawLogAction serverConfig@ServerConfig {..} communicate ackResp = withSocketsDo $ do
9297
let logAction = cfilter (\msg -> if debugLogging then True else severity msg /= DEBUG) rawLogAction
9398
logAction <& (mkDebugMessage $ (T.pack ("Running DAP server on " <> show port <> "...")))
9499
appStore <- newTVarIO mempty
@@ -99,7 +104,7 @@ runDAPServerWithLogger rawLogAction serverConfig@ServerConfig {..} communicate =
99104
handle <- socketToHandle socket ReadWriteMode
100105
hSetNewlineMode handle NewlineMode { inputNL = CRLF, outputNL = CRLF }
101106
adaptorStateMVar <- initAdaptorState logAction handle address appStore serverConfig
102-
serviceClient communicate adaptorStateMVar
107+
serviceClient communicate ackResp adaptorStateMVar
103108
`catch` exceptionHandler logAction handle address debugLogging mainThread
104109
server `catch` \(SomeAsyncException e) ->
105110
case fromException $ toException e of
@@ -127,13 +132,20 @@ initAdaptorState logAction handle address appStore serverConfig = do
127132
-- Evaluates the current 'Request' located in the 'AdaptorState'
128133
-- Fetches, updates and recurses on the next 'Request'
129134
--
135+
-- Similarly, if the client responded to a reverse request then we execute the
136+
-- acknowledge action (which, notably, is not an @'Adaptor' _ 'Request'@
137+
-- because there's no 'Request' to reply to)
130138
serviceClient
131139
:: (Command -> Adaptor app Request ())
140+
-> (ReverseRequestResponse -> Adaptor app r ())
132141
-> AdaptorLocal app r
133142
-> IO ()
134-
serviceClient communicate lcl = forever $ runAdaptorWith lcl st $ do
135-
nextRequest <- getRequest
136-
withRequest nextRequest (communicate (command nextRequest))
143+
serviceClient communicate ackResp lcl = forever $ runAdaptorWith lcl st $ do
144+
either_nextRequest <- getRequest
145+
case either_nextRequest of
146+
Right nextRequest ->
147+
withRequest nextRequest (communicate (command nextRequest))
148+
Left rrr -> ackResp rrr
137149
where
138150
st = AdaptorState MessageTypeResponse []
139151
----------------------------------------------------------------------------
@@ -172,7 +184,7 @@ exceptionHandler logAction handle address shouldLog serverThread (e :: SomeExcep
172184
-- 'parseHeader' Attempts to parse 'Content-Length: <byte-count>'
173185
-- Helper function for parsing message headers
174186
-- e.g. ("Content-Length: 11\r\n")
175-
getRequest :: Adaptor app r Request
187+
getRequest :: Adaptor app r (Either ReverseRequestResponse Request)
176188
getRequest = do
177189
handle <- getHandle
178190
header <- liftIO $ getHeaderHandle handle
@@ -186,10 +198,15 @@ getRequest = do
186198
("\n" <> encodePretty (decodeStrict body :: Maybe Value))
187199
case eitherDecode (BL8.fromStrict body) of
188200
Left couldn'tDecodeBody -> do
189-
logError (T.pack couldn'tDecodeBody)
190-
liftIO $ throwIO (ParseException couldn'tDecodeBody)
201+
-- As a fallback, try to parse a reverse request response
202+
case eitherDecode (BL8.fromStrict body) of
203+
Right rrr -> pure (Left rrr)
204+
Left _ -> do
205+
-- No luck, report fail to parse command:
206+
logError (T.pack couldn'tDecodeBody)
207+
liftIO $ throwIO (ParseException couldn'tDecodeBody)
191208
Right request ->
192-
pure request
209+
pure (Right request)
193210

194211
getHeaderHandle :: Handle -> IO (Either String PayloadSize)
195212
getHeaderHandle handle = do

src/DAP/Types.hs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,14 @@ module DAP.Types
101101
, AdaptorState (..)
102102
, AdaptorLocal(..)
103103
, AppStore
104-
, MonadIO(..)
104+
, MonadIO
105105
-- * Errors
106106
, AdaptorException (..)
107107
, ErrorMessage (..)
108108
, ErrorResponse (..)
109109
-- * Request
110110
, Request (..)
111+
, ReverseRequestResponse (..)
111112
-- * Misc.
112113
, PayloadSize
113114
, Seq
@@ -223,7 +224,7 @@ import Data.Aeson ( (.:), (.:?), withObject, with
223224
, FromJSON(parseJSON), Value, KeyValue((.=))
224225
, ToJSON(toJSON), genericParseJSON, defaultOptions
225226
)
226-
import Data.Aeson.Types ( Pair, typeMismatch )
227+
import Data.Aeson.Types ( Pair, typeMismatch, Parser )
227228
import Data.Proxy ( Proxy(Proxy) )
228229
import Data.String ( IsString(..) )
229230
import Data.Time ( UTCTime )
@@ -365,11 +366,36 @@ data Request
365366
----------------------------------------------------------------------------
366367
instance FromJSON Request where
367368
parseJSON = withObject "Request" $ \o -> do
369+
"request" <- (o .: "type") :: Parser String
368370
Request
369371
<$> o .:? "arguments"
370372
<*> o .: "seq"
371373
<*> o .: "command"
372374
----------------------------------------------------------------------------
375+
data ReverseRequestResponse
376+
= ReverseRequestResponse
377+
{ body :: Maybe Value
378+
-- ^ Request arguments
379+
--
380+
, reverseRequestResponseSeqNum :: Seq
381+
-- ^ Request sequence number
382+
--
383+
, reverseRequestCommand :: ReverseCommand
384+
-- ^ Command of Request
385+
--
386+
, success :: Bool
387+
-- ^ Whether the reverse request was successful
388+
} deriving stock (Show)
389+
----------------------------------------------------------------------------
390+
instance FromJSON ReverseRequestResponse where
391+
parseJSON = withObject "ReverseRequestResponse" $ \o -> do
392+
"response" <- (o .: "type") :: Parser String
393+
ReverseRequestResponse
394+
<$> o .:? "body"
395+
<*> o .: "seq"
396+
<*> o .: "command"
397+
<*> o .: "success"
398+
----------------------------------------------------------------------------
373399
data Breakpoint
374400
= Breakpoint
375401
{ breakpointId :: Maybe Int
@@ -1115,6 +1141,8 @@ data RunInTerminalResponse
11151141
----------------------------------------------------------------------------
11161142
instance ToJSON RunInTerminalResponse where
11171143
toJSON = genericToJSONWithModifier
1144+
instance FromJSON RunInTerminalResponse where
1145+
parseJSON = genericParseJSONWithModifier
11181146
----------------------------------------------------------------------------
11191147
data ModulesResponse
11201148
= ModulesResponse

0 commit comments

Comments
 (0)