I’m trying to make a simple web application using Snap. I want to have a global MongoDB connection which I can use across handlers.
I open this connection in appInit, which works.
data App = App { _mongoDB :: Pipe -- connection
}
appInit :: SnapletInit App App
appInit = makeSnaplet "site" "My Site" Nothing $ do
db <- liftIO $ do
pipe <- runIOE $ connect (host "127.0.0.1")
return pipe
return $ App db
However, I have no idea how to access this connection (_mongoDB) from a handler. I tried several things, including the following, but I get type errors every time.
watchHandler :: Handler App App ()
watchHandler = do
res <- liftIO $ do
pipe <- gets _mongoDB -- type error
results <- access pipe master "db" (find $ select [] "movies")
return results
writeBS "test"
It gives me the following error:
No instance for (MonadState App IO)
arising from a use of `gets'
Possible fix: add an instance declaration for (MonadState App IO)
In a stmt of a 'do' block: pipe <- gets _mongoDB
In the second argument of `($)', namely
`do { pipe <- gets _mongoDB;
results <- access pipe master "db" (find $ select [] "movies");
return results }'
In a stmt of a 'do' block:
res <- liftIO
$ do { pipe <- gets _mongoDB;
results <- access pipe master "db" (find $ select [] "movies");
return results }
It confuses the hell out of me. How can I access my MongoDB connection from a handler?
The liftIO block is just for IO operations, accessing the application state needs to be done in the Handler monad itself.
watchHandler :: Handler App App ()
watchHandler = do
pipe <- gets _mongoDB
res <- liftIO $ do
results <- access pipe master "db" (find $ select [] "movies")
return results
writeBS "test"
Also, binding a value and then immediately returning it is redundant. You can just write:
watchHandler :: Handler App App ()
watchHandler = do
pipe <- gets _mongoDB
res <- liftIO $ access pipe master "db" (find $ select [] "movies")
writeBS "test"
Related
I'm trying to generate a {targets} list programmatically, via a function in an R package.
get_pipeline <- function(which_countries) {
countries <- NULL # avoid R CMD CHECK warning
print(which_countries) # Shows that which_countries is available
list(
targets::tar_target(
name = countries,
command = which_countries # But here, which_countries is not found
)
)
}
The _targets.R file looks like this:
library(targets)
couns <- c("USA", "GBR")
TargetsQuestions::get_pipeline(couns)
I see the following error:
> tar_make()
[1] "USA" "GBR"
Error in enexpr(expr) : object 'which_countries' not found
Error in `tar_throw_run()`:
! callr subprocess failed: object 'which_countries' not found
Note that the which_countries variable is printable, but not found in the call to tar_target.
How can I get create the countries target successfully so that it contains the vector c("USA", "GBR")?
This code is in a GitHub repository at https://github.com/MatthewHeun/TargetsQuestions. To reproduce:
git clone https://github.com/MatthewHeun/TargetsQuestions
Build the package in RStudio.
targets::tar_make() at the Console.
Thanks in advance for any suggestions!
Thanks to #landau for pointing to https://wlandau.github.io/targetopia/contributing.html#target-factories which in turn points to the metaprogramming section of Advanced R at https://adv-r.hadley.nz/metaprogramming.html.
The solution turned out to be:
get_pipeline <- function(which_countries) {
list(
targets::tar_target_raw(
name = "countries",
# command = which_countries # which_countries must have length 1
# command = !!which_countries # invalid argument type
command = rlang::enexpr(which_countries) # Works
)
)
}
With _targets.R like this:
library(targets)
couns <- c("USA", "GBR")
TargetsQuestions::get_pipeline(couns)
the commands tar_make() and tar_read(countries), give
[1] "USA" "GBR"
as expected!
I'm trying to write a very basic webserver in Haskell. This is my code:
{-# LANGUAGE OverloadedStrings #-}
import Network (withSocketsDo, listenOn, PortID(..))
import Network.Socket (Socket, accept, close, setSocketOption, SocketOption(..))
import Network.Socket.ByteString (send, sendAll, recv)
import Control.Concurrent.Async (async)
import Control.Monad (forever)
import Data.ByteString.Char8 (unpack)
import Request
main = withSocketsDo $ do
sock <- listenOn $ PortNumber 3000
putStrLn "Listening on port 3000..."
forever $ do
(conn, _) <- accept sock
async $ handleAccept conn
handleAccept :: Socket -> IO ()
handleAccept sock = do
putStrLn $ "Connected!"
rawReq <- recv sock 4096
let req = parseRawRequest $ unpack rawReq -- returns Maybe Request
putStrLn $ show req
handleRequest sock req
handleRequest :: Socket -> Maybe Request -> IO ()
handleRequest sock Nothing = do
putStrLn "Closing..."
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Doesn't appear until server is killed.
This is what I expected to happen:
Start server.
"Listening on port 3000..." is printed on server-side.
Do curl localhost:3000
"Connected!" is printed server-side.
The request is printed server-side.
"In handleRequest!" is printed.
What actually happens:
Start server.
"Listening on port 3000..." is printed on server-side.
Do curl localhost:3000
"Connected!" is printed server-side.
The request is printed server-side.
I wait patiently
I kill the server with CTRL+C
"In handleRequest!" prints client-side.
I suspect this has something to do with possible laziness in recv, although I use the value immediately afterwards (I parse the raw request into a Request type), so theoretically it should be evaluated.
If I put sendAll sock "Yadda yadda at the end of handleAccept, everything works fine. It's when I move this behaviour into a new function, handleRequest, that things go wonky.
Any thoughts? I'm new-ish to Haskell, so I'd appreciate any comments on the issue, or my code generally.
Cheers.
EDIT:
This is super weird! I "fixed" it, but I have no idea why this occurs.
This is the line that only appeared after I killed the server:
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Doesn't appear until server is killed.
If I intentionally close the socket after sending, it works:
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Now appears without killing the server
close sock
So it sends when the connection is closed. This is consistent with previous behaviour, since the connection automatically closes when the server is killed.
Now for the confusing bit. If I replace it with:
handleRequest sock req = do
sendAll sock "In handleRequest!\n" -- Works perfect
This works without closing the connection! It does what I expected, just by adding a newline. Why does this occur?
What on earth? Is it a printing problem with my terminal, not the code? (OSX iTerm2)
EDIT 2:
Was asked to provide the code for my Request module:
import Data.List (isInfixOf)
import Data.List.Split (splitOn)
data RequestType = GET | PUT
deriving Show
data Request =
Request {
reqType :: RequestType,
path :: String,
options :: [(String, String)]
} deriving Show
-- Turn a raw HTTP request into a request
-- object.
parseRawRequest :: String -> Maybe Request
parseRawRequest rawReq =
Request <$> parseRawRequestType rawReq
<*> parseRawRequestPath rawReq
<*> parseRawRequestOps rawReq
-- Turn an (entire) raw HTTP request into just
-- the request type.
parseRawRequestType :: String -> Maybe RequestType
parseRawRequestType rawReq =
case typ of
"GET" -> Just GET
"PUT" -> Just PUT
_ -> Nothing
where typ = (head . words . head . lines) rawReq
-- Turn an (entire) raw HTTP request into just
-- the path.
parseRawRequestPath :: String -> Maybe String
parseRawRequestPath = Just . (!! 1) . words . head . lines
-- Turn an (entire) raw HTTP request into just
-- a lookup table of their options.
parseRawRequestOps :: String -> Maybe [(String, String)]
parseRawRequestOps rawReq = Just [("One", "Two")] -- Test impl
I have one answer and one suggestion.
The suggestion is for you to turn off the naggle algorithm after accept:
setSocketOption conn NoDelay 1
The answer is that your sendAll is sending data but curl is not printing it. You can confirm this with netcat, for example. I commented out your Nothing case so that no matter what I typed in netcat I was sure to get the "In handleRequest!" message back:
server:
% ghc so.hs && ./so
Listening on port 3000...
Connected!
Nothing
client:
% nc localhost 3000
test ; My input, with a newline
In handleRequest! ; Printed out, no newline
Alternatively, you can use curl's -N option to disable buffering.
% curl -N localhost:3000
In handleRequest!
I found this when reading the warp source code.
-- | Run an 'Application' with the given 'Settings'.
runSettings :: Settings -> Application -> IO ()
runSettings set app = withSocketsDo $
bracket
(bindPortTCP (settingsPort set) (settingsHost set))
sClose
(\socket -> do
setSocketCloseOnExec socket
runSettingsSocket set socket app)
-- Copied from: https://github.com/mzero/plush/blob/master/src/Plush/Server/Warp.hs
setSocketCloseOnExec :: Socket -> IO ()
#if WINDOWS
setSocketCloseOnExec _ = return ()
#else
setSocketCloseOnExec socket =
setFdOption (fromIntegral $ fdSocket socket) CloseOnExec True
#endif
Sockets used by the web server need to be set so that they are not leaked into processes fork/exec'd by the shell.
However, acceptFork in Network.Simple.TCP module does not set CloseOnExec option. Shouldn't this option be the default?
I'm fairly new to Scala and need to build a really simple command line parser which provides something like the following which I created using JRuby in a few minutes:-
java -jar demo.jar --help
Command Line Example Application
Example: java -jar demo.jar --dn "CN=Test" --nde-url "http://www.example.com" --password "password"
For usage see below:
-n http://www.example.com
-p, --password set the password
-c, --capi set add to Windows key-store
-h, --help Show this message
-v, --version Print version
Scallop looks like it will do the trick, but I can't seem to find a simple example that works! All of the examples I've found seem to be fragmented and don't work for some reason or other.
UPDATE
I found this example which works, but I'm not sure how to bind it into the actual args within the main method.
import org.rogach.scallop._;
object cmdlinetest {
def main(args: Array[String])
val opts = Scallop(List("-d","--num-limbs","1"))
.version("test 1.2.3 (c) 2012 Mr Placeholder")
.banner("""Usage: test [OPTION]... [pet-name]
|test is an awesome program, which does something funny
|Options:
|""".stripMargin)
.footer("\nFor all other tricks, consult the documentation!")
.opt[Boolean]("donkey", descr = "use donkey mode")
.opt("monkeys", default = Some(2), short = 'm')
.opt[Int]("num-limbs", 'k',
"number of libms", required = true)
.opt[List[Double]]("params")
.opt[String]("debug", hidden = true)
.props[String]('D',"some key-value pairs")
// you can add parameters a bit later
.args(List("-Dalpha=1","-D","betta=2","gamma=3", "Pigeon"))
.trailArg[String]("pet name")
.verify
println(opts.help)
}
}
Well, I'll try to add more examples :)
In this case, it would be much better to use ScallopConf:
import org.rogach.scallop._
object Main extends App {
val opts = new ScallopConf(args) {
banner("""
NDE/SCEP Certificate enrollment prototype
Example: java -jar demo.jar --dn CN=Test --nde-url http://www.example.com --password password
For usage see below:
""")
val ndeUrl = opt[String]("nde-url")
val password = opt[String]("password", descr = "set the password")
val capi = toggle("capi", prefix = "no-", descrYes = "enable adding to Windows key-store", descrNo = "disable adding to Windows key-store")
val version = opt[Boolean]("version", noshort = true, descr = "Print version")
val help = opt[Boolean]("help", noshort = true, descr = "Show this message")
}
println(opts.password())
}
It prints:
$ java -jar demo.jar --help
NDE/SCEP Certificate enrollment prototype
Example: java -jar demo.jar --dn CN=Test --nde-url http://www.example.com --password password
For usage see below:
-c, --capi enable adding to Windows key-store
--no-capi disable adding to Windows key-store
--help Show this message
-n, --nde-url <arg>
-p, --password <arg> set the password
--version Print version
Did you read the documentation? It looks like all you have to do is call get for each option you want:
def get [A] (name: String)(implicit m: Manifest[A]): Option[A]
It looks like you might need to provide the expected return type in the method call. Try something like this:
val donkey = opts.get[Boolean]("donkey")
val numLimbs = opts.get[Int]("num-limbs")
If you're just looking for a quick and dirty way to parse command line arguments, you can use pirate, an extremely barebones way to parse arguments. Here is what it would look like to handle the usage you describe above:
import com.mosesn.pirate.Pirate
object Main {
def main(commandLineArgs: Array[String]) {
val args = Pirate("[ -n string ] [ -p string ] [ -chv ]")("-n whatever -c".split(" "))
val c = args.flags.contains('c')
val v = args.flags.contains('v')
val h = args.flags.contains('h')
val n = args.strings.get("n")
val p = args.strings.get("p")
println(Seq(c, v, h, n, p))
}
}
Of course, for your program, you would pass commandLineArgs instead of "-n whatever -c".
Unfortunately, pirate does not yet support GNU style arguments, nor the version or help text options.
I'm messing around with an echo server I found online, trying to get a feel for network programming with Haskell, and I'm hitting a stumbling block. I can't seem to figure out how to send data to the server (via another program or any other means). My current attempt is as follows:
import Network (connectTo, Socket, PortID(..))
import System.IO (hPutStrLn, hClose, hSetBuffering, BufferMode(..))
main :: IO ()
main = do
handle <- connectTo "127.0.0.1" (PortNumber 5555)
hSetBuffering handle LineBuffering
hPutStrLn handle "echo hello, world!"
hPutStrLn handle "add 1 2"
hClose handle
When I run main, I get the error "Server.hs: : hPutChar: resource vanished (Broken pipe)" in the terminal in which the server is running. The server code is as follows:
import Network (listenOn, accept, withSocketsDo, PortID(..), Socket)
import System (getArgs)
import System.IO (hSetBuffering, hGetLine, hPutStrLn, BufferMode(..), Handle)
import Control.Concurrent (forkIO)
main :: IO ()
main = withSocketsDo $ do
args <- getArgs
let port = fromIntegral (read $ head args :: Int)
sock <- listenOn $ PortNumber port
putStrLn $ "Listening on " ++ show port
sockHandler sock
sockHandler :: Socket -> IO ()
sockHandler sock = do
(handle, _, _) <- accept sock
hSetBuffering handle NoBuffering
forkIO $ commandProcessor handle
sockHandler sock
commandProcessor :: Handle -> IO ()
commandProcessor handle = do
line <- hGetLine handle
let cmd = words line
case (head cmd) of
("echo") -> echoCommand handle cmd
("add") -> addCommand handle cmd
_ -> do hPutStrLn handle "Unknown command."
commandProcessor handle
echoCommand :: Handle -> [String] -> IO ()
echoCommand handle cmd = do
hPutStrLn handle (unwords $ tail cmd)
addCommand :: Handle -> [String] -> IO ()
addCommand handle cmd = do
hPutStrLn handle $ show $ (read $ cmd !! 1) + (read $ cmd !! 2)
How do I go about fixing this? I want to get to extending the server so I can learn some more. Thanks!
In this instance, the problem is simple. You're writing to the server an echo command, and then an add command, and then disconnecting. The server then tries to process the echo command, and then it tries to write back to the client, but the client already disconnected! hence you get an exception.
The client can't disconnect until it reads back enough data from the server -- and the server should handle exceptions so that a client disconnect doesn't kill it.