Networkモジュールを使ってGET

Haskell でソケット通信をやってみる - yasuabe blog


このエントリを見て、簡単そうだったので遊んでみました。
httpでGETをするだけですが、こんな感じで書けました。

import Network
import System.IO

getCommand :: String -> IO String
getCommand hostName = withSocketsDo $ do
        hSetBuffering stdout NoBuffering
        soc <- connectTo hostName (PortNumber 80)
        hSetBuffering soc LineBuffering
        hPutStr soc $ "GET /index.html HTTP/1.1\nHost:" ++ hostName ++ "\n\n"
        result <- hGetContents soc
        length result `seq` hClose soc
        hClose soc
        return result

main = getCommand "www.boost.org" >>= putStrLn
HTTP/1.1 200 OK

Date: Wed, 04 Jan 2012 09:05:47 GMT

Server: Apache/2.0.52 (Red Hat)

Accept-Ranges: bytes

Transfer-Encoding: chunked

Content-Type: text/html



38b

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
  <meta name="generator" content=
  "HTML Tidy for Windows (vers 1st November 2003), see www.w3.org" />

  <title>Boost C++ Libraries</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <link rel="icon" href="/favicon.ico" type="image/ico" />
  <link rel="stylesheet" type="text/css" href="/style-v2/section-welcome.css" />
  <!--[if IE 7]> <style type="text/css"> body { behavior: url(/style-v2/csshover3.htc); } </style> <![endif]-->
  <meta name="google-site-verification" content="mpr2HgFpodnbF_8fv4qXd9roIClVwtX3C-Kd3F6r61w" />
</head>

<!--
Note: Editing website content is documented at:
http://www.boost.org/development/website_updating.html
-->
...

いろいろ試行錯誤しました。
まず、名前解決はどうやるのかを調べたら、connectToの中でやってたので、URLを直指定でOKでした。
それと、受信したデータをどうやって関数の戻りにするかで悩みました。hGetContentsで受信したデータは遅延評価されてるので、接続を閉じてしまうとうまくデータがとれなくなってしまいます。
なので、hGetContentsしたデータにlength関数をかませてeofまで評価して、それから接続を閉じています。
それと、ここではエラー処理を書いていませんが、接続に失敗したらconnectToは例外を投げます。catchしましょう。


日本語はまだ表示できていません。


参考:
Network - Hackage
Network.Socket - Hackage
haskell hostname resolve - 今日も明日もググったー
hGetContents と hClose - zknxの日記
Haskell でネットワーク - zyxwvの日記


追記 2012/01/05 15:11:
shelarcyさんからアドバイスいただいたので、deepseqで書き換えてみました。




import Network
import System.IO
import Control.DeepSeq

getCommand :: String -> IO String
getCommand hostName = withSocketsDo $ do
        hSetBuffering stdout NoBuffering
        soc <- connectTo hostName (PortNumber 80)
        hSetBuffering soc LineBuffering
        hPutStr soc $ "GET /index.html HTTP/1.1\nHost:" ++ hostName ++ "\n\n"
        result <- hGetContents soc
        result `deepseq` hClose soc
        return result

main = getCommand "www.boost.org" >>= putStrLn