]> gitweb @ CieloNegro.org - Lucu.git/blob - Network/HTTP/Lucu/Postprocess.hs
getRequestURI should always return an absolute URI
[Lucu.git] / Network / HTTP / Lucu / Postprocess.hs
1 -- #hide
2 module Network.HTTP.Lucu.Postprocess
3     ( postprocess
4     , completeUnconditionalHeaders
5     )
6     where
7
8 import           Control.Concurrent.STM
9 import           Control.Monad
10 import           Data.Char
11 import           Data.Maybe
12 import           GHC.Conc (unsafeIOToSTM)
13 import           Network.HTTP.Lucu.Abortion
14 import           Network.HTTP.Lucu.Config
15 import           Network.HTTP.Lucu.Headers
16 import           Network.HTTP.Lucu.HttpVersion
17 import           Network.HTTP.Lucu.Interaction
18 import           Network.HTTP.Lucu.RFC1123DateTime
19 import           Network.HTTP.Lucu.Request
20 import           Network.HTTP.Lucu.Response
21 import           Network.HTTP.Lucu.Utils
22 import           System.Time
23
24 {-
25   
26   * Response が未設定なら、200 OK にする。
27
28   * ステータスが 2xx, 3xx, 4xx, 5xx のいずれでもなければ 500 にする。
29
30   * 405 Method Not Allowed なのに Allow ヘッダが無ければ 500 にする。
31
32   * 304 Not Modified 以外の 3xx なのに Location ヘッダが無ければ 500 に
33     する。
34
35   * Content-Length があれば、それを削除する。Transfer-Encoding があって
36     も削除する。
37
38   * HTTP/1.1 であり、body を持つ事が出來る時、Transfer-Encoding を
39     chunked に設定する。
40
41   * body を持つ事が出來る時、Content-Type が無ければデフォルト値にする。
42     出來ない時、HEAD でなければContent-Type, Etag, Last-Modified を削除
43     する。
44
45   * body を持つ事が出來ない時、body 破棄フラグを立てる。
46
47   * Connection: close が設定されてゐる時、切斷フラグを立てる。
48
49   * 切斷フラグが立ってゐる時、Connection: close を設定する。
50
51   * Server が無ければ設定。
52
53   * Date が無ければ設定。
54
55 -}
56
57 postprocess :: Interaction -> STM ()
58 postprocess itr
59     = do reqM <- readItr itr itrRequest id
60          res  <- readItr itr itrResponse id
61          let sc = resStatus res
62
63          when (not $ any (\ p -> p sc) [isSuccessful, isRedirection, isError])
64                   $ abortSTM InternalServerError []
65                         $ Just ("The status code is not good for a final status: "
66                                 ++ show sc)
67
68          when (sc == MethodNotAllowed && getHeader "Allow" res == Nothing)
69                   $ abortSTM InternalServerError []
70                         $ Just ("The status was " ++ show sc ++ " but no Allow header.")
71
72          when (sc /= NotModified && isRedirection sc && getHeader "Location" res == Nothing)
73                   $ abortSTM InternalServerError []
74                         $ Just ("The status code was " ++ show sc ++ " but no Location header.")
75
76          when (reqM /= Nothing)
77               $ relyOnRequest itr
78
79          -- itrResponse の内容は relyOnRequest によって變へられてゐる可
80          -- 能性が高い。
81          do oldRes <- readItr itr itrResponse id
82             newRes <- unsafeIOToSTM
83                       $ completeUnconditionalHeaders (itrConfig itr) oldRes
84             writeItr itr itrResponse newRes
85     where
86       relyOnRequest :: Interaction -> STM ()
87       relyOnRequest itr
88           = do status <- readItr itr itrResponse resStatus
89                req    <- readItr itr itrRequest fromJust
90
91                let reqVer      = reqVersion req
92                    canHaveBody = if reqMethod req == HEAD then
93                                      False
94                                  else
95                                      not (isInformational status ||
96                                           status == NoContent    ||
97                                           status == ResetContent ||
98                                           status == NotModified    )
99
100                updateRes itr $ deleteHeader "Content-Length"
101                updateRes itr $ deleteHeader "Transfer-Encoding"
102
103                cType <- readHeader itr "Content-Type"
104                when (cType == Nothing)
105                         $ updateRes itr $ setHeader "Content-Type" defaultPageContentType
106
107                if canHaveBody then
108                    when (reqVer == HttpVersion 1 1)
109                             $ do updateRes itr $ setHeader "Transfer-Encoding" "chunked"
110                                  writeItr itr itrWillChunkBody True
111                  else
112                    -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
113                    when (reqMethod req /= HEAD)
114                             $ do updateRes itr $ deleteHeader "Content-Type"
115                                  updateRes itr $ deleteHeader "Etag"
116                                  updateRes itr $ deleteHeader "Last-Modified"
117
118                conn <- readHeader itr "Connection"
119                case fmap (map toLower) conn of
120                  Just "close" -> writeItr itr itrWillClose True
121                  _            -> return ()
122
123                willClose <- readItr itr itrWillClose id
124                when willClose
125                         $ updateRes itr $ setHeader "Connection" "close"
126
127                when (reqMethod req == HEAD || not canHaveBody)
128                         $ writeTVar (itrWillDiscardBody itr) True
129
130       readHeader :: Interaction -> String -> STM (Maybe String)
131       readHeader itr name
132           = readItr itr itrResponse $ getHeader name
133
134       updateRes :: Interaction -> (Response -> Response) -> STM ()
135       updateRes itr updator 
136           = updateItr itr itrResponse updator
137
138
139 completeUnconditionalHeaders :: Config -> Response -> IO Response
140 completeUnconditionalHeaders conf res
141     = return res >>= compServer >>= compDate >>= return
142       where
143         compServer res
144             = case getHeader "Server" res of
145                 Nothing -> return $ addHeader "Server" (cnfServerSoftware conf) res
146                 Just _  -> return res
147
148         compDate res
149             = case getHeader "Date" res of
150                 Nothing -> do time <- getClockTime
151                               return $ addHeader "Date" (formatHTTPDateTime time) res
152                 Just _  -> return res