packages feed

multipart 0.1.2 → 0.1.3

raw patch · 4 files changed

+44/−102 lines, 4 filesdep +stringsearchdep ~bytestringPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: stringsearch

Dependency ranges changed: bytestring

API changes (from Hackage documentation)

- Network.Multipart: ctParameters :: ContentType -> [(String, String)]
- Network.Multipart: ctSubtype :: ContentType -> String
- Network.Multipart: ctType :: ContentType -> String
- Network.Multipart: instance Eq BodyPart
- Network.Multipart: instance Eq MultiPart
- Network.Multipart: instance Ord BodyPart
- Network.Multipart: instance Ord MultiPart
- Network.Multipart: instance Show BodyPart
- Network.Multipart: instance Show MultiPart
- Network.Multipart.Header: ctParameters :: ContentType -> [(String, String)]
- Network.Multipart.Header: ctSubtype :: ContentType -> String
- Network.Multipart.Header: ctType :: ContentType -> String
- Network.Multipart.Header: instance Eq ContentDisposition
- Network.Multipart.Header: instance Eq ContentTransferEncoding
- Network.Multipart.Header: instance Eq ContentType
- Network.Multipart.Header: instance Eq HeaderName
- Network.Multipart.Header: instance HeaderValue ContentDisposition
- Network.Multipart.Header: instance HeaderValue ContentTransferEncoding
- Network.Multipart.Header: instance HeaderValue ContentType
- Network.Multipart.Header: instance Ord ContentDisposition
- Network.Multipart.Header: instance Ord ContentTransferEncoding
- Network.Multipart.Header: instance Ord ContentType
- Network.Multipart.Header: instance Ord HeaderName
- Network.Multipart.Header: instance Read ContentDisposition
- Network.Multipart.Header: instance Read ContentTransferEncoding
- Network.Multipart.Header: instance Read ContentType
- Network.Multipart.Header: instance Show ContentDisposition
- Network.Multipart.Header: instance Show ContentTransferEncoding
- Network.Multipart.Header: instance Show ContentType
- Network.Multipart.Header: instance Show HeaderName
+ Network.Multipart: [ctParameters] :: ContentType -> [(String, String)]
+ Network.Multipart: [ctSubtype] :: ContentType -> String
+ Network.Multipart: [ctType] :: ContentType -> String
+ Network.Multipart: instance GHC.Classes.Eq Network.Multipart.BodyPart
+ Network.Multipart: instance GHC.Classes.Eq Network.Multipart.MultiPart
+ Network.Multipart: instance GHC.Classes.Ord Network.Multipart.BodyPart
+ Network.Multipart: instance GHC.Classes.Ord Network.Multipart.MultiPart
+ Network.Multipart: instance GHC.Show.Show Network.Multipart.BodyPart
+ Network.Multipart: instance GHC.Show.Show Network.Multipart.MultiPart
+ Network.Multipart.Header: [ctParameters] :: ContentType -> [(String, String)]
+ Network.Multipart.Header: [ctSubtype] :: ContentType -> String
+ Network.Multipart.Header: [ctType] :: ContentType -> String
+ Network.Multipart.Header: instance GHC.Classes.Eq Network.Multipart.Header.ContentDisposition
+ Network.Multipart.Header: instance GHC.Classes.Eq Network.Multipart.Header.ContentTransferEncoding
+ Network.Multipart.Header: instance GHC.Classes.Eq Network.Multipart.Header.ContentType
+ Network.Multipart.Header: instance GHC.Classes.Eq Network.Multipart.Header.HeaderName
+ Network.Multipart.Header: instance GHC.Classes.Ord Network.Multipart.Header.ContentDisposition
+ Network.Multipart.Header: instance GHC.Classes.Ord Network.Multipart.Header.ContentTransferEncoding
+ Network.Multipart.Header: instance GHC.Classes.Ord Network.Multipart.Header.ContentType
+ Network.Multipart.Header: instance GHC.Classes.Ord Network.Multipart.Header.HeaderName
+ Network.Multipart.Header: instance GHC.Read.Read Network.Multipart.Header.ContentDisposition
+ Network.Multipart.Header: instance GHC.Read.Read Network.Multipart.Header.ContentTransferEncoding
+ Network.Multipart.Header: instance GHC.Read.Read Network.Multipart.Header.ContentType
+ Network.Multipart.Header: instance GHC.Show.Show Network.Multipart.Header.ContentDisposition
+ Network.Multipart.Header: instance GHC.Show.Show Network.Multipart.Header.ContentTransferEncoding
+ Network.Multipart.Header: instance GHC.Show.Show Network.Multipart.Header.ContentType
+ Network.Multipart.Header: instance GHC.Show.Show Network.Multipart.Header.HeaderName
+ Network.Multipart.Header: instance Network.Multipart.Header.HeaderValue Network.Multipart.Header.ContentDisposition
+ Network.Multipart.Header: instance Network.Multipart.Header.HeaderValue Network.Multipart.Header.ContentTransferEncoding
+ Network.Multipart.Header: instance Network.Multipart.Header.HeaderValue Network.Multipart.Header.ContentType

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # Changelog +### 0.1.3++* Improve performance of parsing multipart body. Thanks to Ali Abrar.+ ### 0.1.2  * Expose `Network.Multipart.Header`
Network/Multipart.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- #hide  -----------------------------------------------------------------------------@@ -32,7 +33,6 @@     ) where  import Control.Monad-import Data.Int (Int64) import Data.List (intersperse) import Data.Maybe import System.IO (Handle)@@ -41,6 +41,7 @@  import qualified Data.ByteString.Lazy.Char8 as BS import Data.ByteString.Lazy.Char8 (ByteString)+import Data.ByteString.Lazy.Search (breakOn)  -- -- * Multi-part stuff.@@ -65,14 +66,11 @@                   -> IO MultiPart hGetMultipartBody b = liftM (parseMultipartBody b) . BS.hGetContents -- parseBodyPart :: ByteString -> Maybe BodyPart-parseBodyPart s =-    do-    let (hdr,bdy) = splitAtEmptyLine s-    hs <- parseM pHeaders "<input>" (BS.unpack hdr)-    return $ BodyPart hs bdy+parseBodyPart s = do+  let (hdr,bdy) = splitAtEmptyLine s+  hs <- parseM pHeaders "<input>" (BS.unpack hdr)+  return $ BodyPart hs bdy  showMultipartBody :: String -> MultiPart -> ByteString showMultipartBody b (MultiPart bs) =@@ -84,7 +82,6 @@ showBodyPart (BodyPart hs c) =     unlinesCRLF $ [BS.pack (n++": "++v) | (HeaderName n,v) <- hs] ++ [BS.empty,c] - -- -- * Splitting into multipart parts. --@@ -105,9 +102,9 @@ dropPreamble :: ByteString -- ^ The boundary, without the initial dashes              -> ByteString              -> ByteString-dropPreamble b s | BS.null s = BS.empty-                 | isBoundary b s = dropLine s-                 | otherwise = dropPreamble b (dropLine s)+dropPreamble b s = case splitAtBoundary b s of+  Nothing -> BS.empty+  Just (_,_,v) -> v  -- | Split a string at the first boundary line. splitAtBoundary :: ByteString -- ^ The boundary, without the initial dashes@@ -118,35 +115,23 @@                    --   before and the CRLF (if any) after the boundary line                    --   are not included in any of the strings returned.                    --   Returns 'Nothing' if there is no boundary.-splitAtBoundary b s = spl 0-  where-  spl i = case findCRLF (BS.drop i s) of-              Nothing -> Nothing-              Just (j,l) | isBoundary b s2 -> Just (s1,d,s3)-                         | otherwise -> spl (i+j+l)-                  where-                  s1 = BS.take (i+j) s-                  s2 = BS.drop (i+j+l) s-                  (d,s3) = splitAtCRLF s2---- | Check whether a string starts with two dashes followed by---   the given boundary string.-isBoundary :: ByteString -- ^ The boundary, without the initial dashes-           -> ByteString-           -> Bool-isBoundary b s = startsWithDashes s && b `BS.isPrefixOf` BS.drop 2 s+splitAtBoundary b s =+  let bcrlf = BS.append "\r\n--" b+      (before, t) = breakOn (BS.toStrict bcrlf) s+  in case BS.stripPrefix bcrlf t of+       Nothing -> Nothing+       Just t' ->+         let after = case BS.stripPrefix "\r\n" t' of+               Nothing -> t'+               Just t'' -> t''+         in  Just (before, bcrlf, after)  -- | Check whether a string for which 'isBoundary' returns true --   has two dashes after the boudary string. isClose :: ByteString -- ^ The boundary, without the initial dashes         -> ByteString         -> Bool-isClose b s = startsWithDashes (BS.drop (2+BS.length b) s)---- | Checks whether a string starts with two dashes.-startsWithDashes :: ByteString -> Bool-startsWithDashes s = BS.pack "--" `BS.isPrefixOf` s-+isClose b s = BS.isPrefixOf (BS.append "--" (BS.append b "--")) s  -- -- * RFC 2046 CRLF@@ -158,63 +143,15 @@ unlinesCRLF :: [ByteString] -> ByteString unlinesCRLF = BS.concat . intersperse crlf --- | Drop everything up to and including the first CRLF.-dropLine :: ByteString -> ByteString-dropLine s = snd (splitAtCRLF s)- -- | Split a string at the first empty line. The CRLF (if any) before the --   empty line is included in the first result. The CRLF after the --   empty line is not included in the result. --   If there is no empty line, the entire input is returned --   as the first result. splitAtEmptyLine :: ByteString -> (ByteString, ByteString)-splitAtEmptyLine s | startsWithCRLF s = (BS.empty, dropCRLF s)-                   | otherwise = spl 0-  where-  spl i = case findCRLF (BS.drop i s) of-              Nothing -> (s, BS.empty)-              Just (j,l) | startsWithCRLF s2 -> (s1, dropCRLF s2)-                         | otherwise -> spl (i+j+l)-                where (s1,s2) = BS.splitAt (i+j+l) s---- | Split a string at the first CRLF. The CRLF is not included---   in any of the returned strings.---   If there is no CRLF, the entire input is returned---   as the first string.-splitAtCRLF :: ByteString -- ^ String to split.-            -> (ByteString,ByteString)-splitAtCRLF s = case findCRLF s of-                  Nothing -> (s,BS.empty)-                  Just (i,l) -> (s1, BS.drop l s2)-                      where (s1,s2) = BS.splitAt i s---- | Get the index and length of the first CRLF, if any.-findCRLF :: ByteString -- ^ String to split.-         -> Maybe (Int64,Int64)-findCRLF s =-    case findCRorLF s of-              Nothing -> Nothing-              Just j | BS.null (BS.drop (j+1) s) -> Just (j,1)-              Just j -> case (BS.index s j, BS.index s (j+1)) of-                           ('\n','\r') -> Just (j,2)-                           ('\r','\n') -> Just (j,2)-                           _           -> Just (j,1)--findCRorLF :: ByteString -> Maybe Int64-findCRorLF s = BS.findIndex (\c -> c == '\n' || c == '\r') s--startsWithCRLF :: ByteString -> Bool-startsWithCRLF s = not (BS.null s) && (c == '\n' || c == '\r')-  where c = BS.index s 0---- | Drop an initial CRLF, if any. If the string is empty,---   nothing is done. If the string does not start with CRLF,---   the first character is dropped.-dropCRLF :: ByteString -> ByteString-dropCRLF s | BS.null s = BS.empty-           | BS.null (BS.drop 1 s) = BS.empty-           | c0 == '\n' && c1 == '\r' = BS.drop 2 s-           | c0 == '\r' && c1 == '\n' = BS.drop 2 s-           | otherwise = BS.drop 1 s-  where c0 = BS.index s 0-        c1 = BS.index s 1+splitAtEmptyLine s =+  let blank = "\r\n\r\n"+      (before, after) = breakOn (BS.toStrict blank) s+  in case BS.stripPrefix blank after of+       Nothing -> (before, after)+       Just after' -> (BS.append before "\r\n", after')
Network/Multipart/Header.hs view
@@ -46,7 +46,7 @@ import Control.Monad import Data.Char import Data.List-import Data.Monoid+import qualified Data.Monoid as M  import Text.ParserCombinators.Parsec @@ -142,7 +142,7 @@ --   See <http://www.ietf.org/rfc/rfc2046.txt> for more --   information about MIME media types. data ContentType =-	ContentType {+    ContentType {                      -- | The top-level media type, the general type                      --   of the data. Common examples are                      --   \"text\", \"image\", \"audio\", \"video\",@@ -165,9 +165,9 @@              && ctParameters x == ctParameters y  instance Ord ContentType where-    x `compare` y = mconcat [ctType x `caseInsensitiveCompare` ctType y,-                             ctSubtype x `caseInsensitiveCompare` ctSubtype y,-                             ctParameters x `compare` ctParameters y]+    x `compare` y = M.mconcat [ctType x `caseInsensitiveCompare` ctType y,+                               ctSubtype x `caseInsensitiveCompare` ctSubtype y,+                               ctParameters x `compare` ctParameters y]  instance HeaderValue ContentType where     parseHeaderValue =@@ -197,7 +197,7 @@ --  data ContentTransferEncoding =-	ContentTransferEncoding String+    ContentTransferEncoding String     deriving (Show, Read, Eq, Ord)  instance HeaderValue ContentTransferEncoding where@@ -215,7 +215,7 @@ --  data ContentDisposition =-	ContentDisposition String [(String, String)]+    ContentDisposition String [(String, String)]     deriving (Show, Read, Eq, Ord)  instance HeaderValue ContentDisposition where@@ -271,9 +271,9 @@  literalString :: Parser String literalString = do _ <- char '\"'-		   str <- many (noneOf "\"\\" <|> quoted_pair)-		   _ <- char '\"'-		   return str+                   str <- many (noneOf "\"\\" <|> quoted_pair)+                   _ <- char '\"'+                   return str  -- No web browsers seem to implement RFC 2046 correctly, -- since they do not escape double quotes and backslashes@@ -306,4 +306,4 @@  quoted_pair :: Parser Char quoted_pair = do _ <- char '\\'-		 p_text+                 p_text
multipart.cabal view
@@ -1,5 +1,5 @@ name:                multipart-version:             0.1.2+version:             0.1.3 synopsis:            HTTP multipart split out of the cgi package description:         HTTP multipart split out of the cgi package copyright:           Bjorn Bringert, Andy Gill, Anders Kaseorg, Ian Lynagh, Erik Meijer, Sven Panne, Jeremy Shaw@@ -29,5 +29,6 @@     Network.Multipart.Header   build-depends:       base >= 3 && < 5-    , bytestring+    , bytestring >= 0.10.8.0 && < 0.11     , parsec >= 2.0+    , stringsearch