http-api-data 0.6.3 → 0.7
raw patch · 6 files changed
+26/−32 lines, 6 filesdep −unordered-containersdep ~basedep ~containers
Dependencies removed: unordered-containers
Dependency ranges changed: base, containers
Files
- CHANGELOG.md +5/−0
- http-api-data.cabal +2/−3
- src/Web/Internal/FormUrlEncoded.hs +10/−19
- src/Web/Internal/HttpApiData.hs +1/−2
- test/Web/Internal/FormUrlEncodedSpec.hs +5/−5
- test/Web/Internal/TestInstances.hs +3/−3
CHANGELOG.md view
@@ -1,3 +1,8 @@+0.7++* Change Form representation to use Map (from `containers`) instead of `HashMap`.+ This prevents possible HashDOS attacks.+ 0.6.3 * Add To/FromForm instances for () and Void
http-api-data.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: http-api-data-version: 0.6.3+version: 0.7 synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters. category: Web@@ -56,7 +56,6 @@ , text-iso8601 >= 0.1.1 && < 0.2 , tagged >= 0.8.8 && < 0.9 , time-compat >= 1.9.5 && < 1.10- , unordered-containers >= 0.2.20 && < 0.3 , uuid-types >= 1.0.6 && < 1.1 if flag(use-text-show)@@ -90,7 +89,7 @@ , http-api-data , text , time-compat- , unordered-containers+ , containers , uuid-types build-depends: hspec >= 2.7.1 && <2.12
src/Web/Internal/FormUrlEncoded.hs view
@@ -25,14 +25,11 @@ import qualified Data.Foldable as F import Data.Functor.Identity (Identity(Identity)) import Data.Hashable (Hashable)-import Data.HashMap.Strict (HashMap)-import qualified Data.HashMap.Strict as HashMap+import qualified Data.Map.Strict as Map import Data.Int (Int16, Int32, Int64, Int8) import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap import Data.List (intersperse, sortBy)-import Data.Map (Map)-import qualified Data.Map as Map import Data.Monoid (All (..), Any (..), Dual (..), Product (..), Sum (..)) import Data.Ord (comparing)@@ -205,7 +202,7 @@ -- | The contents of a form, not yet URL-encoded. -- -- 'Form' can be URL-encoded with 'urlEncodeForm' and URL-decoded with 'urlDecodeForm'.-newtype Form = Form { unForm :: HashMap Text [Text] }+newtype Form = Form { unForm :: Map.Map Text [Text] } deriving (Eq, Read, Generic, Semigroup, Monoid) instance Show Form where@@ -216,8 +213,8 @@ -- For a stable conversion use 'toListStable'. instance IsList Form where type Item Form = (Text, Text)- fromList = Form . HashMap.fromListWith (flip (<>)) . fmap (\(k, v) -> (k, [v]))- toList = concatMap (\(k, vs) -> map ((,) k) vs) . HashMap.toList . unForm+ fromList = Form . Map.fromListWith (flip (<>)) . fmap (\(k, v) -> (k, [v]))+ toList = concatMap (\(k, vs) -> map ((,) k) vs) . Map.toList . unForm -- | A stable version of 'toList'. toListStable :: Form -> [(Text, Text)]@@ -275,17 +272,14 @@ -- | -- -- @since 0.6.3-instance ToForm () where toForm _ = Form HashMap.empty+instance ToForm () where toForm _ = Form Map.empty instance (ToFormKey k, ToHttpApiData v) => ToForm [(k, v)] where toForm = fromList . map (toFormKey *** toQueryParam) -instance (ToFormKey k, ToHttpApiData v) => ToForm (Map k [v]) where+instance (ToFormKey k, ToHttpApiData v) => ToForm (Map.Map k [v]) where toForm = fromEntriesByKey . Map.toList -instance (ToFormKey k, ToHttpApiData v) => ToForm (HashMap k [v]) where- toForm = fromEntriesByKey . HashMap.toList- instance ToHttpApiData v => ToForm (IntMap [v]) where toForm = fromEntriesByKey . IntMap.toList @@ -294,7 +288,7 @@ -- >>> fromEntriesByKey [("name",["Nick"]),("color",["red","blue"])] -- fromList [("color","red"),("color","blue"),("name","Nick")] fromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form-fromEntriesByKey = Form . HashMap.fromListWith (<>) . map (toFormKey *** map toQueryParam)+fromEntriesByKey = Form . Map.fromListWith (<>) . map (toFormKey *** map toQueryParam) data Proxy3 a b c = Proxy3 @@ -437,12 +431,9 @@ instance (FromFormKey k, FromHttpApiData v) => FromForm [(k, v)] where fromForm = fmap (concatMap (\(k, vs) -> map ((,) k) vs)) . toEntriesByKey -instance (Ord k, FromFormKey k, FromHttpApiData v) => FromForm (Map k [v]) where+instance (Ord k, Eq k, Hashable k, FromFormKey k, FromHttpApiData v) => FromForm (Map.Map k [v]) where fromForm = fmap (Map.fromListWith (<>)) . toEntriesByKey -instance (Eq k, Hashable k, FromFormKey k, FromHttpApiData v) => FromForm (HashMap k [v]) where- fromForm = fmap (HashMap.fromListWith (<>)) . toEntriesByKey- instance FromHttpApiData v => FromForm (IntMap [v]) where fromForm = fmap (IntMap.fromListWith (<>)) . toEntriesByKey @@ -451,7 +442,7 @@ -- _NOTE:_ this conversion is unstable and may result in different key order -- (but not values). For a stable encoding see 'toEntriesByKeyStable'. toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])]-toEntriesByKey = traverse parseGroup . HashMap.toList . unForm+toEntriesByKey = traverse parseGroup . Map.toList . unForm where parseGroup (k, vs) = (,) <$> parseFormKey k <*> traverse parseQueryParam vs @@ -678,7 +669,7 @@ -- >>> lookupAll "name" [("name", "Oleg"), ("name", "David")] -- ["Oleg","David"] lookupAll :: Text -> Form -> [Text]-lookupAll key = F.concat . HashMap.lookup key . unForm+lookupAll key = F.concat . Map.lookup key . unForm -- | Lookup an optional value for a key. -- Fail if there is more than one value.
src/Web/Internal/HttpApiData.hs view
@@ -28,11 +28,10 @@ import Data.Functor.Identity (Identity(Identity)) import Data.Int (Int16, Int32, Int64, Int8) import Data.Kind (Type)-import qualified Data.Map as Map+import qualified Data.Map.Strict as Map import Data.Monoid (All (..), Any (..), Dual (..), First (..), Last (..), Product (..), Sum (..))-import Data.Semigroup (Semigroup (..)) import qualified Data.Semigroup as Semi import Data.Tagged (Tagged (..)) import Data.Text (Text)
test/Web/Internal/FormUrlEncodedSpec.hs view
@@ -4,7 +4,7 @@ import Control.Monad ((<=<)) import qualified Data.ByteString.Lazy.Char8 as BSL-import qualified Data.HashMap.Strict as HashMap+import qualified Data.Map.Strict as Map import Data.Text (Text, unpack) import Test.Hspec import Test.QuickCheck@@ -27,13 +27,13 @@ it "contains the record names" $ property $ \(x :: SimpleRec) -> do let f = unForm $ toForm x- HashMap.member "rec1" f `shouldBe` True- HashMap.member "rec2" f `shouldBe` True+ Map.member "rec1" f `shouldBe` True+ Map.member "rec2" f `shouldBe` True it "contains the correct record values" $ property $ \(x :: SimpleRec) -> do let f = unForm $ toForm x- HashMap.lookup "rec1" f `shouldBe` Just [rec1 x]- (parseQueryParams <$> HashMap.lookup "rec2" f) `shouldBe` Just (Right [rec2 x])+ Map.lookup "rec1" f `shouldBe` Just [rec1 x]+ (parseQueryParams <$> Map.lookup "rec2" f) `shouldBe` Just (Right [rec2 x]) context "FromForm" $ do
test/Web/Internal/TestInstances.hs view
@@ -8,9 +8,9 @@ , NoEmptyKeyForm(..) ) where -import Control.Applicative+import Control.Applicative -- for ghc < 9.6 import Data.Char-import qualified Data.HashMap.Strict as HashMap+import qualified Data.Map.Strict as Map import qualified Data.Text as T import Data.Time.Compat import GHC.Exts (fromList)@@ -63,4 +63,4 @@ instance Arbitrary NoEmptyKeyForm where arbitrary = NoEmptyKeyForm . removeEmptyKeys <$> arbitrary where- removeEmptyKeys (Form m) = Form (HashMap.delete "" m)+ removeEmptyKeys (Form m) = Form (Map.delete "" m)