How do I use wai-handler-devel with a simple wai application - haskell

I have the basic "hello world" application setup using wai, and would like to use wai-handler-devel, but am unsure how to go about it and can't find any examples of it in usage on a wai project.
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
import Data.ByteString.Lazy.Char8 () -- Just for an orphan instance
app :: Application
app _ = return $ responseLBS
status200
[("Content-Type", "text/plain")]
"Hello, World!"
main :: IO ()
main = do
putStrLn $ "http://localhost:8080/"
run 8080 app
What do I need to do to get wai-handler-devel working with a basic wai app?
Note:
There is a fix here ( https://gist.github.com/1499226) i f you run into issues with "wai-handler-devel: command not found"

wai-handler-devel's Hackage page says that it should be invoked from the command-line like so:
$ wai-handler-devel <port> My.App.Module myApp
and that your application's type must look like this:
myApp :: (Application -> IO ()) -> IO ()
In this case, you should define myApp as follows:
myApp :: (Application -> IO ()) -> IO ()
myApp handler = handler app
although you may want to inline app entirely:
myApp :: (Application -> IO ()) -> IO ()
myApp handler = handler $ \_ -> return $ responseLBS
status200
[("Content-Type", "text/plain")]
"Hello, World!"
The type is like this so that you can do initialisation on start-up and the like in IO. I suggest reading the SmallApp and FullApp examples from wai-handler-devel's git repository; the latter is especially helpful, as it has debug output showing the flow of the code during a reload, and shows how to integrate a long-running database connection.
The run script for the FullApp example also shows how to use wai-handler-devel programmatically, including manually specifying Hamlet template dependencies (which the wai-handler-devel command-line tool determines automatically).
You should then be able to rewrite your main as follows:
main :: IO ()
main = do
putStrLn $ "http://localhost:8080/"
myApp (run 8080)
Of course, you could just as easily pass the run function from wai-handler-fastcgi, wai-handler-scgi or even wai-handler-webkit.

Related

perform IO inside wai application

I am following the basic dispatching section of wai application.
I am able to catch the url parameter. How can I perform IO operation using these params.
I would like to use runCommand of System.Process to execute a system command using these parameters.
:t runCommand give
runCommand :: String -> IO ProcessHandle
my Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad
import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
import Data.ByteString
import Control.Monad
import System.Process
import qualified Data.ByteString.Lazy.Char8 as L8
main :: IO ()
main = do
run 8080 app
app :: Application
app request respond = respond $ case rawPathInfo request of
"/" -> indexHtml
"/wake" -> wakeMeUP request
_ -> fourNotFour
indexHtml :: Response
indexHtml = responseFile
status200
[("Content-Type","text/html")]
"index.html"
Nothing
wakeMeUP :: Request -> Response
wakeMeUP request =
let query = queryString request
hour = join $ lookup "hour" query
min = join $ lookup "min" query
--I would like to use runCommand "using hour and min variables"
in responseLBS
status200
[("Content-Type","text/plain")]
"Alarm set at...to be coded later"
fourNotFour :: Response
fourNotFour = responseLBS
status404
[("Content-Type","text/plain")]
"404 not found"
Your design prevents it, because of how you have written app,
app request respond = respond $ case rawPathInfo request of
which says that you immediately respond. Note the type of Application:
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
Since the result type has an IO, you have the opportunity to do I/O before yielding a value. So you could:
app request respond = do
whateverResult <- runCommand "whatever"
respond $ ...
(You could also do it afterward, at least according to the types:
app request respond = do
rcvd <- respond $ ...
runCommand "whatever"
return rcvd
Though that's a bit odd to do for the continuation-passing idiom being used here (the (a -> b) -> b pattern in the Application type). It means that the command will be run after everything else, for some definition of "everything else" that we can't know without reading the wai source.)
Anyway, you probably don't want the command to be run inside app, but rather in wakeMeUp, which means you need to change some types around. In particular,
wakeMeUp :: Request -> IO Response
-- ^^
and suitably monadify the function. Then your app needs to not call respond immediately, so you can say
app request respond =
response <- case rawPathInfo request of
"/" -> return indexHtml
-- ^^^^^^
"/wake" -> wakeMeUp request
-- no change, because wakeMeUp now has an IO return type
...
respond response
If this is gibberish to you, it's time to do some monad tutorials. Or if you just want to get the damn thing working, I recommend Dan Piponi's The IO Monad For People who Simply Don't Care. Happy hacking!

Scotty with Persistent and Hspec-wai

Problem trying to unit test routes. Scotty, Persistent, and Hspec-WAI.
Unlike Yesod or Spock, Scotty doesn't have a nice place to store database handlers. I've got it working by having one massive "do" that starts up the database, keeps the database pool as a local variable, then uses that variable.
app :: IO ()
app = do
-- allocate_database $ \pool
-- scotty 8080 $do
-- handleSomeRoute pool
However, Hspec-WAI wants it in the IO Application form.
scottyApp :: ScottyM () -> IO Application
Is there a sane way to inject the DB connection pool into a scottyApp ?
Here's how you can do it. Basically you open the database before you make the hspec call:
{-# LANGUAGE OverloadedStrings #-}
import Test.Hspec
import Test.Hspec.Wai
import Network.Wai (Application)
import qualified Web.Scotty as S
allocate_db :: (Int -> IO a) -> IO a
allocate_db = undefined
handleSomeRoute :: Int -> S.ScottyM ()
handleSomeRoute = undefined
main2 :: IO ()
main2 = allocate_db $ \pool -> do
let app' = handleSomeRoute pool
hspec $ with (S.scottyApp app') $ do
describe "GET /" $ do
it "responds with 200" $ do
get "/" `shouldRespondWith` 200

IO monad issues when trying to implement WAI HTTP Server + Fallback Proxy

What I'm trying to do is to create a somewhat smart reverse proxy server that should process some requests on its own and forward the others to the backend of choice. To make it challenging I'm trying hard to do it in Haskell, which I am a total newbie in.
Here's the code I've come up so far:
{-# LANGUAGE OverloadedStrings #-}
import Control.Applicative
import Data.ByteString
import Network.HTTP.ReverseProxy
import Network.HTTP.Types
import Network.Wai
import Network.Wai.Handler.Warp
import Network.Wai.Middleware.RequestLogger
import qualified Network.HTTP.Client as HC
helloApp :: Application
helloApp req respond =
respond $ responseLBS status200 [("Content-Type", "text/plain")] "Hello"
proxyStubApp :: Application
proxyStubApp req respond =
respond $ responseLBS status200 [("Content-Type", "text/plain")] "You've hit the stub"
proxyApp :: IO Application
proxyApp = do
manager <- HC.newManager HC.defaultManagerSettings
return $ waiProxyTo (const $ return $ WPRProxyDest ProxyDest { pdHost = "localhost", pdPort = 9393 }) defaultOnExc manager
app :: Application
app req respond =
serve req respond
where serve = lookupServeFunction req
lookupServeFunction :: Request -> Application
lookupServeFunction req
| isInfixOf "sample_path" (rawPathInfo req) = proxyStubApp
| otherwise = helloApp
main = run 3011 =<< (logStdoutDev <$> return app)
It works fine, but when I exchange proxyStubApp for actual proxyApp I am forced to add IO all over the place. Particularly it gets added to app, consequently leaving me with the following compilation error message:
Couldn't match expected type ‘Request -> t5 -> t4’
with actual type ‘IO Application’
The equation(s) for ‘app’ have two arguments,
but its type ‘IO Application’ has none
I feel like I understand why it is happening, but I'm out of ideas of how to cope with it :( Or am I doing something totally wrong?
Thank you!
P.S. Here are the dependencies should you want to compile the thing on your own: wai warp http-types text bytestring wai-extra time http-reverse-proxy http-client
The IO in IO Application is kind-of redundant. Note that
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
so, expanding proxyApp's arguments (what you already do in proxyStubApp), you get
proxyApp :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
proxyApp req continuation = do
manager <- HC.newManager HC.defaultManagerSettings
waiProxyTo (...) req respond
That works, because in either case
proxyApp :: IO Application
proxyApp = do
manager <- HC.newManager ...
...
and
proxyApp :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
proxyApp req continuation = do
manager <- HC.newManager ...
...
the IO action HC.newManager ... is "run within IO".
You may find it conceptually clearer to construct an Application in IO and hand it to some other place, and I won't argue with you. I want to note though, that you choose the Application based on the Request, so in a way you are in the hypothetical HTTP monad when choosing, so lookupServeFunction's signature Request -> Application makes more sense to me.
If you want to keep that type signature for proxyApp,
lookupServeFunction and app will have to be in IO as well and main will have to change accordingly, e.g.
myApp <- app
...
As haoformayor said, It is generally easier to work without the outer IO layer.
You might also like to simplify main.
fmap logStdoutDev (return app)
is the same as
return (logStdoutDev app)
and
run 3011 =<< return (logStdoutDev app)
is the same as
run 3011 (logStdoutDev app)
You might want to install hlint, which will help you spot these.

Scotty monad transformer for per-handler Reader

In the question Web, Scotty: connection pool as monad reader it is shown how to use ScottyT to embed a Reader monad in the stack to access a static configuration (in that case, a connection pool).
I have a similar question, but simpler – or at least I thought so…
I want to add a Reader to a single handler (i.e. a ActionT), not the whole app.
I started modifying the program from the question above, but I cannot figure out how to turn an ActionT Text (ReaderT String IO) into the ActionT Text IO the handler needs to be. After fumbling around and trying to use typed holes hoping to see how to construct this I have to give up for now and ask for help. I really feel this should be simple, but cannot figure out how to do this.
Here's the program, with the lines where I'm stuck highlighted:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.Lazy as T
import Data.Text.Lazy (Text)
import Control.Monad.Reader
import Web.Scotty.Trans
type ActionD = ActionT Text (ReaderT String IO)
main :: IO ()
main = do
scottyT 3000 id id app
-- Application
app :: ScottyT Text IO ()
app = do
get "/foo" $ do
h <- handler -- ?
runReaderT h "foo" -- ?
--get "/bar" $ do
-- h <- handler
-- runReaderT h "bar"
-- Route action handler
handler :: ActionD ()
handler = do
config <- lift ask
html $ T.pack $ show config
If you want to run each action in a separate reader, you don't need the more complex Scotty.Trans interface at all. You can just build you monad stack the other way around, with ReaderT on top.
import qualified Data.Text.Lazy as T
import Control.Monad.Reader
import Web.Scotty
type ActionD = ReaderT String ActionM
main :: IO ()
main = do
scotty 3000 app
-- Application
app :: ScottyM ()
app = do
get "/foo" $ do
runReaderT handler "foo"
-- Route action handler
handler :: ActionD ()
handler = do
config <- ask
lift $ html $ T.pack $ show config

How do I perform IO inside a WAI (Warp) Application

I have a simple WAI application (Warp in this case) that responds to all web requests with "Hi". I also want it to display "Said hi" on the server each time a request is processed. How do I perform IO inside my WAI response handler? Here's my application:
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.HTTP.Types (status200)
import Network.Wai.Handler.Warp (run)
main :: IO ()
main = do
putStrLn "http://localhost:3000/"
run 3000 app
app :: Application
app _ = return hello
hello = responseLBS status200 [("Content-Type", "text/plain")] "Hi"
The type of a WAI application is:
type Application = Request -> Iteratee ByteString IO Response
This means that a WAI application runs in an Iteratee monad transformer over IO, so you'll have to use liftIO to perform regular IO actions.
import Control.Monad.Trans
app _ = do
liftIO $ putStrLn "Said hi"
return hello

Resources