5 -- |Utility functions used internally in the Lucu httpd. These
6 -- functions may be useful too for something else.
7 module Network.HTTP.Lucu.Utils
10 , parseWWWFormURLEncoded
15 import Blaze.ByteString.Builder.ByteString as B
16 import Blaze.Text.Int as BT
18 import Data.Ascii (Ascii, AsciiBuilder)
19 import qualified Data.Ascii as A
20 import Data.ByteString (ByteString)
21 import qualified Data.ByteString.Char8 as BS
22 import Data.List hiding (last)
23 import Data.Monoid.Unicode
25 import Prelude hiding (last)
26 import Prelude.Unicode
28 -- |> splitBy (== ':') "ab:c:def"
29 -- > ==> ["ab", "c", "def"]
30 splitBy ∷ (a → Bool) → [a] → [[a]]
31 {-# INLINEABLE splitBy #-}
33 = case break isSep src of
35 (first, _sep:rest) → first : splitBy isSep rest
41 -- > ==> "\"ab\\\"c\""
42 quoteStr ∷ Ascii → AsciiBuilder
43 quoteStr str = A.toAsciiBuilder "\"" ⊕
44 go (A.toByteString str) (∅) ⊕
47 go ∷ BS.ByteString → AsciiBuilder → AsciiBuilder
49 = case BS.break (≡ '"') bs of
51 | BS.null y → ab ⊕ b2ab x
52 | otherwise → go (BS.tail y) (ab ⊕ b2ab x
53 ⊕ A.toAsciiBuilder "\\\"")
55 b2ab ∷ BS.ByteString → AsciiBuilder
56 b2ab = A.toAsciiBuilder ∘ A.unsafeFromByteString
58 -- |> parseWWWFormURLEncoded "aaa=bbb&ccc=ddd"
59 -- > ==> [("aaa", "bbb"), ("ccc", "ddd")]
60 parseWWWFormURLEncoded ∷ Ascii → [(ByteString, ByteString)]
61 parseWWWFormURLEncoded src
62 -- THINKME: We could gain some performance by using attoparsec
65 | otherwise = do pairStr ← splitBy (\ c → c ≡ ';' ∨ c ≡ '&') (A.toString src)
66 let (key, value) = break (≡ '=') pairStr
68 , unescape $ case value of
73 unescape ∷ String → ByteString
74 unescape = BS.pack ∘ unEscapeString ∘ map plusToSpace
76 plusToSpace ∷ Char → Char
80 -- |> splitPathInfo "http://example.com/foo/bar"
81 -- > ==> ["foo", "bar"]
82 splitPathInfo ∷ URI → [ByteString]
84 = let reqPathStr = uriPath uri
85 reqPath = [unEscapeString x | x ← splitBy (≡ '/') reqPathStr, (¬) (null x)]
91 show3 ∷ Integral n ⇒ n → AsciiBuilder
92 {-# INLINEABLE show3 #-}
93 show3 = A.unsafeFromBuilder ∘ go
95 go i | i ≥ 0 ∧ i < 10 = B.fromByteString "00" ⊕ BT.digit i
96 | i ≥ 0 ∧ i < 100 = B.fromByteString "0" ⊕ BT.integral i
97 | i ≥ 0 ∧ i < 1000 = BT.integral i
98 | otherwise = error ("show3: the integer i must satisfy 0 <= i < 1000: " ⧺ show i)
99 -- FIXME: Drop this function as soon as possible, to eliminate the
100 -- dependency on blaze-textual.