
Hello, I'm trying to use haskell to put together a TCP proxy I can put between my browser and my webserver. This is as far as I got. The webserver isn't returning my request:
listen = withSocketsDo $ do putStrLn "Listening..." socket <- listenOn $ PortNumber 8082 (handleToClient, hostName, portNumber) <- accept socket putStrLn "Connected to client" contents <- hGetContents handleToClient putStrLn "Received from client:" putStrLn contents handleToServer <- connectTo "127.0.0.1" (PortNumber 8080) putStrLn "Connected to server" hPutStrLn handleToServer contents putStrLn "Waiting for server to respond" hPutStrLn handleToServer "\r\n\r\n" putStrLn "Waiting for server to respond" result <- hGetContents handleToServer putStrLn result hClose handleToServer hClose handleToClient sClose socket
Am I doing something wrong? Thanks, -John

Actually, it blocks on:
putStrLn contents
It even blocks if I replace it with:
print $ length contents
Is there some kind of magic happening here?
-John
On 9/20/06, John Ky
Hello,
I'm trying to use haskell to put together a TCP proxy I can put between my browser and my webserver.
This is as far as I got. The webserver isn't returning my request:
listen = withSocketsDo $ do putStrLn "Listening..." socket <- listenOn $ PortNumber 8082 (handleToClient, hostName, portNumber) <- accept socket putStrLn "Connected to client" contents <- hGetContents handleToClient putStrLn "Received from client:" putStrLn contents handleToServer <- connectTo "127.0.0.1" (PortNumber 8080) putStrLn "Connected to server" hPutStrLn handleToServer contents putStrLn "Waiting for server to respond" hPutStrLn handleToServer "\r\n\r\n" putStrLn "Waiting for server to respond" result <- hGetContents handleToServer putStrLn result hClose handleToServer hClose handleToClient sClose socket
Am I doing something wrong?
Thanks,
-John

On Wed, 20 Sep 2006, John Ky wrote:
Actually, it blocks on:
putStrLn contents
It even blocks if I replace it with:
print $ length contents
Is there some kind of magic happening here?
No, but you're trying to do magic - it can't get all of contents until the connection's dropped. -- flippa@flippac.org A problem that's all in your head is still a problem. Brain damage is but one form of mind damage.

Hi again,
Given that putStrLn contents did manage to print out the HTTP header
before blocking, am I correct in coming to the conlusion that
'contents' is evaluated lazily? So Monads don't actually eliminate
laziness?
-John
On 9/20/06, Philippa Cowderoy
On Wed, 20 Sep 2006, John Ky wrote:
Actually, it blocks on:
putStrLn contents
It even blocks if I replace it with:
print $ length contents
Is there some kind of magic happening here?
No, but you're trying to do magic - it can't get all of contents until the connection's dropped.
-- flippa@flippac.org
A problem that's all in your head is still a problem. Brain damage is but one form of mind damage.

On Thu, Sep 21, 2006 at 12:26:03AM +1000, John Ky wrote:
Given that putStrLn contents did manage to print out the HTTP header before blocking, am I correct in coming to the conlusion that 'contents' is evaluated lazily?
hGetContents breaks the rules of the IO monad - it returns a value (the contents list) that is not entirely pure - evaluation of this value causes execution of IO actions, namely reading from the handle. This way of operation is not a rule, but an exception. BTW, try to implement hGetContents in pure Haskell 98, using only the functions specified there, but excluding (hGetContents, getContents, readFile and interact). As a test try this code on a big or infinite input, like /dev/zero: import IO main = hGetContents stdin >>= print . take 100 hGetContents may cause some confusion, but it also is very handy. It lets you treat file contents as a lazy list, which allows you to use very high-level code to process the data without having to load the whole file into memory. Best regards Tomasz

On 20/09/06, John Ky
So Monads don't actually eliminate laziness?
Monads and laziness are completely orthagonal issues; they have nothing to do with one another. I think you're getting confused with purity. Monads can be used to encapsulate effects, to allow you to write code as if the code had side effects, while your program remains technically pure. -- -David House, dmhouse@gmail.com

Hello John, Wednesday, September 20, 2006, 3:59:36 PM, you wrote:
I'm trying to use haskell to put together a TCP proxy I can put between my browser and my webserver.
This is as far as I got. The webserver isn't returning my request:
hSetBuffering handleToServer LineBuffering may help -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Hi Bulat, Thanks. Yes it helps with an earlier implementation I wrote (below). But surely there must be a better way to write this. My code is way to verbose. -John ---
doProxyServer handleToClient handleToServer = do eof <- hIsEOF handleToServer if not eof then do ready <- hReady handleToServer if ready then do text <- hGetChar handleToServer putChar text hPutChar handleToClient text doProxyServer handleToClient handleToServer else do hPutStr handleToClient "\n" doProxyClient handleToClient handleToServer else do hClose handleToServer hClose handleToClient
doProxyClient handleToClient handleToServer = do ready <- hReady handleToClient if ready then do text <- hGetChar handleToClient putChar text hPutChar handleToServer text doProxyClient handleToClient handleToServer else do hPutStr handleToServer "\n" doProxyServer handleToClient handleToServer
listen = withSocketsDo $ do putStrLn "Listening..." socket <- listenOn $ PortNumber 8082 (handleToClient, hostName, portNumber) <- accept socket putStrLn "Connected to client" handleToServer <- connectTo "localhost" (PortNumber 8080) hSetBuffering handleToServer LineBuffering putStrLn "Connected to server" doProxyClient handleToClient handleToServer hClose handleToServer putStrLn "Closed server" hClose handleToClient putStrLn "Closed client" sClose socket
main = listen
On 9/20/06, Bulat Ziganshin
Hello John,
Wednesday, September 20, 2006, 3:59:36 PM, you wrote:
I'm trying to use haskell to put together a TCP proxy I can put between my browser and my webserver.
This is as far as I got. The webserver isn't returning my request:
hSetBuffering handleToServer LineBuffering
may help
-- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Hi all, Thanks for all your help. I finally realised that hGetLine strips out the "\n" newline character so when I forward the line to the server, I need to append it again. Here's my implementation. -John
main = proxyServe
proxyServe = withSocketsDo $ do socket <- listenOn $ PortNumber 8082 acceptConnection socket
acceptConnection socket = do (handleToClient, hostName, portNumber) <- accept socket handleToServer <- connectTo "127.0.0.1" (PortNumber 8080) hSetBuffering handleToClient LineBuffering hSetBuffering handleToServer LineBuffering putStrLn $ "Socket accepted from " ++ hostName forkIO (clientToServer handleToClient handleToServer) forkIO (serverToClient handleToClient handleToServer) acceptConnection socket
clientToServer handleToClient handleToServer = do putStrLn "Streaming from client to server" eof <- hIsEOF handleToClient if not eof then do text <- hGetLine handleToClient hPutStr handleToServer $ text ++ "\n" hFlush handleToServer putStr ("C: " ++ text ++ "\n") clientToServer handleToClient handleToServer else do putStrLn "EOF from Client" return ()
serverToClient handleToClient handleToServer = do putStrLn "Streaming from server to client" eof <- hIsEOF handleToClient if not eof then do text <- hGetContents handleToServer hPutStr handleToClient text putStrLn $ "S: " ++ text serverToClient handleToClient handleToServer else do putStrLn "EOF from Server" return()
participants (6)
-
Bulat Ziganshin
-
Christian Sievers
-
David House
-
John Ky
-
Philippa Cowderoy
-
Tomasz Zielonka