hsp-0.2: HTTP/Request.hs
{-# OPTIONS -fglasgow-exts #-}
-----------------------------------------------------------------------------
-- |
-- Module : HTTP.Request
-- Copyright : (c) Andreas Farre, Niklas Broberg, 2004
-- License : BSD-style (see the file LICENSE.txt)
--
-- Maintainer : Andreas Farre, d00farre@dtek.chalmers.se,
-- Niklas Broberg, d00nibro@dtek.chalmers.se
-- Stability : experimental
-- Portability : portable
--
-- Datatypes representing the concept of an HTTP request.
-----------------------------------------------------------------------------
module HTTP.Request (
-- * Datatypes
Request(..)
, Method(..)
, RequestURI(..)
, HTTPVersion(..)
, Header(..)
, MessageBody(..)
-- * Parsing
, parseRequest -- :: String -> Maybe Request
) where
import Data.Char ( isDigit )
import Data.List ( isPrefixOf )
import Network.URI
import HTTP.Common
import HTTP.Util
-------------------------------------------------------
-- Datatypes
-- | An HTTP request
data Request
= Request {
reqMethod :: Method,
reqURI :: RequestURI,
reqVersion :: HTTPVersion,
reqHeaders :: [Header],
reqMessageBody :: MessageBody
}
instance Show Request where
showsPrec n req = -- parensIf (n > 0) $
shows (reqMethod req) .
showsSpace .
shows (reqURI req) .
showsSpace .
shows (reqVersion req) .
showsCRLF .
shows (reqHeaders req) .
shows (reqMessageBody req)
{-
where parensIf b s =
if b then showChar '(' . s . showChar ')'
else s
-}
data Method
= OPTIONS
| GET
| HEAD
| POST
| PUT
| DELETE
| TRACE
| CONNECT
| ExtensionMethod String
deriving (Eq, Show, Read)
data RequestURI
= NoURI
| AbsURI URI
| AbsPath FilePath
instance Show RequestURI where
show NoURI = "*"
show (AbsURI uri) = show uri
show (AbsPath path) = path
-------------------------------------------------------
-- Parsing requests in string format
parseRequest :: String -> Maybe Request
parseRequest str =
case crlines $ killLWS str of
(rqline:hdrstrs) ->
case words rqline of
[m, u, v] ->
let muri = parseRURI u
mver = parseVersion v
mhds = parseHeaders hdrstrs
in case (muri, mver, mhds) of
(Just uri, Just ver, Just hds) ->
Just $ Request
(parseMethod m)
uri
ver
hds
EmptyBody -- TODO: this is wrong, and dangerous
_ -> Nothing
_ -> Nothing
_ -> Nothing
where parseMethod :: String -> Method
parseMethod m
= case m of
"GET" -> GET
"POST" -> POST
"OPTIONS" -> OPTIONS
"HEAD" -> HEAD
"PUT" -> PUT
"DELETE" -> DELETE
"TRACE" -> TRACE
"CONNECT" -> CONNECT
str -> ExtensionMethod str
parseRURI :: String -> Maybe RequestURI
parseRURI ruri =
case ruri of
"*" -> Just NoURI
('/':_) -> Just $ AbsPath ruri
(x:_) | Just uri <- parseURI ruri
-> Just $ AbsURI uri
"" -> Just $ AbsPath "/"
_ -> Nothing
-- Is this one really correct?
-- It will do the right thing on a correct version spec, but
-- isn't it possible to fool it?
parseVersion :: String -> Maybe HTTPVersion
parseVersion v
| "HTTP" `isPrefixOf` v
= let (maj, rest) = span isDigit $ dropWhile (not . isDigit) v
min = takeWhile isDigit $ dropWhile (not . isDigit) rest
in Just $ HTTPVersion (read maj, read min)
| otherwise = Just $ HTTPVersion (9, 9) -- huh?
| otherwise = Nothing
parseHeaders :: [String] -> Maybe [Header]
parseHeaders [] = Just []
parseHeaders (str:strs) =
case str of
[/ fn*, ':', fv* /] -> fmap ((Header (fn, fv)):) $ parseHeaders strs
_ -> Nothing