------------------------------------------------------------------------------- |-- Module : Network.HTTP.Stream-- Copyright : See LICENSE file-- License : BSD---- Maintainer : Ganesh Sittampalam <ganesh@earth.li>-- Stability : experimental-- Portability : non-portable (not tested)---- Transmitting HTTP requests and responses holding @String@ in their payload bodies.-- This is one of the implementation modules for the "Network.HTTP" interface, representing-- request and response content as @String@s and transmitting them in non-packed form-- (cf. "Network.HTTP.HandleStream" and its use of @ByteString@s.) over 'Stream' handles.-- It is mostly here for backwards compatibility, representing how requests and responses-- were transmitted up until the 4.x releases of the HTTP package.---- For more detailed information about what the individual exports do, please consult-- the documentation for "Network.HTTP". /Notice/ however that the functions here do-- not perform any kind of normalization prior to transmission (or receipt); you are-- responsible for doing any such yourself, or, if you prefer, just switch to using-- "Network.HTTP" function instead.-------------------------------------------------------------------------------moduleNetwork.HTTP.Stream(moduleNetwork.Stream ,simpleHTTP -- :: Request_String -> IO (Result Response_String),simpleHTTP_ -- :: Stream s => s -> Request_String -> IO (Result Response_String),sendHTTP -- :: Stream s => s -> Request_String -> IO (Result Response_String),sendHTTP_notify -- :: Stream s => s -> Request_String -> IO () -> IO (Result Response_String),receiveHTTP -- :: Stream s => s -> IO (Result Request_String),respondHTTP -- :: Stream s => s -> Response_String -> IO ())where----------------------------------------------------------------------------------- Imports -------------------------------------------------------------------------------------------------------importNetwork.Stream importNetwork.StreamDebugger (debugStream )importNetwork.TCP (openTCPPort )importNetwork.BufferType (stringBufferOp )importNetwork.HTTP.Base importNetwork.HTTP.Headers importNetwork.HTTP.Utils (trim )importData.Char(toLower)importData.Maybe(fromMaybe)importControl.Exception(onException)importControl.Monad(when)-- Turn on to enable HTTP traffic loggingdebug ::Booldebug :: Bool
debug =Bool
False-- File that HTTP traffic logs go tohttpLogFile ::StringhttpLogFile :: String
httpLogFile =String
"http-debug.log"----------------------------------------------------------------------------------- Misc ------------------------------------------------------------------------------------------------------------ | Simple way to transmit a resource across a non-persistent connection.simpleHTTP ::Request_String ->IO(Result Response_String )simpleHTTP :: Request_String -> IO (Result Response_String)
simpleHTTP Request_String
r =doURIAuthority
auth <-Request_String -> IO URIAuthority
forall (m :: * -> *) ty.
MonadFail m =>
Request ty -> m URIAuthority
getAuth Request_String
r Connection
c <-String -> Int -> IO Connection
openTCPPort (URIAuthority -> String
host URIAuthority
auth )(Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybeInt
80(URIAuthority -> Maybe Int
port URIAuthority
auth ))Connection -> Request_String -> IO (Result Response_String)
forall s.
Stream s =>
s -> Request_String -> IO (Result Response_String)
simpleHTTP_ Connection
c Request_String
r -- | Like 'simpleHTTP', but acting on an already opened stream.simpleHTTP_ ::Stream s =>s ->Request_String ->IO(Result Response_String )simpleHTTP_ :: s -> Request_String -> IO (Result Response_String)
simpleHTTP_ s
s Request_String
r |Bool -> Bool
notBool
debug =s -> Request_String -> IO (Result Response_String)
forall s.
Stream s =>
s -> Request_String -> IO (Result Response_String)
sendHTTP s
s Request_String
r |Bool
otherwise=doStreamDebugger s
s' <-String -> s -> IO (StreamDebugger s)
forall a. Stream a => String -> a -> IO (StreamDebugger a)
debugStream String
httpLogFile s
s StreamDebugger s -> Request_String -> IO (Result Response_String)
forall s.
Stream s =>
s -> Request_String -> IO (Result Response_String)
sendHTTP StreamDebugger s
s' Request_String
r sendHTTP ::Stream s =>s ->Request_String ->IO(Result Response_String )sendHTTP :: s -> Request_String -> IO (Result Response_String)
sendHTTP s
conn Request_String
rq =s -> Request_String -> IO () -> IO (Result Response_String)
forall s.
Stream s =>
s -> Request_String -> IO () -> IO (Result Response_String)
sendHTTP_notify s
conn Request_String
rq (() -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return())sendHTTP_notify ::Stream s =>s ->Request_String ->IO()->IO(Result Response_String )sendHTTP_notify :: s -> Request_String -> IO () -> IO (Result Response_String)
sendHTTP_notify s
conn Request_String
rq IO ()
onSendComplete =doBool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
whenBool
providedClose (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$(s -> Bool -> IO ()
forall x. Stream x => x -> Bool -> IO ()
closeOnEnd s
conn Bool
True)IO (Result Response_String) -> IO () -> IO (Result Response_String)
forall a b. IO a -> IO b -> IO a
onException(s -> Request_String -> IO () -> IO (Result Response_String)
forall s.
Stream s =>
s -> Request_String -> IO () -> IO (Result Response_String)
sendMain s
conn Request_String
rq IO ()
onSendComplete )(s -> IO ()
forall x. Stream x => x -> IO ()
close s
conn )whereprovidedClose :: Bool
providedClose =[Header] -> Bool
findConnClose (Request_String -> [Header]
forall a. Request a -> [Header]
rqHeaders Request_String
rq )-- From RFC 2616, section 8.2.3:-- 'Because of the presence of older implementations, the protocol allows-- ambiguous situations in which a client may send "Expect: 100--- continue" without receiving either a 417 (Expectation Failed) status-- or a 100 (Continue) status. Therefore, when a client sends this-- header field to an origin server (possibly via a proxy) from which it-- has never seen a 100 (Continue) status, the client SHOULD NOT wait-- for an indefinite period before sending the request body.'---- Since we would wait forever, I have disabled use of 100-continue for now.sendMain ::Stream s =>s ->Request_String ->IO()->IO(Result Response_String )sendMain :: s -> Request_String -> IO () -> IO (Result Response_String)
sendMain s
conn Request_String
rqst IO ()
onSendComplete =do--let str = if null (rqBody rqst)-- then show rqst-- else show (insertHeader HdrExpect "100-continue" rqst)-- TODO review throwing away of resultResult ()
_<-s -> String -> IO (Result ())
forall x. Stream x => x -> String -> IO (Result ())
writeBlock s
conn (Request_String -> String
forall a. Show a => a -> String
showRequest_String
rqst )-- write body immediately, don't wait for 100 CONTINUE-- TODO review throwing away of resultResult ()
_<-s -> String -> IO (Result ())
forall x. Stream x => x -> String -> IO (Result ())
writeBlock s
conn (Request_String -> String
forall a. Request a -> a
rqBody Request_String
rqst )IO ()
onSendComplete Result ResponseData
rsp <-s -> IO (Result ResponseData)
forall s. Stream s => s -> IO (Result ResponseData)
getResponseHead s
conn s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
forall s.
Stream s =>
s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
switchResponse s
conn Bool
TrueBool
FalseResult ResponseData
rsp Request_String
rqst -- reads and parses headersgetResponseHead ::Stream s =>s ->IO(Result ResponseData )getResponseHead :: s -> IO (Result ResponseData)
getResponseHead s
conn =doResult [String]
lor <-BufferOp String -> IO (Result String) -> IO (Result [String])
forall a. BufferOp a -> IO (Result a) -> IO (Result [a])
readTillEmpty1 BufferOp String
stringBufferOp (s -> IO (Result String)
forall x. Stream x => x -> IO (Result String)
readLine s
conn )Result ResponseData -> IO (Result ResponseData)
forall (m :: * -> *) a. Monad m => a -> m a
return(Result ResponseData -> IO (Result ResponseData))
-> Result ResponseData -> IO (Result ResponseData)
forall a b. (a -> b) -> a -> b
$Result [String]
lor Result [String]
-> ([String] -> Result ResponseData) -> Result ResponseData
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=[String] -> Result ResponseData
parseResponseHead -- Hmmm, this could go bad if we keep getting "100 Continue"-- responses... Except this should never happen according-- to the RFC.switchResponse ::Stream s =>s ->Bool{- allow retry? -}->Bool{- is body sent? -}->Result ResponseData ->Request_String ->IO(Result Response_String )switchResponse :: s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
switchResponse s
_Bool
_Bool
_(LeftConnError
e )Request_String
_=Result Response_String -> IO (Result Response_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(ConnError -> Result Response_String
forall a b. a -> Either a b
LeftConnError
e )-- retry on connreset?-- if we attempt to use the same socket then there is an excellent-- chance that the socket is not in a completely closed state.switchResponse s
conn Bool
allow_retry Bool
bdy_sent (Right(ResponseCode
cd ,String
rn ,[Header]
hdrs ))Request_String
rqst =caseRequestMethod -> ResponseCode -> ResponseNextStep
matchResponse (Request_String -> RequestMethod
forall a. Request a -> RequestMethod
rqMethod Request_String
rqst )ResponseCode
cd ofResponseNextStep
Continue |Bool -> Bool
notBool
bdy_sent ->{- Time to send the body -}do{Result ()
val <-s -> String -> IO (Result ())
forall x. Stream x => x -> String -> IO (Result ())
writeBlock s
conn (Request_String -> String
forall a. Request a -> a
rqBody Request_String
rqst );caseResult ()
val ofLeftConnError
e ->Result Response_String -> IO (Result Response_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(ConnError -> Result Response_String
forall a b. a -> Either a b
LeftConnError
e )Right()
_->do{Result ResponseData
rsp <-s -> IO (Result ResponseData)
forall s. Stream s => s -> IO (Result ResponseData)
getResponseHead s
conn ;s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
forall s.
Stream s =>
s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
switchResponse s
conn Bool
allow_retry Bool
TrueResult ResponseData
rsp Request_String
rqst }}|Bool
otherwise->{- keep waiting -}do{Result ResponseData
rsp <-s -> IO (Result ResponseData)
forall s. Stream s => s -> IO (Result ResponseData)
getResponseHead s
conn ;s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
forall s.
Stream s =>
s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
switchResponse s
conn Bool
allow_retry Bool
bdy_sent Result ResponseData
rsp Request_String
rqst }ResponseNextStep
Retry ->{- Request with "Expect" header failed.
 Trouble is the request contains Expects
 other than "100-Continue" -}do{-- TODO review throwing away of resultResult ()
_<-s -> String -> IO (Result ())
forall x. Stream x => x -> String -> IO (Result ())
writeBlock s
conn (Request_String -> String
forall a. Show a => a -> String
showRequest_String
rqst String -> String -> String
forall a. [a] -> [a] -> [a]
++Request_String -> String
forall a. Request a -> a
rqBody Request_String
rqst );Result ResponseData
rsp <-s -> IO (Result ResponseData)
forall s. Stream s => s -> IO (Result ResponseData)
getResponseHead s
conn ;s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
forall s.
Stream s =>
s
-> Bool
-> Bool
-> Result ResponseData
-> Request_String
-> IO (Result Response_String)
switchResponse s
conn Bool
FalseBool
bdy_sent Result ResponseData
rsp Request_String
rqst }ResponseNextStep
Done ->doBool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when([Header] -> Bool
findConnClose [Header]
hdrs )(s -> Bool -> IO ()
forall x. Stream x => x -> Bool -> IO ()
closeOnEnd s
conn Bool
True)Result Response_String -> IO (Result Response_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(Response_String -> Result Response_String
forall a b. b -> Either a b
Right(Response_String -> Result Response_String)
-> Response_String -> Result Response_String
forall a b. (a -> b) -> a -> b
$ResponseCode -> String -> [Header] -> String -> Response_String
forall a. ResponseCode -> String -> [Header] -> a -> Response a
Response ResponseCode
cd String
rn [Header]
hdrs String
"")DieHorribly String
str ->dos -> IO ()
forall x. Stream x => x -> IO ()
close s
conn Result Response_String -> IO (Result Response_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(Result Response_String -> IO (Result Response_String))
-> Result Response_String -> IO (Result Response_String)
forall a b. (a -> b) -> a -> b
$String -> String -> Result Response_String
forall a. String -> String -> Result a
responseParseError String
"sendHTTP"(String
"Invalid response: "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
str )ResponseNextStep
ExpectEntity ->lettc :: Maybe String
tc =HeaderName -> [Header] -> Maybe String
lookupHeader HeaderName
HdrTransferEncoding [Header]
hdrs cl :: Maybe String
cl =HeaderName -> [Header] -> Maybe String
lookupHeader HeaderName
HdrContentLength [Header]
hdrs indo{Result ([Header], String)
rslt <-caseMaybe String
tc ofMaybe String
Nothing->caseMaybe String
cl ofJustString
x ->(Int -> IO (Result String))
-> Int -> IO (Result ([Header], String))
forall a.
(Int -> IO (Result a)) -> Int -> IO (Result ([Header], a))
linearTransfer (s -> Int -> IO (Result String)
forall x. Stream x => x -> Int -> IO (Result String)
readBlock s
conn )(String -> Int
forall a. Read a => String -> a
readString
x ::Int)Maybe String
Nothing->BufferOp String
-> IO (Result String) -> [String] -> IO (Result ([Header], String))
forall a.
BufferOp a -> IO (Result a) -> [a] -> IO (Result ([Header], a))
hopefulTransfer BufferOp String
stringBufferOp {-null (++) []-}(s -> IO (Result String)
forall x. Stream x => x -> IO (Result String)
readLine s
conn )[]JustString
x ->case(Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
mapChar -> Char
toLower(String -> String
trim String
x )ofString
"chunked"->BufferOp String
-> IO (Result String)
-> (Int -> IO (Result String))
-> IO (Result ([Header], String))
forall a.
BufferOp a
-> IO (Result a)
-> (Int -> IO (Result a))
-> IO (Result ([Header], a))
chunkedTransfer BufferOp String
stringBufferOp (s -> IO (Result String)
forall x. Stream x => x -> IO (Result String)
readLine s
conn )(s -> Int -> IO (Result String)
forall x. Stream x => x -> Int -> IO (Result String)
readBlock s
conn )String
_->String -> IO (Result ([Header], String))
forall a. String -> IO (Result ([Header], a))
uglyDeathTransfer String
"sendHTTP";caseResult ([Header], String)
rslt ofLeftConnError
e ->s -> IO ()
forall x. Stream x => x -> IO ()
close s
conn IO () -> IO (Result Response_String) -> IO (Result Response_String)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>Result Response_String -> IO (Result Response_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(ConnError -> Result Response_String
forall a b. a -> Either a b
LeftConnError
e )Right([Header]
ftrs ,String
bdy )->doBool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when([Header] -> Bool
findConnClose ([Header]
hdrs [Header] -> [Header] -> [Header]
forall a. [a] -> [a] -> [a]
++[Header]
ftrs ))(s -> Bool -> IO ()
forall x. Stream x => x -> Bool -> IO ()
closeOnEnd s
conn Bool
True)Result Response_String -> IO (Result Response_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(Response_String -> Result Response_String
forall a b. b -> Either a b
Right(ResponseCode -> String -> [Header] -> String -> Response_String
forall a. ResponseCode -> String -> [Header] -> a -> Response a
Response ResponseCode
cd String
rn ([Header]
hdrs [Header] -> [Header] -> [Header]
forall a. [a] -> [a] -> [a]
++[Header]
ftrs )String
bdy ))}-- | Receive and parse a HTTP request from the given Stream. Should be used-- for server side interactions.receiveHTTP ::Stream s =>s ->IO(Result Request_String )receiveHTTP :: s -> IO (Result Request_String)
receiveHTTP s
conn =IO (Result RequestData)
getRequestHead IO (Result RequestData)
-> (Result RequestData -> IO (Result Request_String))
-> IO (Result Request_String)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=Result RequestData -> IO (Result Request_String)
processRequest where-- reads and parses headersgetRequestHead ::IO(Result RequestData )getRequestHead :: IO (Result RequestData)
getRequestHead =do{Result [String]
lor <-BufferOp String -> IO (Result String) -> IO (Result [String])
forall a. BufferOp a -> IO (Result a) -> IO (Result [a])
readTillEmpty1 BufferOp String
stringBufferOp (s -> IO (Result String)
forall x. Stream x => x -> IO (Result String)
readLine s
conn );Result RequestData -> IO (Result RequestData)
forall (m :: * -> *) a. Monad m => a -> m a
return(Result RequestData -> IO (Result RequestData))
-> Result RequestData -> IO (Result RequestData)
forall a b. (a -> b) -> a -> b
$Result [String]
lor Result [String]
-> ([String] -> Result RequestData) -> Result RequestData
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=[String] -> Result RequestData
parseRequestHead }processRequest :: Result RequestData -> IO (Result Request_String)
processRequest (LeftConnError
e )=Result Request_String -> IO (Result Request_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(Result Request_String -> IO (Result Request_String))
-> Result Request_String -> IO (Result Request_String)
forall a b. (a -> b) -> a -> b
$ConnError -> Result Request_String
forall a b. a -> Either a b
LeftConnError
e processRequest (Right(RequestMethod
rm ,URI
uri ,[Header]
hdrs ))=do-- FIXME : Also handle 100-continue.lettc :: Maybe String
tc =HeaderName -> [Header] -> Maybe String
lookupHeader HeaderName
HdrTransferEncoding [Header]
hdrs cl :: Maybe String
cl =HeaderName -> [Header] -> Maybe String
lookupHeader HeaderName
HdrContentLength [Header]
hdrs Result ([Header], String)
rslt <-caseMaybe String
tc ofMaybe String
Nothing->caseMaybe String
cl ofJustString
x ->(Int -> IO (Result String))
-> Int -> IO (Result ([Header], String))
forall a.
(Int -> IO (Result a)) -> Int -> IO (Result ([Header], a))
linearTransfer (s -> Int -> IO (Result String)
forall x. Stream x => x -> Int -> IO (Result String)
readBlock s
conn )(String -> Int
forall a. Read a => String -> a
readString
x ::Int)Maybe String
Nothing->Result ([Header], String) -> IO (Result ([Header], String))
forall (m :: * -> *) a. Monad m => a -> m a
return(([Header], String) -> Result ([Header], String)
forall a b. b -> Either a b
Right([],String
""))-- hopefulTransfer ""JustString
x ->case(Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
mapChar -> Char
toLower(String -> String
trim String
x )ofString
"chunked"->BufferOp String
-> IO (Result String)
-> (Int -> IO (Result String))
-> IO (Result ([Header], String))
forall a.
BufferOp a
-> IO (Result a)
-> (Int -> IO (Result a))
-> IO (Result ([Header], a))
chunkedTransfer BufferOp String
stringBufferOp (s -> IO (Result String)
forall x. Stream x => x -> IO (Result String)
readLine s
conn )(s -> Int -> IO (Result String)
forall x. Stream x => x -> Int -> IO (Result String)
readBlock s
conn )String
_->String -> IO (Result ([Header], String))
forall a. String -> IO (Result ([Header], a))
uglyDeathTransfer String
"receiveHTTP"Result Request_String -> IO (Result Request_String)
forall (m :: * -> *) a. Monad m => a -> m a
return(Result Request_String -> IO (Result Request_String))
-> Result Request_String -> IO (Result Request_String)
forall a b. (a -> b) -> a -> b
$do([Header]
ftrs ,String
bdy )<-Result ([Header], String)
rslt Request_String -> Result Request_String
forall (m :: * -> *) a. Monad m => a -> m a
return(URI -> RequestMethod -> [Header] -> String -> Request_String
forall a. URI -> RequestMethod -> [Header] -> a -> Request a
Request URI
uri RequestMethod
rm ([Header]
hdrs [Header] -> [Header] -> [Header]
forall a. [a] -> [a] -> [a]
++[Header]
ftrs )String
bdy )-- | Very simple function, send a HTTP response over the given stream. This-- could be improved on to use different transfer types.respondHTTP ::Stream s =>s ->Response_String ->IO()respondHTTP :: s -> Response_String -> IO ()
respondHTTP s
conn Response_String
rsp =do-- TODO review throwing away of resultResult ()
_<-s -> String -> IO (Result ())
forall x. Stream x => x -> String -> IO (Result ())
writeBlock s
conn (Response_String -> String
forall a. Show a => a -> String
showResponse_String
rsp )-- write body immediately, don't wait for 100 CONTINUE-- TODO review throwing away of resultResult ()
_<-s -> String -> IO (Result ())
forall x. Stream x => x -> String -> IO (Result ())
writeBlock s
conn (Response_String -> String
forall a. Response a -> a
rspBody Response_String
rsp )() -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return()

AltStyle によって変換されたページ (->オリジナル) /