Skip to content
This repository was archived by the owner on Feb 6, 2024. It is now read-only.

Commit 131d2cd

Browse files
committed
wai-lambda: Implement settings
1 parent 7040f95 commit 131d2cd

File tree

1 file changed

+64
-17
lines changed
  • infra/wai-lambda/src/Network/Wai/Handler

1 file changed

+64
-17
lines changed

infra/wai-lambda/src/Network/Wai/Handler/Lambda.hs

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ import qualified Network.Wai as Wai
3636
import qualified Network.Wai.Internal as Wai
3737
import 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.
4957
run :: 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.
80127
handleRequest
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
260303
readResponse (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@
297340
xif :: b -> ((b -> c) -> b -> c) -> c
298341
xif = 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

Comments
 (0)