packages feed

HTTP 3001.0.3 → 3001.0.4

raw patch · 11 files changed

+572/−522 lines, 11 filesdep ~networkPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: network

API changes (from Hackage documentation)

- Network.HTTP: HdrAccept :: HeaderName
- Network.HTTP: HdrAcceptCharset :: HeaderName
- Network.HTTP: HdrAcceptEncoding :: HeaderName
- Network.HTTP: HdrAcceptLanguage :: HeaderName
- Network.HTTP: HdrAge :: HeaderName
- Network.HTTP: HdrAllow :: HeaderName
- Network.HTTP: HdrAuthorization :: HeaderName
- Network.HTTP: HdrCacheControl :: HeaderName
- Network.HTTP: HdrConnection :: HeaderName
- Network.HTTP: HdrContentBase :: HeaderName
- Network.HTTP: HdrContentEncoding :: HeaderName
- Network.HTTP: HdrContentLanguage :: HeaderName
- Network.HTTP: HdrContentLength :: HeaderName
- Network.HTTP: HdrContentLocation :: HeaderName
- Network.HTTP: HdrContentMD5 :: HeaderName
- Network.HTTP: HdrContentRange :: HeaderName
- Network.HTTP: HdrContentTransferEncoding :: HeaderName
- Network.HTTP: HdrContentType :: HeaderName
- Network.HTTP: HdrCookie :: HeaderName
- Network.HTTP: HdrCustom :: String -> HeaderName
- Network.HTTP: HdrDate :: HeaderName
- Network.HTTP: HdrETag :: HeaderName
- Network.HTTP: HdrExpect :: HeaderName
- Network.HTTP: HdrExpires :: HeaderName
- Network.HTTP: HdrFrom :: HeaderName
- Network.HTTP: HdrHost :: HeaderName
- Network.HTTP: HdrIfMatch :: HeaderName
- Network.HTTP: HdrIfModifiedSince :: HeaderName
- Network.HTTP: HdrIfNoneMatch :: HeaderName
- Network.HTTP: HdrIfRange :: HeaderName
- Network.HTTP: HdrIfUnmodifiedSince :: HeaderName
- Network.HTTP: HdrLastModified :: HeaderName
- Network.HTTP: HdrLocation :: HeaderName
- Network.HTTP: HdrMaxForwards :: HeaderName
- Network.HTTP: HdrPragma :: HeaderName
- Network.HTTP: HdrProxyAuthenticate :: HeaderName
- Network.HTTP: HdrProxyAuthorization :: HeaderName
- Network.HTTP: HdrPublic :: HeaderName
- Network.HTTP: HdrRange :: HeaderName
- Network.HTTP: HdrReferer :: HeaderName
- Network.HTTP: HdrRetryAfter :: HeaderName
- Network.HTTP: HdrServer :: HeaderName
- Network.HTTP: HdrSetCookie :: HeaderName
- Network.HTTP: HdrTransferEncoding :: HeaderName
- Network.HTTP: HdrUpgrade :: HeaderName
- Network.HTTP: HdrUserAgent :: HeaderName
- Network.HTTP: HdrVary :: HeaderName
- Network.HTTP: HdrVia :: HeaderName
- Network.HTTP: HdrWWWAuthenticate :: HeaderName
- Network.HTTP: HdrWarning :: HeaderName
- Network.HTTP: Header :: HeaderName -> String -> Header
- Network.HTTP: class HasHeaders x
- Network.HTTP: data Header
- Network.HTTP: data HeaderName
- Network.HTTP: findHeader :: (HasHeaders a) => HeaderName -> a -> Maybe String
- Network.HTTP: insertHeader :: (HasHeaders a) => HeaderName -> String -> a -> a
- Network.HTTP: insertHeaderIfMissing :: (HasHeaders a) => HeaderName -> String -> a -> a
- Network.HTTP: insertHeaders :: (HasHeaders a) => [Header] -> a -> a
- Network.HTTP: instance Eq HeaderName
- Network.HTTP: instance Show Header
- Network.HTTP: instance Show HeaderName
- Network.HTTP: replaceHeader :: (HasHeaders a) => HeaderName -> String -> a -> a
- Network.HTTP: retrieveHeaders :: (HasHeaders a) => HeaderName -> a -> [Header]
- Network.Stream: data Debug x
- Network.Stream: debugStream :: (Stream a) => String -> a -> IO (Debug a)
- Network.Stream: handleSocketError :: Socket -> Exception -> IO (Result a)
- Network.Stream: instance (Stream x) => Stream (Debug x)
- Network.Stream: instance Stream Socket
- Network.Stream: myrecv :: Socket -> Int -> IO String
- Network.TCP: ConnClosed :: Conn
- Network.TCP: ConnRef :: IORef Conn -> Connection
- Network.TCP: MkConn :: !Socket -> !SockAddr -> !String -> String -> Conn
- Network.TCP: connAddr :: Conn -> !SockAddr
- Network.TCP: connBffr :: Conn -> !String
- Network.TCP: connHost :: Conn -> String
- Network.TCP: connSock :: Conn -> !Socket
- Network.TCP: data Conn
- Network.TCP: getRef :: Connection -> IORef Conn
- Network.TCP: newtype Connection
- Network.TCP: openTCP :: String -> IO Connection
+ Network.HTTP.Headers: HdrAccept :: HeaderName
+ Network.HTTP.Headers: HdrAcceptCharset :: HeaderName
+ Network.HTTP.Headers: HdrAcceptEncoding :: HeaderName
+ Network.HTTP.Headers: HdrAcceptLanguage :: HeaderName
+ Network.HTTP.Headers: HdrAge :: HeaderName
+ Network.HTTP.Headers: HdrAllow :: HeaderName
+ Network.HTTP.Headers: HdrAuthorization :: HeaderName
+ Network.HTTP.Headers: HdrCacheControl :: HeaderName
+ Network.HTTP.Headers: HdrConnection :: HeaderName
+ Network.HTTP.Headers: HdrContentBase :: HeaderName
+ Network.HTTP.Headers: HdrContentEncoding :: HeaderName
+ Network.HTTP.Headers: HdrContentLanguage :: HeaderName
+ Network.HTTP.Headers: HdrContentLength :: HeaderName
+ Network.HTTP.Headers: HdrContentLocation :: HeaderName
+ Network.HTTP.Headers: HdrContentMD5 :: HeaderName
+ Network.HTTP.Headers: HdrContentRange :: HeaderName
+ Network.HTTP.Headers: HdrContentTransferEncoding :: HeaderName
+ Network.HTTP.Headers: HdrContentType :: HeaderName
+ Network.HTTP.Headers: HdrCookie :: HeaderName
+ Network.HTTP.Headers: HdrCustom :: String -> HeaderName
+ Network.HTTP.Headers: HdrDate :: HeaderName
+ Network.HTTP.Headers: HdrETag :: HeaderName
+ Network.HTTP.Headers: HdrExpect :: HeaderName
+ Network.HTTP.Headers: HdrExpires :: HeaderName
+ Network.HTTP.Headers: HdrFrom :: HeaderName
+ Network.HTTP.Headers: HdrHost :: HeaderName
+ Network.HTTP.Headers: HdrIfMatch :: HeaderName
+ Network.HTTP.Headers: HdrIfModifiedSince :: HeaderName
+ Network.HTTP.Headers: HdrIfNoneMatch :: HeaderName
+ Network.HTTP.Headers: HdrIfRange :: HeaderName
+ Network.HTTP.Headers: HdrIfUnmodifiedSince :: HeaderName
+ Network.HTTP.Headers: HdrLastModified :: HeaderName
+ Network.HTTP.Headers: HdrLocation :: HeaderName
+ Network.HTTP.Headers: HdrMaxForwards :: HeaderName
+ Network.HTTP.Headers: HdrPragma :: HeaderName
+ Network.HTTP.Headers: HdrProxyAuthenticate :: HeaderName
+ Network.HTTP.Headers: HdrProxyAuthorization :: HeaderName
+ Network.HTTP.Headers: HdrPublic :: HeaderName
+ Network.HTTP.Headers: HdrRange :: HeaderName
+ Network.HTTP.Headers: HdrReferer :: HeaderName
+ Network.HTTP.Headers: HdrRetryAfter :: HeaderName
+ Network.HTTP.Headers: HdrServer :: HeaderName
+ Network.HTTP.Headers: HdrSetCookie :: HeaderName
+ Network.HTTP.Headers: HdrTransferEncoding :: HeaderName
+ Network.HTTP.Headers: HdrUpgrade :: HeaderName
+ Network.HTTP.Headers: HdrUserAgent :: HeaderName
+ Network.HTTP.Headers: HdrVary :: HeaderName
+ Network.HTTP.Headers: HdrVia :: HeaderName
+ Network.HTTP.Headers: HdrWWWAuthenticate :: HeaderName
+ Network.HTTP.Headers: HdrWarning :: HeaderName
+ Network.HTTP.Headers: Header :: HeaderName -> String -> Header
+ Network.HTTP.Headers: class HasHeaders x
+ Network.HTTP.Headers: data Header
+ Network.HTTP.Headers: data HeaderName
+ Network.HTTP.Headers: findHeader :: (HasHeaders a) => HeaderName -> a -> Maybe String
+ Network.HTTP.Headers: getHeaders :: (HasHeaders x) => x -> [Header]
+ Network.HTTP.Headers: insertHeader :: (HasHeaders a) => HeaderName -> String -> a -> a
+ Network.HTTP.Headers: insertHeaderIfMissing :: (HasHeaders a) => HeaderName -> String -> a -> a
+ Network.HTTP.Headers: insertHeaders :: (HasHeaders a) => [Header] -> a -> a
+ Network.HTTP.Headers: instance Eq HeaderName
+ Network.HTTP.Headers: instance Show Header
+ Network.HTTP.Headers: instance Show HeaderName
+ Network.HTTP.Headers: lookupHeader :: HeaderName -> [Header] -> Maybe String
+ Network.HTTP.Headers: parseHeaders :: [String] -> Result [Header]
+ Network.HTTP.Headers: replaceHeader :: (HasHeaders a) => HeaderName -> String -> a -> a
+ Network.HTTP.Headers: retrieveHeaders :: (HasHeaders a) => HeaderName -> a -> [Header]
+ Network.HTTP.Headers: setHeaders :: (HasHeaders x) => x -> [Header] -> x
+ Network.StreamDebugger: data StreamDebugger x
+ Network.StreamDebugger: debugStream :: (Stream a) => FilePath -> a -> IO (StreamDebugger a)
+ Network.StreamDebugger: instance (Stream x) => Stream (StreamDebugger x)
+ Network.StreamSocket: handleSocketError :: Socket -> Exception -> IO (Result a)
+ Network.StreamSocket: instance Stream Socket
+ Network.StreamSocket: myrecv :: Socket -> Int -> IO String
+ Network.TCP: data Connection
- Network.Stream: bindE :: Either ConnError a -> (a -> Either ConnError b) -> Either ConnError b
+ Network.Stream: bindE :: Result a -> (a -> Result b) -> Result b

