`Expect'-like lazy reading/Parsec matching on TCP sockets

Hello all, I'm writing an application to interact with other applications remotely though TCP sockets (primarily), and in the future with local apps also. So far I have a Parsec parser to match the input that I want to see, which works if the remote connection is particularly speedy - but the final goal is to obtain the following behavior: Match the Parsec parser against the input as soon as a match is available, but fail if the match is unavailable after a timeout value if no further data is available on the socket. The parser will be matching some input pattern such as (string "login:"), for example. What's the best approach to take while leveraging laziness if possible? Thanks, - Scott Bell

Hello Scott, Wednesday, April 4, 2007, 1:54:27 AM, you wrote:
Match the Parsec parser against the input as soon as a match is available, but fail if the match is unavailable after a timeout value if no further data is available on the socket.
one possible solution: use Streams library and establish a stream transformer that adds an error call on timeout. something like this: data StreamWithTimeout s = StreamWithTimeout s Timeout instance Stream s => Stream (StreamWithTimeout s) where vGetChar (StreamWithTimeout s t) = do timeout t (vGetChar s) (error "Timed out!") then you can use standard vGetContents lazy string reading in order to get expected behaviour or, even simple, you can make your own variant of hGetContents which adds a timeout checks before each next call to hGetChar or hGetBuf -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Bulat,
one possible solution: use Streams library and establish a stream transformer that adds an error call on timeout. something like this:
data StreamWithTimeout s = StreamWithTimeout s Timeout
instance Stream s => Stream (StreamWithTimeout s) where vGetChar (StreamWithTimeout s t) = do timeout t (vGetChar s) (error "Timed out!")
If possible, I would like to try and use lazy [Char]s -- this would greatly simplify my usage of the Parsec parser.
or, even simple, you can make your own variant of hGetContents which adds a timeout checks before each next call to hGetChar or hGetBuf
Can this be as simple as applying the parser against a string returned by the (modified) hGetContents, which will read all that is possible given a certain time constraint? Thanks, - Scott

Hello Scott, Wednesday, April 4, 2007, 6:39:22 PM, you wrote:
vGetChar (StreamWithTimeout s t) = do timeout t (vGetChar s) (error "Timed out!")
If possible, I would like to try and use lazy [Char]s -- this would greatly simplify my usage of the Parsec parser.
or, even simple, you can make your own variant of hGetContents which adds a timeout checks before each next call to hGetChar or hGetBuf
Can this be as simple as applying the parser against a string returned by the (modified) hGetContents, which will read all that is possible given a certain time constraint?
yes, with both variants. actually, second one should be easier to implement and understand. you should look into unsafeInterleaveIO section of http://haskell.org/haskellwiki/IO_inside feel free to ask me if you need more help -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Bulat,
yes, with both variants. actually, second one should be easier to implement and understand. you should look into unsafeInterleaveIO section of http://haskell.org/haskellwiki/IO_inside
This seems to do what I want, and unless I'm overlooking something it feels very straight-forward: hGetContentsTimeout :: Handle -> Int -> IO String hGetContentsTimeout h t = do hSetBuffering stdin NoBuffering ready <- hWaitForInput h t if (not ready) then return [] else do c <- hGetChar h s <- unsafeInterleaveIO (hGetContentsTimeout h t) return (c:s) This is not extensivly tested, but applying my parser to the string returned by hGetContentsTimeout behaves precisely as I wanted: It returns a match as soon as it is available, and fails if it is not seen within t ms. Thanks for your help! - Scott
participants (2)
-
Bulat Ziganshin
-
Scott Bell