packages feed

sockets-0.4.0.0: example/Main.hs

{-# language BangPatterns #-}
{-# language LambdaCase #-}
{-# language OverloadedStrings #-}

import Prelude hiding (log,length)

import Control.Exception (Exception,throwIO)
import System.Environment (getArgs)
import Data.Primitive (ByteArray(..))
import Control.Monad (replicateM_)
import Data.Bytes.Types (Bytes(..))

import qualified Socket.Datagram.IPv4.Unconnected as DIU
import qualified Socket.Datagram.Uninterruptible.Bytes as DIU
import qualified Socket.Stream.IPv4 as SI
import qualified Socket.Stream.Uninterruptible.Bytes as SI
import qualified GHC.Exts as E
import qualified Data.Primitive as PM
import qualified Data.Primitive.MVar as PM
import qualified Net.IPv4 as IPv4
import qualified Data.ByteString as B
import qualified Data.ByteString.Short.Internal as SB
import qualified Data.ByteString.Char8 as BC
import qualified System.Log.FastLogger as FL
import qualified Data.Primitive as PM

-- TODO: Use optparse-applicative to let the user choose
-- between different commands.

main :: IO ()
main = udpStdoutServer

gettysburgClient :: IO ()
gettysburgClient = do
  [port] <- getArgs
  unhandled $ SI.withConnection
    (SI.Peer IPv4.loopback (read port)) 
    throwOnCloseException $ \conn -> unhandled $ SI.send conn $ Bytes
      { array = gettysburgAddress 
      , offset = 0
      , length = PM.sizeofByteArray gettysburgAddress
      }

-- This waits until a single connection is established. It then dumps out
-- anything it receives to stdout. When the remote application shuts down
-- its end, this shuts down its end in return and then terminates.
tcpStdoutServer :: IO ()
tcpStdoutServer = do
  FL.withFastLogger (FL.LogStdout 2048) $ \log -> do
    unhandled $ SI.withListener (SI.Peer IPv4.loopback 0) $ \listener port -> do
      log (FL.toLogStr ("Listening on 127.0.0.1:" <> BC.pack (show port) <> "\n============================\n"))
      unhandled $ SI.withAccepted listener throwOnCloseException $ \conn _ -> do
        let go = SI.receiveOnce conn 512 >>= \case
              Left SI.ReceiveShutdown -> pure ()
              Left e -> throwIO e
              Right (PM.ByteArray arr) -> do
                log (FL.toLogStr (SB.fromShort (SB.SBS arr)))
                go
        go

-- Print every UDP packet that we receive. This terminates, closing the
-- socket, after receiving ten packets.
udpStdoutServer :: IO ()
udpStdoutServer = do
  unhandled $ DIU.withSocket (DIU.Peer IPv4.loopback 0) $ \sock port -> do
    BC.putStrLn ("Receiving datagrams on 127.0.0.1:" <> BC.pack (show port))
    replicateM_ 10 $ do
      DIU.Message remote (ByteArray payload) <- unhandled (DIU.receiveFromIPv4 sock 1024)
      BC.putStrLn ("Datagram from " <> BC.pack (show remote))
      BC.putStr (SB.fromShort (SB.SBS payload))

unhandled :: Exception e => IO (Either e a) -> IO a
unhandled action = action >>= either throwIO pure

throwOnCloseException :: Either SI.CloseException () -> () -> IO ()
throwOnCloseException e () = either throwIO pure e

gettysburgAddress :: PM.ByteArray
gettysburgAddress = PM.ByteArray arr
  where
  !(SB.SBS arr) = SB.toShort $ mconcat
    [ "Four score and seven years ago our fathers brought forth on this "
    , "continent, a new nation, conceived in Liberty, and dedicated to "
    , "the proposition that all men are created equal. Now we are engaged "
    , "in a great civil war, testing whether that nation, or any nation so "
    , "conceived and so dedicated, can long endure. We are met on a great "
    , "battle-field of that war. We have come to dedicate a portion of that "
    , "field, as a final resting place for those who here gave their lives "
    , "that that nation might live. It is altogether fitting and proper that "
    , "we should do this."
    ]