Lucu 0.6 → 0.7
raw patch · 6 files changed
+71/−51 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Network.HTTP.Lucu.Resource: fdName :: FormData -> String
- Network.HTTP.Lucu.Resource: FormData :: String -> Maybe String -> ByteString -> FormData
+ Network.HTTP.Lucu.Resource: FormData :: Maybe String -> ByteString -> FormData
- Network.HTTP.Lucu.Resource: getQueryForm :: Resource [FormData]
+ Network.HTTP.Lucu.Resource: getQueryForm :: Resource [(String, FormData)]
- Network.HTTP.Lucu.Resource: inputForm :: Int -> Resource [FormData]
+ Network.HTTP.Lucu.Resource: inputForm :: Int -> Resource [(String, FormData)]
Files
- Lucu.cabal +16/−10
- NEWS +11/−0
- Network/HTTP/Lucu.hs +5/−4
- Network/HTTP/Lucu/MultipartForm.hs +13/−16
- Network/HTTP/Lucu/Resource.hs +23/−17
- examples/Multipart.hs +3/−4
Lucu.cabal view
@@ -2,13 +2,13 @@ Synopsis: HTTP Daemonic Library Description: Lucu is an HTTP daemonic library. It can be embedded in any- Haskell program and runs in an independent thread.- Lucu is not a replacement for Apache. It is intended to be- used to create an efficient web-based application without- messing around FastCGI. It is also intended to be run behind a- reverse-proxy so it doesn't have some facilities like logging,- client filtering or such like.-Version: 0.6+ Haskell program and runs in an independent thread. Lucu is+ not a replacement for Apache or lighttpd. It is intended to be+ used to create an efficient web-based RESTful application+ without messing around FastCGI. It is also intended to be run+ behind a reverse-proxy so it doesn't have some facilities like+ logging, client filtering or such like.+Version: 0.7 License: PublicDomain License-File: COPYING Author: PHO <pho at cielonegro dot org>@@ -96,8 +96,12 @@ Network.HTTP.Lucu.SocketLike Extensions:- BangPatterns, DeriveDataTypeable, FlexibleContexts,- FlexibleInstances, ScopedTypeVariables, TypeFamilies,+ BangPatterns+ DeriveDataTypeable+ FlexibleContexts+ FlexibleInstances+ ScopedTypeVariables+ TypeFamilies UnboxedTuples ghc-options:@@ -113,7 +117,9 @@ Main-Is: ImplantFile.hs Extensions:- BangPatterns, ScopedTypeVariables, UnboxedTuples+ BangPatterns+ ScopedTypeVariables+ UnboxedTuples ghc-options: -Wall
NEWS view
@@ -1,3 +1,14 @@+Changes from 0.6 to 0.7+-----------------------+* Network.HTTP.Lucu.Resource: (Suggested by Voker57)++ - getQueryForm and inputForm now return [(name :: String,+ FormData)] instead of [FormData] to ease field lookup by+ name. The reason why it's not 'Map String FormData' is that+ there is a possibility where multiple fields have the same name.++ - Removed field fdName from FormData type as it's now redundant.+ Changes from 0.5 to 0.6 ----------------------- * New dependency: time-http == 0.1.*
Network/HTTP/Lucu.hs view
@@ -16,10 +16,11 @@ -- [/SSL connections/] Lucu can handle HTTP connections over SSL -- layer. ----- Lucu is not a replacement for Apache. It is intended to be used to--- create an efficient web-based application without messing around--- FastCGI. It is also intended to be run behind a reverse-proxy so it--- doesn't have the following (otherwise essential) facilities:+-- Lucu is not a replacement for Apache or lighttpd. It is intended to+-- be used to create an efficient web-based RESTful application+-- without messing around FastCGI. It is also intended to be run+-- behind a reverse-proxy so it doesn't have the following (otherwise+-- essential) facilities: -- -- [/Logging/] Lucu doesn't log any requests from any clients. --
Network/HTTP/Lucu/MultipartForm.hs view
@@ -18,12 +18,11 @@ data Part = Part Headers L8.ByteString --- |This data type represents a form entry name, form value and--- possibly an uploaded file name.+-- |This data type represents a form value and possibly an uploaded+-- file name. data FormData = FormData {- fdName :: String- , fdFileName :: Maybe String+ fdFileName :: Maybe String , fdContent :: L8.ByteString } @@ -50,7 +49,7 @@ value -multipartFormP :: String -> Parser [FormData]+multipartFormP :: String -> Parser [(String, FormData)] multipartFormP boundary = do parts <- many (partP boundary) _ <- string "--"@@ -58,7 +57,7 @@ _ <- string "--" _ <- crlf eof- return $ map partToFormData parts+ return $ map partToFormPair parts partP :: String -> Parser Part@@ -82,17 +81,15 @@ return body -partToFormData :: Part -> FormData-partToFormData part@(Part _ body)+partToFormPair :: Part -> (String, FormData)+partToFormPair part@(Part _ body) = let name = partName part- fName = partFileName part- in- FormData {- fdName = name- , fdFileName = fName- , fdContent = body- }-+ fname = partFileName part+ fd = FormData {+ fdFileName = fname+ , fdContent = body+ }+ in (name, fd) partName :: Part -> String partName = getName' . getContDispoFormData
Network/HTTP/Lucu/Resource.hs view
@@ -301,20 +301,26 @@ -- rsrcPath の長さの分だけ削除すれば良い。 return $! drop (length rsrcPath) reqPath --- | Assume the query part of request URI as--- application\/x-www-form-urlencoded, and parse it. This action--- doesn't parse the request body. See 'inputForm'.-getQueryForm :: Resource [FormData]-getQueryForm = do uri <- getRequestURI- return $! map pairToFormData $ parseWWWFormURLEncoded $ snd $ splitAt 1 $ uriQuery uri+-- |Assume the query part of request URI as+-- application\/x-www-form-urlencoded, and parse it to pairs of+-- @(name, formData)@. This action doesn't parse the request body. See+-- 'inputForm'.+getQueryForm :: Resource [(String, FormData)]+getQueryForm = liftM parse' getRequestURI+ where+ parse' = map toPairWithFormData .+ parseWWWFormURLEncoded .+ snd .+ splitAt 1 .+ uriQuery -pairToFormData :: (String, String) -> FormData-pairToFormData (name, value)- = FormData {- fdName = name- , fdFileName = Nothing- , fdContent = L8.pack value- }+toPairWithFormData :: (String, String) -> (String, FormData)+toPairWithFormData (name, value)+ = let fd = FormData {+ fdFileName = Nothing+ , fdContent = L8.pack value+ }+ in (name, fd) -- |Get a value of given request header. Comparison of header name is -- case-insensitive. Note that this action is not intended to be used@@ -726,7 +732,7 @@ -- makes 'Resource' abort with status \"415 Unsupported Media -- Type\". If the request has no \"Content-Type\", it aborts with -- \"400 Bad Request\".-inputForm :: Int -> Resource [FormData]+inputForm :: Int -> Resource [(String, FormData)] inputForm limit = limit `seq` do cTypeM <- getContentType@@ -742,8 +748,7 @@ ++ show cType) where readWWWFormURLEncoded- = do src <- input limit- return $ map pairToFormData $ parseWWWFormURLEncoded src+ = liftM (map toPairWithFormData . parseWWWFormURLEncoded) (input limit) readMultipartFormData params = do case find ((== "boundary") . map toLower . fst) params of@@ -752,7 +757,8 @@ Just (_, boundary) -> do src <- inputLBS limit case parse (multipartFormP boundary) src of- (# Success fdList, _ #) -> return fdList+ (# Success formList, _ #)+ -> return formList (# _, _ #) -> abort BadRequest [] (Just "Unparsable multipart/form-data")
examples/Multipart.hs view
@@ -1,5 +1,4 @@ import qualified Data.ByteString.Lazy.Char8 as L8-import Data.List import Data.Maybe import Network.HTTP.Lucu @@ -28,9 +27,9 @@ , resHead = Nothing , resPost = Just $ do form <- inputForm defaultLimit- let text = fromMaybe L8.empty $ fmap fdContent $ find ((== "text") . fdName) form- file = fromMaybe L8.empty $ fmap fdContent $ find ((== "file") . fdName) form- fileName = fdFileName =<< find ((== "file") . fdName) form+ let text = fromMaybe L8.empty $ fmap fdContent $ lookup "text" form+ file = fromMaybe L8.empty $ fmap fdContent $ lookup "file" form+ fileName = fdFileName =<< lookup "file" form setContentType $ read "text/plain" outputChunk ("You entered \"" ++ L8.unpack text ++ "\".\n") outputChunk ("You uploaded a " ++ show (L8.length file) ++ " bytes long file.\n")