)
where
-import Data.ByteString.Base (ByteString, toForeignPtr, w2c, inlinePerformIO)
-import qualified Data.ByteString.Char8 as C8
+import qualified Data.ByteString as Strict (ByteString)
+import Data.ByteString.Internal (toForeignPtr, w2c, inlinePerformIO)
+import qualified Data.ByteString.Char8 as C8 hiding (ByteString)
import Data.Char
import Data.List
import Data.Map (Map)
import qualified Data.Map as M
+import Data.Ord
import Data.Word
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
+import Network.HTTP.Lucu.HandleLike
import Network.HTTP.Lucu.Parser
import Network.HTTP.Lucu.Parser.Http
import Network.HTTP.Lucu.Utils
-import System.IO
-type Headers = Map NCBS ByteString
-newtype NCBS = NCBS ByteString
+type Headers = Map NCBS Strict.ByteString
+newtype NCBS = NCBS Strict.ByteString
-toNCBS :: ByteString -> NCBS
+toNCBS :: Strict.ByteString -> NCBS
toNCBS = NCBS
{-# INLINE toNCBS #-}
-fromNCBS :: NCBS -> ByteString
+fromNCBS :: NCBS -> Strict.ByteString
fromNCBS (NCBS x) = x
{-# INLINE fromNCBS #-}
instance Show NCBS where
show (NCBS x) = show x
-noCaseCmp :: ByteString -> ByteString -> Ordering
+noCaseCmp :: Strict.ByteString -> Strict.ByteString -> Ordering
noCaseCmp a b = a `seq` b `seq`
toForeignPtr a `cmp` toForeignPtr b
where
noCaseCmp' p1 l1 p2 l2
| p1 `seq` l1 `seq` p2 `seq` l2 `seq` False = undefined
| l1 == 0 && l2 == 0 = return EQ
- | l1 == 0 && l1 /= 0 = return LT
- | l1 /= 0 && l2 == 0 = return GT
+ | l1 == 0 = return LT
+ | l2 == 0 = return GT
| otherwise
= do c1 <- peek p1
c2 <- peek p2
- case toLower (w2c c1) `compare` toLower (w2c c2) of
+ case comparing (toLower . w2c) c1 c2 of
EQ -> noCaseCmp' (p1 `plusPtr` 1) (l1 - 1) (p2 `plusPtr` 1) (l2 - 1)
x -> return x
-noCaseEq :: ByteString -> ByteString -> Bool
+noCaseEq :: Strict.ByteString -> Strict.ByteString -> Bool
noCaseEq a b = a `seq` b `seq`
toForeignPtr a `cmp` toForeignPtr b
where
getHeaders :: a -> Headers
setHeaders :: a -> Headers -> a
- getHeader :: ByteString -> a -> Maybe ByteString
+ getHeader :: Strict.ByteString -> a -> Maybe Strict.ByteString
getHeader key a
= key `seq` a `seq`
M.lookup (toNCBS key) (getHeaders a)
- deleteHeader :: ByteString -> a -> a
+ deleteHeader :: Strict.ByteString -> a -> a
deleteHeader key a
= key `seq` a `seq`
setHeaders a $ M.delete (toNCBS key) (getHeaders a)
- setHeader :: ByteString -> ByteString -> a -> a
+ setHeader :: Strict.ByteString -> Strict.ByteString -> a -> a
setHeader key val a
= key `seq` val `seq` a `seq`
setHeaders a $ M.insert (toNCBS key) val (getHeaders a)
emptyHeaders = M.empty
-toHeaders :: [(ByteString, ByteString)] -> Headers
-toHeaders xs = M.fromList [(toNCBS a, b) | (a, b) <- xs]
+toHeaders :: [(Strict.ByteString, Strict.ByteString)] -> Headers
+toHeaders xs = mkHeaders xs M.empty
-fromHeaders :: Headers -> [(ByteString, ByteString)]
+mkHeaders :: [(Strict.ByteString, Strict.ByteString)] -> Headers -> Headers
+mkHeaders [] m = m
+mkHeaders ((key, val):xs) m = mkHeaders xs $
+ case M.lookup (toNCBS key) m of
+ Nothing -> M.insert (toNCBS key) val m
+ Just old -> M.insert (toNCBS key) (merge old val) m
+ where
+ merge :: Strict.ByteString -> Strict.ByteString -> Strict.ByteString
+ -- カンマ區切りである事を假定する。RFC ではカンマ區切りに出來ない
+ -- ヘッダは複數個あってはならない事になってゐる。
+ merge a b
+ | C8.null a && C8.null b = C8.empty
+ | C8.null a = b
+ | C8.null b = a
+ | otherwise = C8.concat [a, C8.pack ", ", b]
+
+
+fromHeaders :: Headers -> [(Strict.ByteString, Strict.ByteString)]
fromHeaders hs = [(fromNCBS a, b) | (a, b) <- M.toList hs]
-}
headersP :: Parser Headers
headersP = do xs <- many header
- crlf
- return (M.fromList xs)
+ _ <- crlf
+ return $! toHeaders xs
where
- header :: Parser (NCBS, ByteString)
+ header :: Parser (Strict.ByteString, Strict.ByteString)
header = do name <- token
- char ':'
+ _ <- char ':'
-- FIXME: これは多少インチキだが、RFC 2616 のこの部分
-- の記述はひどく曖昧であり、この動作が本當に間違って
-- ゐるのかどうかも良く分からない。例へば
-- のか?直勸的には駄目さうに思へるが、そんな記述は見
-- 付からない。
contents <- many (lws <|> many1 text)
- crlf
+ _ <- crlf
let value = foldr (++) "" contents
norm = normalize value
- return (toNCBS $ C8.pack name, C8.pack norm)
+ return (C8.pack name, C8.pack norm)
normalize :: String -> String
normalize = trimBody . trim isWhiteSpace
- trimBody = foldr (++) []
+ trimBody = concat
. map (\ s -> if head s == ' ' then
" "
else
else c)
-hPutHeaders :: Handle -> Headers -> IO ()
+hPutHeaders :: HandleLike h => h -> Headers -> IO ()
hPutHeaders h hds
= h `seq` hds `seq`
- mapM_ putH (M.toList hds) >> hPutStr h "\r\n"
+ mapM_ putH (M.toList hds) >> hPutBS h (C8.pack "\r\n")
where
- putH :: (NCBS, ByteString) -> IO ()
+ putH :: (NCBS, Strict.ByteString) -> IO ()
putH (name, value)
= name `seq` value `seq`
- do C8.hPutStr h (fromNCBS name)
- C8.hPutStr h (C8.pack ": ")
- C8.hPutStr h value
- C8.hPutStr h (C8.pack "\r\n")
+ do hPutBS h (fromNCBS name)
+ hPutBS h (C8.pack ": ")
+ hPutBS h value
+ hPutBS h (C8.pack "\r\n")