scotty 0.7.3 → 0.8.0
raw patch · 10 files changed
+120/−65 lines, 10 filesdep +hspecdep +scottydep −conduit-extradep ~basedep ~bytestringdep ~http-types
Dependencies added: hspec, scotty
Dependencies removed: conduit-extra
Dependency ranges changed: base, bytestring, http-types, wai, wai-extra, warp
Files
- README.md +1/−1
- Web/Scotty.hs +17/−14
- Web/Scotty/Action.hs +20/−14
- Web/Scotty/Route.hs +23/−13
- Web/Scotty/Trans.hs +17/−8
- Web/Scotty/Types.hs +6/−7
- Web/Scotty/Util.hs +4/−1
- changelog.md +13/−2
- scotty.cabal +18/−5
- test/Spec.hs +1/−0
README.md view
@@ -1,4 +1,4 @@-# Scotty+# Scotty [](https://travis-ci.org/scotty-web/scotty)[](https://coveralls.io/r/scotty-web/scotty?branch=master) A Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp.
Web/Scotty.hs view
@@ -17,14 +17,14 @@ -- ** Route Patterns , capture, regex, function, literal -- ** Accessing the Request, Captures, and Query Parameters- , request, header, reqHeader, headers, body, param, params, jsonData, files+ , request, header, headers, body, param, params, jsonData, files -- ** Modifying the Response and Redirecting , status, addHeader, setHeader, redirect -- ** Setting Response Body -- -- | Note: only one of these should be present in any given route -- definition, as they completely replace the current 'Response' body.- , text, html, file, json, source, raw+ , text, html, file, json, stream, source, raw -- ** Exceptions , raise, rescue, next, defaultHandler -- * Parsing Parameters@@ -35,22 +35,23 @@ -- With the exception of this, everything else better just import types. import qualified Web.Scotty.Trans as Trans+import qualified Web.Scotty.Action as Action -- for 'source', to avoid deprecation warning on Trans.source import Blaze.ByteString.Builder (Builder) import Data.Aeson (FromJSON, ToJSON) import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Conduit (Flush, Source) import Data.Text.Lazy (Text)+import Data.Conduit (Flush, Source) import Network.HTTP.Types (Status, StdMethod)-import Network.Wai (Application, Middleware, Request)+import Network.Wai (Application, Middleware, Request, StreamingBody) import Network.Wai.Handler.Warp (Port) import Web.Scotty.Types (ScottyT, ActionT, Param, RoutePattern, Options, File) type ScottyM = ScottyT Text IO-type ActionM = ActionT Text IO +type ActionM = ActionT Text IO -- | Run a scotty application using the warp server. scotty :: Port -> ScottyM () -> IO ()@@ -65,14 +66,14 @@ scottyApp :: ScottyM () -> IO Application scottyApp = Trans.scottyAppT id id --- | Global handler for uncaught exceptions. +-- | Global handler for uncaught exceptions. ----- Uncaught exceptions normally become 500 responses. +-- Uncaught exceptions normally become 500 responses. -- You can use this to selectively override that behavior. -- -- Note: IO exceptions are lifted into Scotty exceptions by default. -- This has security implications, so you probably want to provide your--- own defaultHandler in production which does not send out the error +-- own defaultHandler in production which does not send out the error -- strings as 500 responses. defaultHandler :: (Text -> ActionM ()) -> ScottyM () defaultHandler = Trans.defaultHandler@@ -134,11 +135,6 @@ header :: Text -> ActionM (Maybe Text) header = Trans.header --- | Get a request header. Header name is case-insensitive. (Deprecated in favor of `header`.)-reqHeader :: Text -> ActionM (Maybe Text)-reqHeader = Trans.header-{-# DEPRECATED reqHeader "Use header instead. reqHeader will be removed in the next release." #-}- -- | Get all the request headers. Header names are case-insensitive. headers :: ActionM [(Text, Text)] headers = Trans.headers@@ -198,11 +194,18 @@ json :: ToJSON a => a -> ActionM () json = Trans.json +-- | Set the body of the response to a StreamingBody. Doesn't set the+-- \"Content-Type\" header, so you probably want to do that on your+-- own with 'setHeader'.+stream :: StreamingBody -> ActionM ()+stream = Trans.stream+ -- | Set the body of the response to a Source. Doesn't set the -- \"Content-Type\" header, so you probably want to do that on your -- own with 'setHeader'. source :: Source IO (Flush Builder) -> ActionM ()-source = Trans.source+source = Action.source+{-# DEPRECATED source "Use 'stream' instead. This will be removed in the next release." #-} -- | Set the body of the response to the given 'BL.ByteString' value. Doesn't set the -- \"Content-Type\" header, so you probably want to do that on your own with 'setHeader'.
Web/Scotty/Action.hs view
@@ -16,12 +16,12 @@ , raw , readEither , redirect- , reqHeader -- Deprecated , request , rescue , setHeader- , source , status+ , stream+ , source -- Deprecated , text , Param , Parsable(..)@@ -39,12 +39,13 @@ import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.CaseInsensitive as CI-import Data.Conduit (Flush, Source)-import Data.Default (def)-import Data.Monoid (mconcat)+import Data.Conduit+import qualified Data.Conduit.List as CL+import Data.Default (def)+import Data.Monoid (mconcat) import qualified Data.Text as ST import qualified Data.Text.Lazy as T-import Data.Text.Lazy.Encoding (encodeUtf8)+import Data.Text.Lazy.Encoding (encodeUtf8) import Network.HTTP.Types import Network.Wai@@ -123,11 +124,6 @@ files :: (ScottyError e, Monad m) => ActionT e m [File] files = ActionT $ liftM getFiles ask --- | Get a request header. Header name is case-insensitive. (Deprecated in favor of `header`.)-reqHeader :: (ScottyError e, Monad m) => T.Text -> ActionT e m (Maybe T.Text)-reqHeader = header-{-# DEPRECATED reqHeader "Use header instead. This will be removed in the next release." #-}- -- | Get a request header. Header name is case-insensitive. header :: (ScottyError e, Monad m) => T.Text -> ActionT e m (Maybe T.Text) header k = do@@ -139,7 +135,7 @@ headers = do hs <- liftM requestHeaders request return [ ( strictByteStringToLazyText (CI.original k)- , strictByteStringToLazyText v) + , strictByteStringToLazyText v) | (k,v) <- hs ] -- | Get the request body.@@ -198,7 +194,7 @@ instance (Parsable a) => Parsable [a] where parseParam = parseParamList -instance Parsable Bool where +instance Parsable Bool where parseParam t = if t' == T.toCaseFold "true" then Right True else if t' == T.toCaseFold "false"@@ -262,8 +258,18 @@ -- | Set the body of the response to a Source. Doesn't set the -- \"Content-Type\" header, so you probably want to do that on your -- own with 'setHeader'.+stream :: (ScottyError e, Monad m) => StreamingBody -> ActionT e m ()+stream = ActionT . MS.modify . setContent . ContentStream++-- | Set the body of the response to a Source. Doesn't set the+-- \"Content-Type\" header, so you probably want to do that on your+-- own with 'setHeader'. source :: (ScottyError e, Monad m) => Source IO (Flush Builder) -> ActionT e m ()-source = ActionT . MS.modify . setContent . ContentSource+source src = stream $ \send flush -> src $$ CL.mapM_ (\mbuilder ->+ case mbuilder of+ Chunk b -> send b+ Flush -> flush)+-- Deprecated, but pragma is in Web.Scotty and Web.Scotty.Trans -- | Set the body of the response to the given 'BL.ByteString' value. Doesn't set the -- \"Content-Type\" header, so you probably want to do that on your
Web/Scotty/Route.hs view
@@ -1,20 +1,17 @@-{-# LANGUAGE OverloadedStrings, FlexibleContexts, FlexibleInstances, RankNTypes #-}+{-# LANGUAGE FlexibleContexts, FlexibleInstances, LambdaCase,+ OverloadedStrings, RankNTypes, ScopedTypeVariables #-} module Web.Scotty.Route ( get, post, put, delete, patch, addroute, matchAny, notFound, capture, regex, function, literal ) where import Control.Arrow ((***))+import Control.Concurrent.MVar import Control.Monad.Error import qualified Control.Monad.State as MS import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Conduit (($$), (=$))-import Data.Conduit.Binary (sourceLbs)-import Data.Conduit.Lazy (lazyConsume)-import Data.Conduit.List (consume)-import Data.Either (partitionEithers) import Data.Maybe (fromMaybe) import Data.Monoid (mconcat) import Data.String (fromString)@@ -108,26 +105,39 @@ path :: Request -> T.Text path = T.fromStrict . TS.cons '/' . TS.intercalate "/" . pathInfo --- Stolen from wai-extra, modified to accept body as lazy ByteString+-- Stolen from wai-extra's Network.Wai.Parse, modified to accept body as list of Bytestrings.+-- Reason: WAI's requestBody is an IO action that returns the body as chunks. Once read,+-- they can't be read again. We read them into a lazy Bytestring, so Scotty user can get+-- the raw body, even if they also want to call wai-extra's parsing routines. parseRequestBody :: MonadIO m- => BL.ByteString+ => [B.ByteString] -> Parse.BackEnd y -> Request -> m ([Parse.Param], [Parse.File y])-parseRequestBody b s r =+parseRequestBody bl s r = case Parse.getRequestBodyType r of Nothing -> return ([], [])- Just rbt -> liftIO $ liftM partitionEithers $ sourceLbs b $$ Parse.conduitRequestBody s rbt =$ consume+ Just rbt -> do+ mvar <- liftIO $ newMVar bl -- MVar is a bit of a hack so we don't have to inline+ -- large portions of Network.Wai.Parse+ let provider = takeMVar mvar >>= \case+ [] -> putMVar mvar [] >> return B.empty+ (b:bs) -> putMVar mvar bs >> return b+ liftIO $ Parse.sinkRequestBody s rbt provider -mkEnv :: MonadIO m => Request -> [Param] -> m ActionEnv+mkEnv :: forall m. MonadIO m => Request -> [Param] -> m ActionEnv mkEnv req captures = do- b <- liftIO $ liftM BL.fromChunks $ lazyConsume (requestBody req)+ let rbody = requestBody req+ takeAll :: ([B.ByteString] -> m [B.ByteString]) -> m [B.ByteString]+ takeAll prefix = liftIO rbody >>= \ b -> if B.null b then prefix [] else takeAll (prefix . (b:))+ bs <- takeAll return - (formparams, fs) <- liftIO $ parseRequestBody b Parse.lbsBackEnd req+ (formparams, fs) <- liftIO $ parseRequestBody bs Parse.lbsBackEnd req let convert (k, v) = (strictByteStringToLazyText k, strictByteStringToLazyText v) parameters = captures ++ map convert formparams ++ queryparams queryparams = parseEncodedParams $ rawQueryString req+ b = BL.fromChunks bs return $ Env req parameters b [ (strictByteStringToLazyText k, fi) | (k,fi) <- fs ]
Web/Scotty/Trans.hs view
@@ -21,14 +21,14 @@ -- ** Route Patterns , capture, regex, function, literal -- ** Accessing the Request, Captures, and Query Parameters- , request, header, reqHeader, headers, body, param, params, jsonData, files+ , request, header, headers, body, param, params, jsonData, files -- ** Modifying the Response and Redirecting , status, addHeader, setHeader, redirect -- ** Setting Response Body -- -- | Note: only one of these should be present in any given route -- definition, as they completely replace the current 'Response' body.- , text, html, file, json, source, raw+ , text, html, file, json, stream, source, raw -- ** Exceptions , raise, rescue, next, defaultHandler, ScottyError(..) -- * Parsing Parameters@@ -39,23 +39,32 @@ , ScottyT, ActionT ) where -import Blaze.ByteString.Builder (fromByteString)+import Blaze.ByteString.Builder (Builder, fromByteString) import Control.Monad (when) import Control.Monad.State (execStateT, modify) import Control.Monad.IO.Class +import Data.Conduit (Flush, Source) import Data.Default (def) import Network.HTTP.Types (status404) import Network.Wai import Network.Wai.Handler.Warp (Port, runSettings, setPort, getPort) -import Web.Scotty.Action+import Web.Scotty.Action hiding (source)+import qualified Web.Scotty.Action as Action import Web.Scotty.Route import Web.Scotty.Types hiding (Application, Middleware) import qualified Web.Scotty.Types as Scotty +-- | Set the body of the response to a Source. Doesn't set the+-- \"Content-Type\" header, so you probably want to do that on your+-- own with 'setHeader'.+source :: (ScottyError e, Monad m) => Source IO (Flush Builder) -> ActionT e m ()+source = Action.source+{-# DEPRECATED source "Use 'stream' instead. This will be removed in the next release." #-}+ -- | Run a scotty application using the warp server. -- NB: scotty p === scottyT p id id scottyT :: (Monad m, MonadIO n)@@ -89,21 +98,21 @@ -> n Application scottyAppT runM runActionToIO defs = do s <- runM $ execStateT (runS defs) def- let rapp = runActionToIO . foldl (flip ($)) notFoundApp (routes s)+ let rapp = \ req callback -> runActionToIO (foldl (flip ($)) notFoundApp (routes s) req) >>= callback return $ foldl (flip ($)) rapp (middlewares s) notFoundApp :: Monad m => Scotty.Application m notFoundApp _ = return $ responseBuilder status404 [("Content-Type","text/html")] $ fromByteString "<h1>404: File Not Found!</h1>" --- | Global handler for uncaught exceptions. +-- | Global handler for uncaught exceptions. ----- Uncaught exceptions normally become 500 responses. +-- Uncaught exceptions normally become 500 responses. -- You can use this to selectively override that behavior. -- -- Note: IO exceptions are lifted into 'ScottyError's by 'stringError'. -- This has security implications, so you probably want to provide your--- own defaultHandler in production which does not send out the error +-- own defaultHandler in production which does not send out the error -- strings as 500 responses. defaultHandler :: Monad m => (e -> ActionT e m ()) -> ScottyT e m () defaultHandler f = ScottyT $ modify $ addHandler $ Just f
Web/Scotty/Types.hs view
@@ -10,7 +10,6 @@ import Control.Monad.State import Data.ByteString.Lazy.Char8 (ByteString)-import qualified Data.Conduit as C import Data.Default (Default, def) import Data.Monoid (mempty) import Data.String (IsString(..))@@ -42,7 +41,7 @@ type Application m = Request -> m Response --------------- Scotty Applications ------------------data ScottyState e m = +data ScottyState e m = ScottyState { middlewares :: [Wai.Middleware] , routes :: [Middleware m] , handler :: ErrorHandler e m@@ -72,7 +71,7 @@ | ActionError e -- | In order to use a custom exception type (aside from 'Text'), you must--- define an instance of 'ScottyError' for that type. +-- define an instance of 'ScottyError' for that type. class ScottyError e where stringError :: String -> e showError :: e -> Text@@ -87,7 +86,7 @@ showError Next = pack "Next" showError (ActionError e) = showError e -instance ScottyError e => Error (ActionError e) where +instance ScottyError e => Error (ActionError e) where strMsg = stringError type ErrorHandler e m = Maybe (e -> ActionT e m ())@@ -100,12 +99,12 @@ data ActionEnv = Env { getReq :: Request , getParams :: [Param] , getBody :: ByteString- , getFiles :: [File] + , getFiles :: [File] } data Content = ContentBuilder Builder | ContentFile FilePath- | ContentSource (C.Source IO (C.Flush Builder))+ | ContentStream StreamingBody data ScottyResponse = SR { srStatus :: Status , srHeaders :: ResponseHeaders@@ -136,5 +135,5 @@ | Literal Text | Function (Request -> Maybe [Param]) -instance IsString RoutePattern where +instance IsString RoutePattern where fromString = Capture . pack
Web/Scotty/Util.hs view
@@ -34,11 +34,14 @@ setStatus :: Status -> ScottyResponse -> ScottyResponse setStatus s sr = sr { srStatus = s } +-- Note: we currently don't support responseRaw, which may be useful+-- for websockets. However, we always read the request body, which+-- is incompatible with responseRaw responses. mkResponse :: ScottyResponse -> Response mkResponse sr = case srContent sr of ContentBuilder b -> responseBuilder s h b ContentFile f -> responseFile s h f Nothing- ContentSource src -> responseSource s h src+ ContentStream str -> responseStream s h str where s = srStatus sr h = srHeaders sr
changelog.md view
@@ -1,6 +1,17 @@+## 0.8.0++* Upgrade to wai/wai-extra/warp 3.0++* No longer depend on conduit-extra.++* The `source` response method has been deprecated in favor+ of a new `stream` response, matching changes in WAI 3.0.++* Removed the deprecated `reqHeader` function.+ ## 0.7.3 -* Bump upper bound for `case-insensitive`, `mtl` and `transformers`.+* Bump upper bound for case-insensitive, mtl and transformers. ## 0.7.2 @@ -65,7 +76,7 @@ * Removed lambda action syntax. This will return when we have a better story for typesafe routes. -* `reqHeader :: Text -> ActionM Text` ==> +* `reqHeader :: Text -> ActionM Text` ==> `reqHeader :: Text -> ActionM (Maybe Text)` * New `raw` method to set body to a raw `ByteString`
scotty.cabal view
@@ -1,5 +1,5 @@ Name: scotty-Version: 0.7.3+Version: 0.8.0 Synopsis: Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp Homepage: https://github.com/scotty-web/scotty Bug-reports: https://github.com/scotty-web/scotty/issues@@ -74,18 +74,31 @@ bytestring >= 0.10.0.2 && < 0.11, case-insensitive >= 1.0.0.1 && < 1.3, conduit >= 1.1 && < 1.2,- conduit-extra >= 1.1 && < 1.2, data-default >= 0.5.3 && < 0.6, http-types >= 0.8.2 && < 0.9, mtl >= 2.1.2 && < 2.3, regex-compat >= 0.95.1 && < 0.96, text >= 0.11.3.1 && < 1.2, transformers >= 0.3.0.0 && < 0.5,- wai >= 2.0.0 && < 2.2,- wai-extra >= 2.0.1 && < 2.2,- warp >= 2.1.1 && < 2.2+ wai >= 3.0.0 && < 3.1,+ wai-extra >= 3.0.0 && < 3.1,+ warp >= 3.0.0 && < 3.1 GHC-options: -Wall -fno-warn-orphans++test-suite spec+ main-is: Spec.hs+ type: exitcode-stdio-1.0+ default-language: Haskell2010+ hs-source-dirs: test+ build-depends: base,+ bytestring,+ http-types,+ wai,+ hspec >= 1.9.2,+ wai-extra >= 3.0.0,+ scotty+ GHC-options: -Wall -fno-warn-orphans source-repository head type: git
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}