Files

HTTP.cabal view
@@ -1,5 +1,5 @@ Name: HTTP-Version: 3001.0.3+Version: 3001.0.4 Cabal-Version: >= 1.2 Build-type: Simple License: BSD3@@ -11,6 +11,7 @@   Copyright (c) 2004, Andre Furtado   Copyright (c) 2004, Ganesh Sittampalam   Copyright (c) 2004-2005, Dominic Steinitz+  Copyright 2007 Robin Bate Boerop Author: Warrick Gray <warrick.gray@hotmail.com> Maintainer: Bjorn Bringert <bjorn@bringert.net> Homepage: http://www.haskell.org/http/@@ -23,8 +24,11 @@ Library   Exposed-modules:                   Network.Stream,+                 Network.StreamDebugger,+                 Network.StreamSocket,                  Network.TCP,                                  Network.HTTP,+                 Network.HTTP.Headers,                  Network.Browser   Other-modules:                  Network.HTTP.Base64,
Network/Browser.hs view
@@ -12,6 +12,10 @@ -----------------------------------------------------------------------------   {-+  Changes by Robin Bate Boerop <robin@bateboerop.name>:+   - Made dependencies explicit in import statements.+   - Added type signatures.+   - Imported new StreamDebugger module.    Change Log:    - altered 'closeTCP' to 'close', for consistency with altered HTTP@@ -70,20 +74,32 @@     uriTrimHost ) where +import Network.URI+   ( URI(uriAuthority, uriScheme, uriPath, uriQuery)+   , URIAuth(URIAuth, uriUserInfo, uriPort, uriRegName)+   , parseURI, parseURIReference, relativeTo+   )+import Network.StreamDebugger (debugStream)+import Network.TCP (Connection, isConnectedTo) import Network.HTTP+import qualified Network.HTTP.MD5 as MD5 (hash)+import qualified Network.HTTP.Base64 as Base64 (encode)  import Data.Char (toLower,isAlphaNum,isSpace) import Data.List (isPrefixOf,isSuffixOf,elemIndex,elemIndices)-import Data.Maybe-import Control.Monad (foldM,filterM,liftM,when)+import Data.Maybe (fromMaybe, listToMaybe, catMaybes, fromJust, isJust)+import Control.Monad (foldM, filterM, liftM, when) import Text.ParserCombinators.Parsec-import Network.URI-+   ( Parser, char, many, many1, satisfy, parse, option, try+   , (<|>), spaces, sepBy1+   ) import qualified System.IO+   ( hSetBuffering, hPutStr, stdout, stdin, hGetChar+   , BufferMode(NoBuffering, LineBuffering)+   ) import Data.Word (Word8)-import qualified Network.HTTP.MD5 as MD5-import qualified Network.HTTP.Base64 as Base64 + type Octet = Word8  ------------------------------------------------------------------@@ -932,6 +948,7 @@    +uriAuth :: URI -> URIAuth uriAuth x = case uriAuthority x of               Just ua -> ua               _       -> error ("No uri authority for: "++show x)@@ -956,6 +973,7 @@ ------------------------------------------------------------------  +libUA :: String libUA = "haskell-libwww/0.1"  defaultGETRequest :: URI -> Request
Network/HTTP.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module      :  Network.HTTP--- Copyright   :  (c) Warrick Gray 2002, Bjorn Bringert 2003-2005+-- Copyright   :  (c) Warrick Gray 2002, Bjorn Bringert 2003-2005, 2007 Robin Bate Boerop -- License     :  BSD --  -- Maintainer  :  bjorn@bringert.net@@ -10,6 +10,12 @@ -- -- An easy HTTP interface enjoy. --+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+--      - Made dependencies explicit in import statements.+--      - Removed false dependencies in import statements.+--      - Added missing type signatures.+--      - Moved Header-related code to Network.HTTP.Headers module.+-- -- * Changes by Simon Foster: --      - Split module up into to sepearate Network.[Stream,TCP,HTTP] modules --      - Created functions receiveHTTP and responseHTTP to allow server side interactions@@ -105,15 +111,7 @@     respondHTTP,      -- ** Header Functions-    HasHeaders,-    Header(..),-    HeaderName(..),-    insertHeader,-    insertHeaderIfMissing,-    insertHeaders,-    retrieveHeaders,-    replaceHeader,-    findHeader,+    module Network.HTTP.Headers,      -- ** URL Encoding     urlEncode,@@ -126,40 +124,30 @@ ) where  - ----------------------------------------------------------------- ------------------ Imports -------------------------------------- ----------------------------------------------------------------- -import Control.Exception as Exception---- Networking-import Network (withSocketsDo)-import Network.BSD import Network.URI-import Network.Socket+   ( URI(URI, uriScheme, uriAuthority, uriPath)+   , URIAuth(uriUserInfo, uriRegName, uriPort)+   , parseURIReference+   )+import Network.HTTP.Headers import Network.Stream-import Network.TCP-+import Network.StreamDebugger (debugStream)+import Network.TCP (openTCPPort) --- Util+import Control.Exception as Exception (catch, throw) import Data.Bits ((.&.))-import Data.Char-import Data.List (isPrefixOf,partition,elemIndex)-import Data.Maybe-import Data.Array.MArray-import Data.IORef-import Control.Concurrent-import Control.Monad (when,liftM,guard)-import Control.Monad.ST (ST,stToIO)+import Data.Char (isSpace, intToDigit, digitToInt, ord, chr, toLower)+import Data.List (partition)+import Data.Maybe (listToMaybe, fromMaybe)+import Control.Monad (when, guard) import Numeric (readHex)+import Text.Read.Lex (readDecP) import Text.ParserCombinators.ReadP-import Text.Read.Lex -import System.IO-import System.IO.Error (isEOFError)-import qualified System.IO.Error--import Foreign.C.Error+   ( ReadP, readP_to_S, char, (<++), look, munch )   -- Turn on to enable HTTP traffic logging@@ -180,16 +168,7 @@        reverse . dropspace . reverse . dropspace  --- Split a list into two parts, the delimiter occurs--- at the head of the second list.  Nothing is returned--- when no occurance of the delimiter is found.-split :: Eq a => a -> [a] -> Maybe ([a],[a])-split delim list = case delim `elemIndex` list of-    Nothing -> Nothing-    Just x  -> Just $ splitAt x list-    --+crlf, sp :: String crlf = "\r\n" sp   = " " @@ -236,229 +215,6 @@ orNothing p = fmap Just p <++ return Nothing  ------------------------------------------------------------------------------------ Header Data --------------------------------------------------------------------------------------------------------- | The Header data type pairs header names & values.-data Header = Header HeaderName String---instance Show Header where-    show (Header key value) = show key ++ ": " ++ value ++ crlf----- | HTTP Header Name type:---  Why include this at all?  I have some reasons---   1) prevent spelling errors of header names,---   2) remind everyone of what headers are available,---   3) might speed up searches for specific headers.------  Arguments against:---   1) makes customising header names laborious---   2) increases code volume.----data HeaderName = -                 -- Generic Headers ---                  HdrCacheControl-                | HdrConnection-                | HdrDate-                | HdrPragma-                | HdrTransferEncoding        -                | HdrUpgrade                -                | HdrVia--                -- Request Headers ---                | HdrAccept-                | HdrAcceptCharset-                | HdrAcceptEncoding-                | HdrAcceptLanguage-                | HdrAuthorization-                | HdrCookie-                | HdrExpect-                | HdrFrom-                | HdrHost-                | HdrIfModifiedSince-                | HdrIfMatch-                | HdrIfNoneMatch-                | HdrIfRange-                | HdrIfUnmodifiedSince-                | HdrMaxForwards-                | HdrProxyAuthorization-                | HdrRange-                | HdrReferer-                | HdrUserAgent--                -- Response Headers-                | HdrAge-                | HdrLocation-                | HdrProxyAuthenticate-                | HdrPublic-                | HdrRetryAfter-                | HdrServer-                | HdrSetCookie-                | HdrVary-                | HdrWarning-                | HdrWWWAuthenticate--                -- Entity Headers-                | HdrAllow-                | HdrContentBase-                | HdrContentEncoding-                | HdrContentLanguage-                | HdrContentLength-                | HdrContentLocation-                | HdrContentMD5-                | HdrContentRange-                | HdrContentType-                | HdrETag-                | HdrExpires-                | HdrLastModified--                -- Mime entity headers (for sub-parts)-                | HdrContentTransferEncoding--                -- | Allows for unrecognised or experimental headers.-                | HdrCustom String -- not in header map below.-    deriving(Eq)----- Translation between header names and values,--- good candidate for improvement.-headerMap :: [ (String,HeaderName) ]-headerMap - = [  ("Cache-Control"        ,HdrCacheControl      )-	, ("Connection"           ,HdrConnection        )-	, ("Date"                 ,HdrDate              )    -	, ("Pragma"               ,HdrPragma            )-	, ("Transfer-Encoding"    ,HdrTransferEncoding  )        -	, ("Upgrade"              ,HdrUpgrade           )                -	, ("Via"                  ,HdrVia               )-	, ("Accept"               ,HdrAccept            )-	, ("Accept-Charset"       ,HdrAcceptCharset     )-	, ("Accept-Encoding"      ,HdrAcceptEncoding    )-	, ("Accept-Language"      ,HdrAcceptLanguage    )-	, ("Authorization"        ,HdrAuthorization     )-	, ("From"                 ,HdrFrom              )-	, ("Host"                 ,HdrHost              )-	, ("If-Modified-Since"    ,HdrIfModifiedSince   )-	, ("If-Match"             ,HdrIfMatch           )-	, ("If-None-Match"        ,HdrIfNoneMatch       )-	, ("If-Range"             ,HdrIfRange           ) -	, ("If-Unmodified-Since"  ,HdrIfUnmodifiedSince )-	, ("Max-Forwards"         ,HdrMaxForwards       )-	, ("Proxy-Authorization"  ,HdrProxyAuthorization)-	, ("Range"                ,HdrRange             )   -	, ("Referer"              ,HdrReferer           )-	, ("User-Agent"           ,HdrUserAgent         )-	, ("Age"                  ,HdrAge               )-	, ("Location"             ,HdrLocation          )-	, ("Proxy-Authenticate"   ,HdrProxyAuthenticate )-	, ("Public"               ,HdrPublic            )-	, ("Retry-After"          ,HdrRetryAfter        )-	, ("Server"               ,HdrServer            )-	, ("Vary"                 ,HdrVary              )-	, ("Warning"              ,HdrWarning           )-	, ("WWW-Authenticate"     ,HdrWWWAuthenticate   )-	, ("Allow"                ,HdrAllow             )-	, ("Content-Base"         ,HdrContentBase       )-	, ("Content-Encoding"     ,HdrContentEncoding   )-	, ("Content-Language"     ,HdrContentLanguage   )-	, ("Content-Length"       ,HdrContentLength     )-	, ("Content-Location"     ,HdrContentLocation   )-	, ("Content-MD5"          ,HdrContentMD5        )-	, ("Content-Range"        ,HdrContentRange      )-	, ("Content-Type"         ,HdrContentType       )-	, ("ETag"                 ,HdrETag              )-	, ("Expires"              ,HdrExpires           )-	, ("Last-Modified"        ,HdrLastModified      )-   	, ("Set-Cookie"           ,HdrSetCookie         )-	, ("Cookie"               ,HdrCookie            )-    , ("Expect"               ,HdrExpect            ) ]---instance Show HeaderName where-    show (HdrCustom s) = s-    show x = case filter ((==x).snd) headerMap of-                [] -> error "headerMap incomplete"-                (h:_) -> fst h-------- | This class allows us to write generic header manipulation functions--- for both 'Request' and 'Response' data types.-class HasHeaders x where-    getHeaders :: x -> [Header]-    setHeaders :: x -> [Header] -> x------ Header manipulation functions-insertHeader, replaceHeader, insertHeaderIfMissing-    :: HasHeaders a => HeaderName -> String -> a -> a----- | Inserts a header with the given name and value.--- Allows duplicate header names.-insertHeader name value x = setHeaders x newHeaders-    where-        newHeaders = (Header name value) : getHeaders x----- | Adds the new header only if no previous header shares--- the same name.-insertHeaderIfMissing name value x = setHeaders x (newHeaders $ getHeaders x)-    where-        newHeaders list@(h@(Header n _): rest)-            | n == name  = list-            | otherwise  = h : newHeaders rest-        newHeaders [] = [Header name value]--            ---- | Removes old headers with duplicate name.-replaceHeader name value x = setHeaders x newHeaders-    where-        newHeaders = Header name value : [ x | x@(Header n v) <- getHeaders x, name /= n ]-          ---- | Inserts multiple headers.-insertHeaders :: HasHeaders a => [Header] -> a -> a-insertHeaders hdrs x = setHeaders x (getHeaders x ++ hdrs)----- | Gets a list of headers with a particular 'HeaderName'.-retrieveHeaders :: HasHeaders a => HeaderName -> a -> [Header]-retrieveHeaders name x = filter matchname (getHeaders x)-    where-        matchname (Header n _)  |  n == name  =  True-        matchname _ = False----- | Lookup presence of specific HeaderName in a list of Headers--- Returns the value from the first matching header.-findHeader :: HasHeaders a => HeaderName -> a -> Maybe String-findHeader n x = lookupHeader n (getHeaders x)---- An anomally really:-lookupHeader :: HeaderName -> [Header] -> Maybe String-lookupHeader v (Header n s:t)  |  v == n   =  Just s-                               | otherwise =  lookupHeader v t-lookupHeader _ _  =  Nothing-----{--instance HasHeaders [Header]-...requires -fglasgow-exts, and is not really necessary anyway...--}--------------------------------------------------------------------- ------------------ HTTP Messages -------------------------------- ----------------------------------------------------------------- @@ -474,6 +230,7 @@ data RequestMethod = HEAD | PUT | GET | POST | DELETE | OPTIONS | TRACE     deriving(Show,Eq) +rqMethodMap :: [(String, RequestMethod)] rqMethodMap = [("HEAD",    HEAD), 	       ("PUT",     PUT), 	       ("GET",     GET),@@ -498,7 +255,6 @@   - -- Notice that request body is not included, -- this show function is used to serialise -- a request for the transport link, we send@@ -512,16 +268,10 @@                         then u { uriPath = '/' : uriPath u }                          else u - instance HasHeaders Request where     getHeaders = rqHeaders     setHeaders rq hdrs = rq { rqHeaders=hdrs } ----- type ResponseCode  = (Int,Int,Int) type ResponseData  = (ResponseCode,String,[Header]) type RequestData   = (RequestMethod,URI,[Header])@@ -538,8 +288,6 @@              , rspBody     :: String              }                    -- -- This is an invalid representation of a received response,  -- since we have made the assumption that all responses are HTTP/1.1 instance Show Response where@@ -547,8 +295,6 @@         httpVersion ++ ' ' : map intToDigit [a,b,c] ++ ' ' : reason ++ crlf         ++ foldr (++) [] (map show headers) ++ crlf -- instance HasHeaders Response where     getHeaders = rspHeaders     setHeaders rsp hdrs = rsp { rspHeaders=hdrs }@@ -557,46 +303,6 @@ ------------------ Parsing -------------------------------------- ----------------------------------------------------------------- -parseHeader :: String -> Result Header-parseHeader str =-    case split ':' str of-        Nothing -> Left (ErrorParse $ "Unable to parse header: " ++ str)-        Just (k,v) -> Right $ Header (fn k) (trim $ drop 1 v)-    where-        fn k = case map snd $ filter (match k . fst) headerMap of-                 [] -> (HdrCustom k)-                 (h:_) -> h--        match :: String -> String -> Bool-        match s1 s2 = map toLower s1 == map toLower s2-    --parseHeaders :: [String] -> Result [Header]-parseHeaders = catRslts [] . map (parseHeader . clean) . joinExtended ""-    where-        -- Joins consecutive lines where the second line-        -- begins with ' ' or '\t'.-        joinExtended old (h : t)-            | not (null h) && (head h == ' ' || head h == '\t')-                = joinExtended (old ++ ' ' : tail h) t-            | otherwise = old : joinExtended h t-        joinExtended old [] = [old]--        clean [] = []-        clean (h:t) | h `elem` "\t\r\n" = ' ' : clean t-                    | otherwise = h : clean t--        -- tollerant of errors?  should parse-        -- errors here be reported or ignored?-        -- currently ignored.-        catRslts :: [a] -> [Result a] -> Result [a]-        catRslts list (h:t) = -            case h of-                Left _ -> catRslts list t-                Right v -> catRslts (v:list) t-        catRslts list [] = Right $ reverse list            -        - -- Parsing a request parseRequestHead :: [String] -> Result RequestData parseRequestHead [] = Left ErrorClosed@@ -648,10 +354,6 @@                | Done                | ExpectEntity                | DieHorribly String----  matchResponse :: RequestMethod -> ResponseCode -> Behaviour matchResponse rqst rsp =
Network/HTTP/Base64.hs view
@@ -14,13 +14,12 @@ -- ----------------------------------------------------------------------------- -module Network.HTTP.Base64 (-    encode,-    decode,-    chop72-) where--+module Network.HTTP.Base64+   ( encode+   , decode+   , chop72+   , Octet+   ) where  {------------------------------------------------------------------------ This is what RFC2045 had to say:@@ -135,10 +134,8 @@    delimiters within base64-encoded bodies within multipart entities    because no hyphen characters are used in the base64 encoding. - ----------------------------------------------------------------------------} - {-  The following properties should hold:@@ -151,18 +148,14 @@ MIME applications might be undesireable.  - But: The Haskell98 Char type is at least 16bits (and often 32), these implementations assume only       8 significant bits, which is more than enough for US-ASCII.   -}  ---import Data.Array-import Data.Bits-import Data.Int-import Data.Char (chr,ord)+import Data.Array (Array, array, (!))+import Data.Bits (shiftL, shiftR, (.&.), (.|.))+import Data.Char (chr, ord) import Data.Word (Word8)  type Octet = Word8@@ -245,6 +238,7 @@  -- Pads a base64 code to a multiple of 4 characters, using the special -- '=' character.+quadruplets :: [Char] -> [Char] quadruplets (a:b:c:d:t) = a:b:c:d:quadruplets t quadruplets [a,b,c]     = [a,b,c,'=']      -- 16bit tail unit quadruplets [a,b]       = [a,b,'=','=']    -- 8bit tail unit@@ -255,6 +249,7 @@ enc = quadruplets . map enc1  +dcd :: String -> [Int] dcd [] = [] dcd (h:t)     | h <= 'Z' && h >= 'A'  =  ord h - ord 'A'      : dcd t
+ Network/HTTP/Headers.hs view
@@ -0,0 +1,310 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Network.HTTP.Headers+-- Copyright   :  (c) Warrick Gray 2002, Bjorn Bringert 2003-2005, 2007 Robin Bate Boerop+-- License     :  BSD+-- +-- Maintainer  :  bjorn@bringert.net+-- Stability   :  experimental+-- Portability :  non-portable (not tested)+--+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+--      - Made dependencies explicit in import statements.+--      - Removed false dependencies in import statements.+--      - Added missing type signatures.+--      - Created Network.HTTP.Headers from Network.HTTP modules.+--+-- See changes history and TODO list in Network.HTTP module.+--+-- * Header notes:+--+--     [@Host@]+--                  Required by HTTP\/1.1, if not supplied as part+--                  of a request a default Host value is extracted+--                  from the request-uri.+-- +--     [@Connection@] +--                  If this header is present in any request or+--                  response, and it's value is "close", then+--                  the current request\/response is the last +--                  to be allowed on that connection.+-- +--     [@Expect@]+--                  Should a request contain a body, an Expect+--                  header will be added to the request.  The added+--                  header has the value \"100-continue\".  After+--                  a 417 \"Expectation Failed\" response the request+--                  is attempted again without this added Expect+--                  header.+--                  +--     [@TransferEncoding,ContentLength,...@]+--                  if request is inconsistent with any of these+--                  header values then you may not receive any response+--                  or will generate an error response (probably 4xx).+--+-----------------------------------------------------------------------------+module Network.HTTP.Headers+   ( HasHeaders(..)+   , Header(..)+   , HeaderName(..)+   , insertHeader+   , insertHeaderIfMissing+   , insertHeaders+   , retrieveHeaders+   , replaceHeader+   , findHeader+   , lookupHeader+   , parseHeaders+   ) where++import Data.Char (isSpace, toLower)+import Data.List (elemIndex)+import Network.Stream (Result, ConnError(ErrorParse))++-- remove leading and trailing whitespace.+trim :: String -> String+trim = let dropspace = dropWhile isSpace in+       reverse . dropspace . reverse . dropspace++-- Split a list into two parts, the delimiter occurs+-- at the head of the second list.  Nothing is returned+-- when no occurance of the delimiter is found.+split :: Eq a => a -> [a] -> Maybe ([a],[a])+split delim list = case delim `elemIndex` list of+    Nothing -> Nothing+    Just x  -> Just $ splitAt x list++crlf :: String+crlf = "\r\n"++-- | The Header data type pairs header names & values.+data Header = Header HeaderName String++instance Show Header where+    show (Header key value) = show key ++ ": " ++ value ++ crlf++-- | HTTP Header Name type:+--  Why include this at all?  I have some reasons+--   1) prevent spelling errors of header names,+--   2) remind everyone of what headers are available,+--   3) might speed up searches for specific headers.+--+--  Arguments against:+--   1) makes customising header names laborious+--   2) increases code volume.+--+data HeaderName = +                 -- Generic Headers --+                  HdrCacheControl+                | HdrConnection+                | HdrDate+                | HdrPragma+                | HdrTransferEncoding        +                | HdrUpgrade                +                | HdrVia++                -- Request Headers --+                | HdrAccept+                | HdrAcceptCharset+                | HdrAcceptEncoding+                | HdrAcceptLanguage+                | HdrAuthorization+                | HdrCookie+                | HdrExpect+                | HdrFrom+                | HdrHost+                | HdrIfModifiedSince+                | HdrIfMatch+                | HdrIfNoneMatch+                | HdrIfRange+                | HdrIfUnmodifiedSince+                | HdrMaxForwards+                | HdrProxyAuthorization+                | HdrRange+                | HdrReferer+                | HdrUserAgent++                -- Response Headers+                | HdrAge+                | HdrLocation+                | HdrProxyAuthenticate+                | HdrPublic+                | HdrRetryAfter+                | HdrServer+                | HdrSetCookie+                | HdrVary+                | HdrWarning+                | HdrWWWAuthenticate++                -- Entity Headers+                | HdrAllow+                | HdrContentBase+                | HdrContentEncoding+                | HdrContentLanguage+                | HdrContentLength+                | HdrContentLocation+                | HdrContentMD5+                | HdrContentRange+                | HdrContentType+                | HdrETag+                | HdrExpires+                | HdrLastModified++                -- Mime entity headers (for sub-parts)+                | HdrContentTransferEncoding++                -- | Allows for unrecognised or experimental headers.+                | HdrCustom String -- not in header map below.+    deriving(Eq)++-- Translation between header names and values,+-- good candidate for improvement.+headerMap :: [ (String,HeaderName) ]+headerMap + = [  ("Cache-Control"        ,HdrCacheControl      )+	, ("Connection"           ,HdrConnection        )+	, ("Date"                 ,HdrDate              )    +	, ("Pragma"               ,HdrPragma            )+	, ("Transfer-Encoding"    ,HdrTransferEncoding  )        +	, ("Upgrade"              ,HdrUpgrade           )                +	, ("Via"                  ,HdrVia               )+	, ("Accept"               ,HdrAccept            )+	, ("Accept-Charset"       ,HdrAcceptCharset     )+	, ("Accept-Encoding"      ,HdrAcceptEncoding    )+	, ("Accept-Language"      ,HdrAcceptLanguage    )+	, ("Authorization"        ,HdrAuthorization     )+	, ("From"                 ,HdrFrom              )+	, ("Host"                 ,HdrHost              )+	, ("If-Modified-Since"    ,HdrIfModifiedSince   )+	, ("If-Match"             ,HdrIfMatch           )+	, ("If-None-Match"        ,HdrIfNoneMatch       )+	, ("If-Range"             ,HdrIfRange           ) +	, ("If-Unmodified-Since"  ,HdrIfUnmodifiedSince )+	, ("Max-Forwards"         ,HdrMaxForwards       )+	, ("Proxy-Authorization"  ,HdrProxyAuthorization)+	, ("Range"                ,HdrRange             )   +	, ("Referer"              ,HdrReferer           )+	, ("User-Agent"           ,HdrUserAgent         )+	, ("Age"                  ,HdrAge               )+	, ("Location"             ,HdrLocation          )+	, ("Proxy-Authenticate"   ,HdrProxyAuthenticate )+	, ("Public"               ,HdrPublic            )+	, ("Retry-After"          ,HdrRetryAfter        )+	, ("Server"               ,HdrServer            )+	, ("Vary"                 ,HdrVary              )+	, ("Warning"              ,HdrWarning           )+	, ("WWW-Authenticate"     ,HdrWWWAuthenticate   )+	, ("Allow"                ,HdrAllow             )+	, ("Content-Base"         ,HdrContentBase       )+	, ("Content-Encoding"     ,HdrContentEncoding   )+	, ("Content-Language"     ,HdrContentLanguage   )+	, ("Content-Length"       ,HdrContentLength     )+	, ("Content-Location"     ,HdrContentLocation   )+	, ("Content-MD5"          ,HdrContentMD5        )+	, ("Content-Range"        ,HdrContentRange      )+	, ("Content-Type"         ,HdrContentType       )+	, ("ETag"                 ,HdrETag              )+	, ("Expires"              ,HdrExpires           )+	, ("Last-Modified"        ,HdrLastModified      )+   	, ("Set-Cookie"           ,HdrSetCookie         )+	, ("Cookie"               ,HdrCookie            )+    , ("Expect"               ,HdrExpect            ) ]+++instance Show HeaderName where+    show (HdrCustom s) = s+    show x = case filter ((==x).snd) headerMap of+                [] -> error "headerMap incomplete"+                (h:_) -> fst h++-- | This class allows us to write generic header manipulation functions+-- for both 'Request' and 'Response' data types.+class HasHeaders x where+    getHeaders :: x -> [Header]+    setHeaders :: x -> [Header] -> x++-- Header manipulation functions+insertHeader, replaceHeader, insertHeaderIfMissing+    :: HasHeaders a => HeaderName -> String -> a -> a+++-- | Inserts a header with the given name and value.+-- Allows duplicate header names.+insertHeader name value x = setHeaders x newHeaders+    where+        newHeaders = (Header name value) : getHeaders x++-- | Adds the new header only if no previous header shares+-- the same name.+insertHeaderIfMissing name value x = setHeaders x (newHeaders $ getHeaders x)+    where+        newHeaders list@(h@(Header n _): rest)+            | n == name  = list+            | otherwise  = h : newHeaders rest+        newHeaders [] = [Header name value]++-- | Removes old headers with duplicate name.+replaceHeader name value x = setHeaders x newHeaders+    where+        newHeaders = Header name value : [ x | x@(Header n v) <- getHeaders x, name /= n ]+          +-- | Inserts multiple headers.+insertHeaders :: HasHeaders a => [Header] -> a -> a+insertHeaders hdrs x = setHeaders x (getHeaders x ++ hdrs)++-- | Gets a list of headers with a particular 'HeaderName'.+retrieveHeaders :: HasHeaders a => HeaderName -> a -> [Header]+retrieveHeaders name x = filter matchname (getHeaders x)+    where+        matchname (Header n _)  |  n == name  =  True+        matchname _ = False++-- | Lookup presence of specific HeaderName in a list of Headers+-- Returns the value from the first matching header.+findHeader :: HasHeaders a => HeaderName -> a -> Maybe String+findHeader n x = lookupHeader n (getHeaders x)++-- An anomally really:+lookupHeader :: HeaderName -> [Header] -> Maybe String+lookupHeader v (Header n s:t)  |  v == n   =  Just s+                               | otherwise =  lookupHeader v t+lookupHeader _ _  =  Nothing++parseHeader :: String -> Result Header+parseHeader str =+    case split ':' str of+        Nothing -> Left (ErrorParse $ "Unable to parse header: " ++ str)+        Just (k,v) -> Right $ Header (fn k) (trim $ drop 1 v)+    where+        fn k = case map snd $ filter (match k . fst) headerMap of+                 [] -> (HdrCustom k)+                 (h:_) -> h++        match :: String -> String -> Bool+        match s1 s2 = map toLower s1 == map toLower s2+    +parseHeaders :: [String] -> Result [Header]+parseHeaders =+   catRslts [] . map (parseHeader . clean) . joinExtended ""+   where+        -- Joins consecutive lines where the second line+        -- begins with ' ' or '\t'.+        joinExtended old (h : t)+            | not (null h) && (head h == ' ' || head h == '\t')+                = joinExtended (old ++ ' ' : tail h) t+            | otherwise = old : joinExtended h t+        joinExtended old [] = [old]++        clean [] = []+        clean (h:t) | h `elem` "\t\r\n" = ' ' : clean t+                    | otherwise = h : clean t++        -- tollerant of errors?  should parse+        -- errors here be reported or ignored?+        -- currently ignored.+        catRslts :: [a] -> [Result a] -> Result [a]+        catRslts list (h:t) = +            case h of+                Left _ -> catRslts list t+                Right v -> catRslts (v:list) t+        catRslts list [] = Right $ reverse list            
Network/HTTP/MD5.hs view
@@ -16,17 +16,18 @@ -- ----------------------------------------------------------------------------- -module Network.HTTP.MD5 (-   -- * Function Types-   hash) where--import Data.Char(chr)-import Data.List(unfoldr)-import Numeric(readHex)+module Network.HTTP.MD5+   ( hash+   , Octet+   ) where -import Network.HTTP.MD5Aux+import Data.Char (chr)+import Data.List (unfoldr) import Data.Word (Word8)+import Numeric (readHex) +import Network.HTTP.MD5Aux (md5s, Str(Str))+ type Octet = Word8  -- | Take [Octet] and return [Octet] according to the standard.@@ -37,8 +38,6 @@ hash xs =     unfoldr f $ md5s $ Str $ map (chr . fromIntegral) xs       where f :: String -> Maybe (Octet,String)-            f [] = -               Nothing-            f (x:y:zs) = -               Just (fromIntegral a,zs)-	       where [(a,_)] = readHex (x:y:[])+            f []       = Nothing+            f (x:y:zs) = Just (fromIntegral a,zs)+                         where [(a,_)] = readHex (x:y:[])
Network/HTTP/MD5Aux.hs view
@@ -3,9 +3,9 @@     MD5(..), ABCD(..),      Zord64, Str(..), BoolList(..), WordList(..)) where -import Data.Char-import Data.Bits-import Data.Word+import Data.Char (ord, chr)+import Data.Bits (rotateL, shiftL, shiftR, (.&.), (.|.), xor, complement)+import Data.Word (Word32, Word64)  rotL x = rotateL x type Zord64 = Word64
Network/Stream.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module      :  Network.Stream--- Copyright   :  (c) Warrick Gray 2002, Bjorn Bringert 2003-2004, Simon Foster 2004+-- Copyright   :  (c) Warrick Gray 2002, Bjorn Bringert 2003-2004, Simon Foster 2004, 2007 Robin Bate Boerop -- License     :  BSD -- -- Maintainer  :  bjorn@bringert.net@@ -11,36 +11,21 @@ -- An library for creating abstract streams. Originally part of Gray's\/Bringert's -- HTTP module. --+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+--      - Removed unnecessary import statements.+--      - Moved Debug code to StreamDebugger.hs+--      - Moved Socket-related code to StreamSocket.hs.+-- -- * Changes by Simon Foster:---      - Split module up into to sepearate Network.[Stream,TCP,HTTP] modules---      +--      - Split Network.HTTPmodule up into to separate+--        Network.[Stream,TCP,HTTP] modules ------------------------------------------------------------------------------module Network.Stream (-    -- ** Streams-    Debug,-    Stream(..),-    debugStream,-    -    -- ** Errors-    ConnError(..),-    Result,-    handleSocketError,-    bindE,-    myrecv--) where--import Control.Exception as Exception-import System.IO.Error---- Networking-import Network (withSocketsDo)-import Network.BSD-import Network.URI-import Network.Socket--import Control.Monad (when,liftM,guard)-import System.IO+module Network.Stream+   ( Stream(..)+   , ConnError(..)+   , Result+   , bindE+   ) where  data ConnError = ErrorReset                 | ErrorClosed@@ -48,10 +33,7 @@                | ErrorMisc String     deriving(Show,Eq) --- error propagating:--- we could've used a monad, but that would lead us--- into using the "-fglasgow-exts" compile flag.-bindE :: Either ConnError a -> (a -> Either ConnError b) -> Either ConnError b+bindE :: Result a -> (a -> Result b) -> Result b bindE (Left e)  _ = Left e bindE (Right v) f = f v @@ -59,10 +41,6 @@ type Result a = Either ConnError   {- error  -}                        a           {- result -} ------------------------------------------------------------------------------------- Gentle Art of Socket Sucking ------------------------------------------------------------------------------------ -- | Streams should make layering of TLS protocol easier in future, -- they allow reading/writing to files etc for debugging, -- they allow use of protocols other than TCP/IP@@ -78,99 +56,3 @@     writeBlock :: x -> String -> IO (Result ())     close      :: x -> IO () ------- Exception handler for socket operations-handleSocketError :: Socket -> Exception -> IO (Result a)-handleSocketError sk e =-    do { se <- getSocketOption sk SoError-       ; if se == 0-            then throw e-            else return $ if se == 10054       -- reset-                then Left ErrorReset-                else Left $ ErrorMisc $ show se-       }-----instance Stream Socket where-    readBlock sk n = (liftM Right $ fn n) `Exception.catch` (handleSocketError sk)-        where-            fn x = do { str <- myrecv sk x-                      ; let len = length str-                      ; if len < x-                          then ( fn (x-len) >>= \more -> return (str++more) )                        -                          else return str-                      }--    -- Use of the following function is discouraged.-    -- The function reads in one character at a time, -    -- which causes many calls to the kernel recv()-    -- hence causes many context switches.-    readLine sk = (liftM Right $ fn "") `Exception.catch` (handleSocketError sk)-            where-                fn str =-                    do { c <- myrecv sk 1 -- like eating through a straw.-                       ; if null c || c == "\n"-                           then return (reverse str++c)-                           else fn (head c:str)-                       }-    -    writeBlock sk str = (liftM Right $ fn str) `Exception.catch` (handleSocketError sk)-        where-            fn [] = return ()-            fn x  = send sk x >>= \i -> fn (drop i x)--    -- This slams closed the connection (which is considered rude for TCP\/IP)-    close sk = shutdown sk ShutdownBoth >> sClose sk--myrecv :: Socket -> Int -> IO String-myrecv _ 0 = return ""-myrecv sock len =-    let handler e = if isEOFError e then return [] else ioError e-        in System.IO.Error.catch (recv sock len) handler---- | Allows stream logging.--- Refer to 'debugStream' below.-data Debug x = Dbg Handle x---instance (Stream x) => Stream (Debug x) where-    readBlock (Dbg h c) n =-        do { val <- readBlock c n-           ; hPutStrLn h ("readBlock " ++ show n ++ ' ' : show val)-           ; return val-           }--    readLine (Dbg h c) =-        do { val <- readLine c-           ; hPutStrLn h ("readLine " ++ show val)-           ; return val-           }--    writeBlock (Dbg h c) str =-        do { val <- writeBlock c str-           ; hPutStrLn h ("writeBlock " ++ show val ++ ' ' : show str)-           ; return val-           }--    close (Dbg h c) =-        do { hPutStrLn h "closing..."-           ; hFlush h-           ; close c-           ; hPutStrLn h "...closed"-           ; hClose h-           }----- | Wraps a stream with logging I\/O, the first--- argument is a filename which is opened in AppendMode.-debugStream :: (Stream a) => String -> a -> IO (Debug a)-debugStream file stm = -    do { h <- openFile file AppendMode-       ; hPutStrLn h "File opened for appending."-       ; return (Dbg h stm)-       }
+ Network/StreamDebugger.hs view
@@ -0,0 +1,59 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Network.StreamDebugger+-- Copyright   :  (c) Warrick Gray 2002, Bjorn Bringert 2003-2004, Simon Foster 2004, 2007 Robin Bate Boerop+-- License     :  BSD+--+-- Maintainer  :  bjorn@bringert.net+-- Stability   :  experimental+-- Portability :  non-portable (not tested)+--+-- Implements debugging of @Stream@s.  Originally part of Gray's\/Bringert's+-- HTTP module.+--+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+--      - Created.  Made minor formatting changes.+--      +-----------------------------------------------------------------------------+module Network.StreamDebugger+   ( StreamDebugger+   , debugStream+   ) where++import Network.Stream (Stream(..))+import System.IO+   ( Handle, hFlush, hPutStrLn, IOMode(AppendMode), hClose, openFile+   )++-- | Allows stream logging.  Refer to 'debugStream' below.+data StreamDebugger x+   = Dbg Handle x++instance (Stream x) => Stream (StreamDebugger x) where+    readBlock (Dbg h x) n =+        do val <- readBlock x n+           hPutStrLn h ("readBlock " ++ show n ++ ' ' : show val)+           return val+    readLine (Dbg h x) =+        do val <- readLine x+           hPutStrLn h ("readLine " ++ show val)+           return val+    writeBlock (Dbg h x) str =+        do val <- writeBlock x str+           hPutStrLn h ("writeBlock " ++ show val ++ ' ' : show str)+           return val+    close (Dbg h x) =+        do hPutStrLn h "closing..."+           hFlush h+           close x+           hPutStrLn h "...closed"+           hClose h++-- | Wraps a stream with logging I\/O.+--   The first argument is a filename which is opened in @AppendMode@.+debugStream :: (Stream a) => FilePath -> a -> IO (StreamDebugger a)+debugStream file stream = +    do h <- openFile file AppendMode+       hPutStrLn h ("File \"" ++ file ++ "\" opened for appending.")+       return (Dbg h stream)+
+ Network/StreamSocket.hs view
@@ -0,0 +1,83 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Network.StreamSocket+-- Copyright   :  (c) Warrick Gray 2002, Bjorn Bringert 2003-2004, Simon Foster 2004, 2007 Robin Bate Boerop.+-- License     :  BSD+--+-- Maintainer  :  bjorn@bringert.net+-- Stability   :  experimental+-- Portability :  non-portable (not tested)+--+-- Socket Stream instance. Originally part of Gray's\/Bringert's HTTP module.+--+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+--      - Made dependencies explicit in import statements.+--      - Removed false dependencies in import statements.+--      - Created separate module for instance Stream Socket.+--+-- * Changes by Simon Foster:+--      - Split module up into to sepearate Network.[Stream,TCP,HTTP] modules+--      +-----------------------------------------------------------------------------+module Network.StreamSocket+   ( handleSocketError+   , myrecv+   ) where++import Network.Stream+   ( Stream(..), ConnError(ErrorReset, ErrorMisc), Result+   )+import Network.Socket+   ( Socket, getSocketOption, shutdown, send, recv, sClose+   , ShutdownCmd(ShutdownBoth), SocketOption(SoError)+   )++import Control.Monad (liftM)+import Control.Exception as Exception (Exception, catch, throw)+import System.IO.Error (catch, isEOFError)++-- | Exception handler for socket operations.+handleSocketError :: Socket -> Exception -> IO (Result a)+handleSocketError sk e =+    do se <- getSocketOption sk SoError+       case se of+          0     -> throw e+          10054 -> return $ Left ErrorReset  -- reset+          _     -> return $ Left $ ErrorMisc $ show se++instance Stream Socket where+    readBlock sk n = (liftM Right $ fn n) `Exception.catch` (handleSocketError sk)+        where+            fn x = do { str <- myrecv sk x+                      ; let len = length str+                      ; if len < x+                          then ( fn (x-len) >>= \more -> return (str++more) )                        +                          else return str+                      }++    -- Use of the following function is discouraged.+    -- The function reads in one character at a time, +    -- which causes many calls to the kernel recv()+    -- hence causes many context switches.+    readLine sk = (liftM Right $ fn "") `Exception.catch` (handleSocketError sk)+            where+                fn str =+                    do { c <- myrecv sk 1 -- like eating through a straw.+                       ; if null c || c == "\n"+                           then return (reverse str++c)+                           else fn (head c:str)+                       }+    +    writeBlock sk str = (liftM Right $ fn str) `Exception.catch` (handleSocketError sk)+        where+            fn [] = return ()+            fn x  = send sk x >>= \i -> fn (drop i x)++    -- This slams closed the connection (which is considered rude for TCP\/IP)+    close sk = shutdown sk ShutdownBoth >> sClose sk++myrecv :: Socket -> Int -> IO String+myrecv sock len =+    let handler e = if isEOFError e then return [] else ioError e+        in System.IO.Error.catch (recv sock len) handler+
Network/TCP.hs view
@@ -11,33 +11,40 @@ -- An easy access TCP library. Makes the use of TCP in Haskell much easier. -- This was originally part of Gray's\/Bringert's HTTP module. --+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+--      - Made dependencies explicit in import statements.+--      - Removed false dependencies from import statements.+--      - Removed unused exported functions.+-- -- * Changes by Simon Foster: --      - Split module up into to sepearate Network.[Stream,TCP,HTTP] modules --       ------------------------------------------------------------------------------module Network.TCP (-    -- ** Connections-    Conn(..),-    Connection(..),-    openTCP,-    openTCPPort,-    isConnectedTo-) where--import Control.Exception as Exception+module Network.TCP+   ( Connection+   , openTCPPort+   , isConnectedTo+   ) where --- Networking-import Network (withSocketsDo)-import Network.BSD-import Network.URI+import Network.BSD (getHostByName, hostAddresses) import Network.Socket+   ( Socket, SockAddr(SockAddrInet), SocketOption(KeepAlive, SoError)+   , SocketType(Stream), inet_addr, connect, sendTo+   , shutdown, ShutdownCmd(ShutdownSend, ShutdownReceive)+   , sClose, sIsConnected, setSocketOption, getSocketOption+   , socket, Family(AF_INET)+   ) import Network.Stream+   ( Stream(readBlock, readLine, writeBlock, close)+   , ConnError(ErrorMisc, ErrorReset, ErrorClosed)+   , bindE+   )+import Network.StreamSocket (myrecv, handleSocketError) -import Data.List (isPrefixOf,partition,elemIndex)-import Data.Char-import Data.IORef-import Control.Monad (when,liftM,guard)-import System.IO+import Control.Exception as Exception (catch, throw)+import Data.List (elemIndex)+import Data.Char (toLower)+import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)  ----------------------------------------------------------------- ------------------ TCP Connections ------------------------------@@ -50,9 +57,6 @@ -- implementation of the 'Stream Connection' instance. newtype Connection = ConnRef {getRef :: IORef Conn} ---- | The 'Conn' object allows input buffering, and maintenance of --- some admin-type data. data Conn = MkConn { connSock :: ! Socket                    , connAddr :: ! SockAddr                     , connBffr :: ! String @@ -60,12 +64,6 @@                    }           | ConnClosed     deriving(Eq)----- | Open a connection to port 80 on a remote host.-openTCP :: String -> IO Connection-openTCP host = openTCPPort host 80-  -- | This function establishes a connection to a remote -- host, it uses "getHostByName" which interrogates the