servant-options (empty) → 0.1.0.0
raw patch · 5 files changed
+152/−0 lines, 5 filesdep +basedep +bytestringdep +http-typessetup-changed
Dependencies added: base, bytestring, http-types, servant-foreign, servant-server, text, wai
Files
- LICENSE +21/−0
- README.md +37/−0
- Setup.hs +2/−0
- servant-options.cabal +35/−0
- src/Network/Wai/Middleware/Servant/Options.hs +57/−0
+ LICENSE view
@@ -0,0 +1,21 @@+Copyright 2017 Lyndon Maydwell++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.++
+ README.md view
@@ -0,0 +1,37 @@+## Servant-Options++This library provides a middleware that returns `HTTP OPTIONS` responses for+valid application routes defined by your (Proxy :: Proxy api).+It is especially useful when trying to write an API that can be used+in a cross-origin capacity, as browsers will send "pre-flight" checks+by requesting OPTIONS for routes that are about to be called.++Before:++++After:++++Usage:++ module MyApp where++ import App+ import Servant+ import Network.Wai.Middleware.Cors+ import Network.Wai.Middleware.Servant.Options++ app :: Application+ app = logStdoutDev+ $ cors (const $ Just policy)+ $ provideOptions apiProxy+ $ serve apiProxy apiServer+ where+ policy = simpleCorsResourcePolicy+ { corsRequestHeaders = [ "content-type" ] }++## See Also++* <https://github.com/haskell-servant/servant/issues/278>
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ servant-options.cabal view
@@ -0,0 +1,35 @@+-- This file has been generated from package.yaml by hpack version 0.15.0.+--+-- see: https://github.com/sol/hpack++name: servant-options+version: 0.1.0.0+synopsis: Provide responses to OPTIONS requests for Servant applications.+description: Provide responses to OPTIONS requests for Servant applications.+category: Web+homepage: https://github.com/sordina/servant-options+author: Lyndon Maydwell+maintainer: maydwell@gmail.com+copyright: 2017 Lyndon Maydwell+license: MIT+license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10++extra-source-files:+ README.md++library+ hs-source-dirs:+ src+ build-depends:+ base >= 4.7 && < 5+ , servant-server+ , servant-foreign+ , wai+ , bytestring+ , text+ , http-types+ exposed-modules:+ Network.Wai.Middleware.Servant.Options+ default-language: Haskell2010
+ src/Network/Wai/Middleware/Servant/Options.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- A middleware to respond to Options requests for a servant app+-- very helpful when trying to deal with pre-flight CORS requests.+--+module Network.Wai.Middleware.Servant.Options (provideOptions) where++import Servant+import Servant.Foreign+import Network.Wai+import Data.Text hiding (null, zipWith, length)+import Network.HTTP.Types.Method+import Data.Maybe+import Data.List (nub)+import Network.HTTP.Types+import qualified Data.ByteString as B++provideOptions :: (GenerateList NoContent (Foreign NoContent api), HasForeign NoTypes NoContent api)+ => Proxy api -> Middleware+provideOptions apiproxy app req cb+ | rmeth == "OPTIONS" = optional cb prior pinfo mlist+ | otherwise = prior+ where+ rmeth = requestMethod req :: Method+ pinfo = pathInfo req :: [ Text ]+ mlist = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy NoContent) apiproxy+ prior = app req cb++optional :: (Response -> r) -> r -> [Text] -> [Req NoContent] -> r+optional cb prior ts rs+ | null methods = prior+ | otherwise = cb (buildResponse methods)+ where+ methods = mapMaybe (getMethod ts) rs++getMethod :: [Text] -> Req NoContent -> Maybe Method+getMethod rs ps+ | sameLength && matchingSegments = Just (_reqMethod ps)+ | otherwise = Nothing+ where+ pattern = _path $ _reqUrl ps+ sameLength = length rs == length pattern+ matchingSegments = and $ zipWith matchSegment rs pattern++matchSegment :: Text -> Segment NoContent -> Bool+matchSegment a (Segment (Static (PathSegment b)) ) | a /= b = False+matchSegment _ _ = True++buildResponse :: [Method] -> Response+buildResponse ms = responseBuilder s h mempty+ where+ s = Status 200 "OK"+ m = B.intercalate ", " ("OPTIONS" : nub ms)+ h = [ ("Allow", m) ]+