summaryrefslogtreecommitdiff
path: root/utils/benchmarks/EventHttp.hs
blob: ea74314b81826b01c9770e66f35dfd18fa7303df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings, BangPatterns #-}

import Control.Concurrent
import Control.Exception (finally)
import Control.Monad
import GHC.Conc hiding (ensureIOManagerIsRunning)
import GHC.Event (ensureIOManagerIsRunning)
import GHC.Event.Manager as M
import Foreign.C.Error
import Foreign.C.Types
import Foreign.Marshal.Alloc
import Foreign.Marshal.Utils
import Foreign.ForeignPtr
import Foreign.Ptr
import System.Posix.Types
import Network.Socket hiding (accept)
import qualified Network.Socket.Address as A
import EventSocket (recv, sendAll, c_recv, c_send)
import EventUtil (setNonBlocking)
import Data.ByteString.Char8 as B hiding (zip)
import Data.ByteString.Internal as B

main = do
  ensureIOManagerIsRunning
  let port = "5002"
      myHints = defaultHints { addrFlags = [AI_PASSIVE]
                             , addrSocketType = Stream }
  (ai:_) <- getAddrInfo (Just myHints) Nothing (Just port)
  sock <- socket (addrFamily ai) (addrSocketType ai) (addrProtocol ai)
  setSocketOption sock ReuseAddr 1
  bind sock (addrAddress ai)
  listen sock 1024
  mgrs <- replicateM numCapabilities M.new
  done <- newEmptyMVar
  forM_ (zip [0..] mgrs) $ \(cpu,mgr) -> do
    forkOn cpu $ do
      accept mgr sock clinet
      M.loop mgr
      putMVar done ()
  takeMVar done

repeatOnIntr :: IO (Either Errno a) -> IO (Either Errno a)
repeatOnIntr act = do
  ret <- act
  case ret of
    l@(Left err) -> if err == eINTR
                    then repeatOnIntr act
                    else return l
    r            -> return r

blocking :: EventManager
         -> Either (Fd,Event) FdKey
         -> IO (Either Errno a)
         -> (Either Fd FdKey -> a -> IO ())
         -> IO ()
blocking mgr efdk act on_success = do
  ret <- repeatOnIntr act
  case ret of
    Left err
        | err /= eWOULDBLOCK && err /= eAGAIN ->
            ioError (errnoToIOError "accept" err Nothing Nothing)
        | otherwise ->
            case efdk of
              Left (fd,evts) -> void (registerFd mgr retry_evt fd evts OneShot)
              Right _        -> return ()
    Right a -> case efdk of
                 Left (fd,_evts) -> on_success (Left fd) a
                 Right fdk       -> on_success (Right fdk) a
 where retry_evt fdk _ = blocking mgr (Right fdk) act on_success

accept :: EventManager -> Socket
       -> (EventManager -> Socket -> SockAddr -> IO ())
       -> IO ()
accept mgr sock serve =
  withFdSocket sock $ \fd -> do
    sk <- getSocketName sock
    let sz = A.sizeOfSocketAddress sk
        act :: IO (Either Errno (CInt, SockAddr))
        act = allocaBytes sz $ \sockaddr -> do
              n <- with (fromIntegral sz) $ c_accept (fromIntegral fd) sockaddr
              if n == -1
                then Left `fmap` getErrno
              else do
                sa <- peekSockAddr sockaddr
                return $! Right (n, sa)
  blocking mgr (Left (fromIntegral fd,evtRead)) act $ \_efdk (nfd,addr) -> do
    setNonBlocking (fromIntegral nfd)
    nsock <- MkSocket nfd family stype proto `fmap` newMVar Connected
    serve mgr nsock addr

clinet :: EventManager -> Socket -> SockAddr -> IO ()
clinet mgr sock _ = withFdSocket sock $ \fd -> do
  let bufSize = 4096
      act = do
        fp <- B.mallocByteString bufSize
        withForeignPtr fp $ \ptr -> do
          ret <- c_recv fd ptr (fromIntegral bufSize) 0
          if ret == -1
            then Left `fmap` getErrno
          else if ret == 0
            then return $! Right empty
            else do
              let !bs = PS (castForeignPtr fp) 0 (fromIntegral ret)
              return $! Right bs
  blocking mgr (Left (fromIntegral fd,evtRead)) act $ \efdk bs -> do
    fd <- case efdk of
            Left fd -> return fd
            Right fdk -> unregisterFd_ mgr fdk >> return (keyFd fdk)
    let (PS fp off len) = "HTTP/1.0 200 OK\r\nConnection: Close\r\nContent-Length: 5\r\n\r\nPong!"
    withForeignPtr fp $ \s ->
      c_send (fromIntegral fd) (s `plusPtr` off) (fromIntegral len) 0
    close sock

client :: EventManager -> Socket -> SockAddr -> IO ()
client _mgr sock _addr = loop' `finally` close sock
 where
  loop' = do
    req <- recvRequest ""
    sendAll sock msg
    when ("Connection: Keep-Alive" `isInfixOf` req) loop'
  msg = "HTTP/1.0 200 OK\r\nConnection: Close\r\nContent-Length: 5\r\n\r\nPong!"
  recvRequest r = do
    s <- recv sock 4096
    let t = B.append r s
    if B.null s || "\r\n\r\n" `B.isInfixOf` t
      then return t
      else recvRequest t

foreign import ccall unsafe "sys/socket.h accept"
    c_accept :: CInt -> Ptr SockAddr -> Ptr CInt{-CSockLen???-} -> IO CInt