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

ここでは URICookie は伏せてあるけど、本来のコードではもちろん適切な文字列が入っている。

で、これを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 を同時に使うと何か挙動不審だなぁ」と思っていたことがいくらか解決されて、ほっとした。