-- ----------------------------------------------------------------------------- -- Copyright 2002, Simon Marlow. -- Copyright 2006, Bjorn Bringert. -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions are -- met: -- -- * Redistributions of source code must retain the above copyright notice, -- this list of conditions and the following disclaimer. -- -- * Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- * Neither the name of the copyright holder(s) nor the names of -- contributors may be used to endorse or promote products derived from -- this software without specific prior written permission. -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- ----------------------------------------------------------------------------- module Request ( HTTPVersion(..), http1_1, http1_0, Request(..), RequestCmd(..), RequestBody, Connection(..), Expect(..), pRequestHeaders, getHost, getConnection ) where import Text.ParserCombinators.Parsec import Headers import Parse import Util import Data.Char import Data.Maybe import Network.URI ----------------------------------------------------------------------------- -- Requests -- Request-Line = Method SP Request-URI SP HTTP-Version CRLF data RequestCmd = OptionsReq | GetReq | HeadReq | PostReq | PutReq | DeleteReq | TraceReq | ConnectReq | ExtensionReq String instance Show RequestCmd where show c = case c of OptionsReq -> "OPTIONS" GetReq -> "GET" HeadReq -> "HEAD" PostReq -> "POST" PutReq -> "PUT" DeleteReq -> "DELETE" TraceReq -> "TRACE" ConnectReq -> "CONNECT" ExtensionReq s -> s data Request = Request { reqCmd :: RequestCmd, reqURI :: URI, reqHTTPVer :: HTTPVersion, reqHeaders :: Headers, reqBody :: RequestBody } instance Show Request where showsPrec _ Request{reqCmd = cmd, reqURI = uri, reqHTTPVer = ver} = shows cmd . (' ':) . shows uri . (' ':) . shows ver instance HasHeaders Request where getHeaders = reqHeaders setHeaders req hs = req { reqHeaders = hs} data HTTPVersion = HTTPVersion Int Int deriving (Eq,Ord) instance Show HTTPVersion where showsPrec _ (HTTPVersion maj min) = showString "HTTP/" . shows maj . showString "." . shows min http1_1, http1_0 :: HTTPVersion http1_1 = HTTPVersion 1 1 http1_0 = HTTPVersion 1 0 -- FIXME: use something more efficient type RequestBody = String -- Request parsing -- Parse the request line and the headers, but not the body. pRequestHeaders :: Parser Request pRequestHeaders = do (cmd,uri,ver) <- pRequestLine headers <- pHeaders pCRLF return $ Request cmd uri ver headers "" pRequestLine :: Parser (RequestCmd, URI, HTTPVersion) pRequestLine = do cmd <- pReqCmd many1 pSP uri <- pReqURI many1 pSP ver <- pReqHTTPVer pCRLF return (cmd,uri,ver) pReqCmd :: Parser RequestCmd pReqCmd = choice [ c "OPTIONS" OptionsReq, c "GET" GetReq, c "HEAD" HeadReq, c "POST" PostReq, c "PUT" PutReq, c "DELETE" DeleteReq, c "TRACE" TraceReq, c "CONNECT" ConnectReq, pToken >>= return . ExtensionReq ] where c x y = try (string x >> return y) pReqURI :: Parser URI pReqURI = do u <- many (noneOf [' ']) -- FIXME: this does not handle authority Request-URIs maybe (fail "Bad Request-URI") return $ parseURIReference u pReqHTTPVer :: Parser HTTPVersion pReqHTTPVer = do string "HTTP/"; major <- int; char '.'; minor <- int; return $ HTTPVersion major minor int :: Parser Int int = many1 digit >>= readM ----------------------------------------------------------------------------- -- Getting specific request headers data Connection = ConnectionClose | ConnectionKeepAlive -- non-std? Netscape generates it. | ConnectionOther String deriving (Eq, Show) parseConnection :: String -> [Connection] parseConnection = map (fn . map toLower) . parseList where fn "close" = ConnectionClose fn "keep-alive" = ConnectionKeepAlive fn other = ConnectionOther other getConnection :: HasHeaders a => a -> [Connection] getConnection = concatMap parseConnection . lookupHeaders HdrConnection data Expect = ExpectContinue deriving Show parseExpect :: String -> Maybe Expect parseExpect s = case parseList s of ["100-continue"] -> Just ExpectContinue _ -> Nothing getHost :: HasHeaders a => a -> Maybe (String, Maybe Int) getHost x = lookupHeader HdrHost x >>= parseHost parseHost :: String -> Maybe (String, Maybe Int) parseHost s = case port of "" -> Just (host, Nothing) ':':port -> readM port >>= \p -> Just (host, Just p) _ -> Nothing where (host,port) = break (==':') s