Network.HTTP と Codec.Text.IConv
原因を十分に特定できていないので、どこが問題かはっきりしないが、Network.HTTP や Codec.Text.IConv を使っていたら、次のような問題にぶち当たった。とりあえず、コード:
module Friends where import Prelude hiding (print, putStr, putStrLn, getContents) import System.IO.UTF8 import Data.Maybe import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy.UTF8 as BS8 import qualified Codec.Text.IConv as IC import Network import Network.HTTP import Network.URI import Network.Browser import Network.TCP get uri cookie encoding = do bs <- browse $ do addCookie cookie (_, rsp)<- request (req :: Request BS.ByteString) return $ rspBody rsp return $ BS8.toString $ IC.convert encoding "UTF-8" bs where req = defaultGETRequest_ $ fromJust $ parseURI uri test = do s <- get uri cookie "EUC-JP" putStrLn s where uri = "http://***" cookie = MkCookie "***" "***" "***" (Just "/") Nothing Nothing
ここでは URI や Cookie は伏せてあるけど、本来のコードではもちろん適切な文字列が入っている。
で、これをghciでロードして、testを一回実行するとちゃんと意図どおりEUC-JPのページを変換して出力してくれるのだが、もう一度testを実行しようとすると、二回目はだめ。なぜか「Exception: Codec.Text.IConv: invalid input sequence at byte offset 10853」というエラーを出す。
もう一回、ghciを起動しなおすと、一回目は大丈夫だが、やはり二回目はだめ。
試行錯誤した結果、次のどちらかのコードであれば、二回目以降も動く:
get uri cookie encoding = do bs <- browse $ do addCookie cookie (_, rsp)<- request (req :: Request BS.ByteString) return $ rspBody rsp return $ BS8.toString $ IC.convert encoding "UTF-8" $ BS.pack $ BS.unpack bs where req = defaultGETRequest_ $ fromJust $ parseURI uri
get uri cookie encoding = do bs <- browse $ do addCookie cookie (_, rsp)<- request (req :: Request BS.ByteString) return $ rspBody rsp BS.writeFile "./tmp" bs bs' <- BS.readFile "./tmp" return $ BS8.toString $ IC.convert encoding "UTF-8" bs' where req = defaultGETRequest_ $ fromJust $ parseURI uri
症状から考えるに、Network.HTTP での領域確保になにか問題がありそう。例えば、Network.HTTP で使っている内部バッファをIConvが上書きしてしまっていて、Network.HTTP がそれを再度使うときにきちんと再初期化していないとか。
なんにせよ、「Network.HTTP と Codec.Text.IConv を同時に使うと何か挙動不審だなぁ」と思っていたことがいくらか解決されて、ほっとした。