module Network.MoHWS.HTTP.Request (
T(Cons), command, uri, httpVersion, headers, body,
toHTTPbis, fromHTTPbis,
Command, HTTP.RequestMethod(..),
Connection(..),
Expect(..),
pHeaders,
getHost,
getConnection,
) where
import Text.ParserCombinators.Parsec (Parser, skipMany1, many, noneOf, )
import Network.MoHWS.ParserUtility (pCRLF, pSP, pToken, parseList, )
import qualified Network.MoHWS.HTTP.Header as Header
import qualified Network.MoHWS.HTTP.Version as HTTPVersion
import Network.MoHWS.HTTP.Header (HasHeaders, )
import Network.MoHWS.Utility (readM, )
import qualified Network.HTTP.Base as HTTP
import qualified Network.HTTP.Headers
import Network.Socket (HostName, )
import Network.URI (URI, nullURI, uriPath, uriQuery, )
import qualified Data.Map as Map
import Data.Monoid (Monoid, mempty, )
import Data.Char (toLower, )
type Command = HTTP.RequestMethod
data T body =
Cons {
command :: Command,
uri :: URI,
httpVersion :: HTTPVersion.T,
headers :: Header.Group,
body :: body
}
toHTTPbis :: T body -> HTTP.Request body
toHTTPbis req =
HTTP.Request {
HTTP.rqURI = uri req,
HTTP.rqMethod = command req,
HTTP.rqHeaders = Header.ungroup $ headers req,
HTTP.rqBody = body req
}
fromHTTPbis :: HTTP.Request body -> T body
fromHTTPbis req =
Cons {
command = HTTP.rqMethod req,
uri = HTTP.rqURI req,
httpVersion = HTTPVersion.http1_1,
headers = Header.group $ HTTP.rqHeaders req,
body = HTTP.rqBody req
}
instance Show (T body) where
showsPrec _ Cons{command = cmd, uri = loc, httpVersion = ver} =
shows cmd . (' ':) . shows loc . (' ':) . shows ver
instance HasHeaders (T body) where
getHeaders = Header.ungroup . headers
setHeaders req hs = req { headers = Header.group hs}
instance Functor T where
fmap f req =
Cons {
command = command req,
uri = uri req,
httpVersion = httpVersion req,
headers = headers req,
body = f $ body req
}
pHeaders :: Monoid body => Parser (T body)
pHeaders =
do (cmd,loc,ver) <- pCommandLine
hdrs <- Header.pGroup
_ <- pCRLF
return $ Cons cmd loc ver hdrs mempty
pCommandLine :: Parser (Command, URI, HTTPVersion.T)
pCommandLine =
do cmd <- pCommand
skipMany1 pSP
loc <- pURI
skipMany1 pSP
ver <- HTTPVersion.pInRequest
_ <- pCRLF
return (cmd,loc,ver)
commandDictionary :: Map.Map String Command
commandDictionary =
Map.fromList $
("HEAD", HTTP.HEAD) :
("PUT", HTTP.PUT) :
("GET", HTTP.GET) :
("POST", HTTP.POST) :
("DELETE", HTTP.DELETE) :
("OPTIONS", HTTP.OPTIONS) :
("TRACE", HTTP.TRACE) :
[]
pCommand :: Parser Command
pCommand =
fmap (\tok -> Map.findWithDefault (HTTP.Custom tok) tok commandDictionary) $
pToken
pURI :: Parser URI
pURI =
do u <- many (noneOf [' '])
return $ laxParseURIReference u
laxParseURIReference :: String -> URI
laxParseURIReference u =
let (p,q) = break ('?'==) u
in nullURI{uriPath=p, uriQuery=q}
data Connection =
ConnectionClose
| ConnectionKeepAlive
| ConnectionOther String
deriving (Eq, Show)
parseConnection :: String -> [Connection]
parseConnection =
let fn "close" = ConnectionClose
fn "keep-alive" = ConnectionKeepAlive
fn other = ConnectionOther other
in map (fn . map toLower) . parseList
getConnection :: HasHeaders a => a -> [Connection]
getConnection =
concatMap parseConnection . Header.lookupMany Header.HdrConnection
data Expect = ExpectContinue
deriving Show
getHost :: HasHeaders a => a -> Maybe (HostName, Maybe Int)
getHost x = Header.lookup Header.HdrHost x >>= parseHost
parseHost :: String -> Maybe (HostName, Maybe Int)
parseHost s =
let (host,prt) = break (==':') s
in case prt of
"" -> Just (host, Nothing)
':':port -> readM port >>= \p -> Just (host, Just p)
_ -> Nothing