@@ -36,6 +36,14 @@ import qualified Network.Wai as Wai
3636import qualified Network.Wai.Internal as Wai
3737import qualified System.IO.Temp as Temp
3838
39+ type RawResponse = (H. Status , H. ResponseHeaders , BS. ByteString )
40+
41+ data Settings = Settings
42+ { timeoutValue :: Int
43+ , handleTimeout :: BS. ByteString -> IO RawResponse
44+ , handleException :: BS. ByteString -> SomeException -> IO RawResponse
45+ }
46+
3947-- | Run an 'Application'.
4048--
4149-- Continuously reads requests from @stdin@. Each line should be a a JSON
@@ -47,7 +55,10 @@ import qualified System.IO.Temp as Temp
4755--
4856-- If you need more control use 'handleRequest' directly.
4957run :: Application -> IO ()
50- run app = xif BS. empty $ \ loop leftover ->
58+ run = runSettings defaultSettings
59+
60+ runSettings :: Settings -> Application -> IO ()
61+ runSettings settings app = xif BS. empty $ \ loop leftover ->
5162 -- XXX: we don't use getLine because it errors out on EOF; here we deal
5263 -- with this explicitly
5364 BS. hGetSome stdin 4096 >>= \ bs ->
@@ -56,17 +67,53 @@ run app = xif BS.empty $ \loop leftover ->
5667 else case second BS8. uncons $ BS8. break (== ' \n ' ) (leftover <> bs) of
5768 (_tmpLine, Nothing ) -> loop (leftover <> bs)
5869 (line, Just (' \n ' , rest)) -> do
59- void $ forkIO $ handleRequest app defaultTimeout line
70+ void $ forkIO $ handleRequest settings app line
6071 loop rest
6172 -- This happens if 'break' found a newline character but 'uncons'
6273 -- returned something different
6374 (_tmpLine, Just {}) -> throwIO $ userError $
6475 " wai-lambda: The impossible happened: was expecting newline"
6576
77+ setTimeoutSeconds :: Int -> Settings -> Settings
78+ setTimeoutSeconds tout settings = settings
79+ { timeoutValue = tout * 1000 * 1000 }
80+
81+ setHandleException
82+ :: (BS. ByteString -> SomeException -> IO RawResponse )
83+ -> Settings
84+ -> Settings
85+ setHandleException handler settings = settings
86+ { handleException = handler}
87+
88+ setHandleTimeout
89+ :: (BS. ByteString -> IO RawResponse )
90+ -> Settings
91+ -> Settings
92+ setHandleTimeout handler settings = settings
93+ { handleTimeout = handler}
94+
95+ defaultSettings :: Settings
96+ defaultSettings = Settings
97+ { timeoutValue = defaultTimeoutValue
98+ , handleTimeout = defaultHandleTimeout
99+ , handleException = defaultHandleException
100+ }
101+
102+ defaultHandleException :: BS. ByteString -> SomeException -> IO RawResponse
103+ defaultHandleException bs e = do
104+ putStrLn $
105+ " Could not process request: " <> show bs <>
106+ " error: " <> show e
107+ pure (H. status500, [] , " Internal Server Error" )
66108
67109-- | Default request timeout. 2 seconds.
68- defaultTimeout :: Int
69- defaultTimeout = 2 * 1000 * 1000
110+ defaultTimeoutValue :: Int
111+ defaultTimeoutValue = 2 * 1000 * 1000
112+
113+ defaultHandleTimeout :: BS. ByteString -> IO RawResponse
114+ defaultHandleTimeout bs = do
115+ putStrLn $ " Timeout processing request: " <> show bs
116+ pure (H. status504, [] , " Timeout" )
70117
71118-------------------------------------------------------------------------------
72119-- Request handling
@@ -78,11 +125,11 @@ defaultTimeout = 2 * 1000 * 1000
78125-- * Returns 500 if an exception occurs while processing the request.
79126-- * Throws an exception if the input cannot be parsed.
80127handleRequest
81- :: Application
82- -> Int -- ^ Timeout in microseconds
128+ :: Settings
129+ -> Application
83130 -> BS. ByteString -- ^ The request (see 'decodeInput')
84131 -> IO ()
85- handleRequest app tout bs = case decodeInput bs of
132+ handleRequest settings app bs = case decodeInput bs of
86133 Left err -> do
87134 -- The request couldn't be parsed. There isn't much we can do since we
88135 -- don't even know where to put the response.
@@ -94,19 +141,15 @@ handleRequest app tout bs = case decodeInput bs of
94141 throwIO $ userError msg
95142 Right (fp, mkReq) -> do
96143 req <- mkReq
97- mresp <- timeout tout $ tryAny $ processRequest app req
144+ mresp <- timeout (timeoutValue settings) $ tryAny $ processRequest app req
98145 resp <- case mresp of
99146 Just (Right r) -> do
100147 (st, hdrs, body) <- readResponse r
101148 pure $ toJSONResponse st hdrs body
102- Just (Left e) -> do
103- putStrLn $
104- " Could not process request: " <> show bs <>
105- " error: " <> show e
106- pure $ toJSONResponse H. status500 [] " Internal Server Error"
107- Nothing -> do
108- putStrLn $ " Timeout processing request: " <> show bs
109- pure $ toJSONResponse H. status504 [] " Timeout"
149+ Just (Left e) ->
150+ uncurry3 toJSONResponse <$> handleException settings bs e
151+ Nothing ->
152+ uncurry3 toJSONResponse <$> handleTimeout settings bs
110153
111154 writeFileAtomic fp $ BL. toStrict $ Aeson. encode $ Aeson. Object resp
112155
@@ -256,7 +299,7 @@ originalRequestKey = unsafePerformIO Vault.newKey
256299{-# NOINLINE originalRequestKey #-}
257300
258301-- | Read the status, headers and body of a 'Wai.Response'.
259- readResponse :: Wai. Response -> IO ( H. Status , H. ResponseHeaders , BS. ByteString )
302+ readResponse :: Wai. Response -> IO RawResponse
260303readResponse (Wai. responseToStream -> (st, hdrs, mkBody)) = do
261304 body <- mkBody drainBody
262305 pure (st, hdrs, body)
@@ -296,3 +339,7 @@ writeFileAtomic fp bs =
296339-- | @flip fix@
297340xif :: b -> ((b -> c ) -> b -> c ) -> c
298341xif = flip fix
342+
343+ {-# INLINE uncurry3 #-}
344+ uncurry3 :: (a -> b -> c -> d ) -> ((a , b , c ) -> d )
345+ uncurry3 f ~ (a,b,c) = f a b c
0 commit comments