]> gitweb @ CieloNegro.org - Lucu.git/blob - Network/HTTP/Lucu/Postprocess.hs
Transfer-Encoding is always overwritten / foundEntity refuses POST requests / Documen...
[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 res <- readItr itr itrResponse id
60          let sc = resStatus res
61
62          when (not $ any (\ p -> p sc) [isSuccessful, isRedirection, isError])
63                   $ abortSTM InternalServerError []
64                         $ Just ("The status code is not good for a final status: "
65                                 ++ show sc)
66
67          when (sc == MethodNotAllowed && getHeader "Allow" res == Nothing)
68                   $ abortSTM InternalServerError []
69                         $ Just ("The status was " ++ show sc ++ " but no Allow header.")
70
71          when (sc /= NotModified && isRedirection sc && getHeader "Location" res == Nothing)
72                   $ abortSTM InternalServerError []
73                         $ Just ("The status code was " ++ show sc ++ " but no Location header.")
74
75          when (itrRequest itr /= Nothing)
76               $ relyOnRequest itr
77
78          -- itrResponse の内容は relyOnRequest によって變へられてゐる可
79          -- 能性が高い。
80          do oldRes <- readItr itr itrResponse id
81             newRes <- unsafeIOToSTM
82                       $ completeUnconditionalHeaders (itrConfig itr) oldRes
83             writeItr itr itrResponse newRes
84     where
85       relyOnRequest :: Interaction -> STM ()
86       relyOnRequest itr
87           = do status <- readItr itr itrResponse resStatus
88
89                let req         = fromJust $ itrRequest itr
90                    reqVer      = reqVersion req
91                    canHaveBody = if reqMethod req == HEAD then
92                                      False
93                                  else
94                                      not (isInformational status ||
95                                           status == NoContent    ||
96                                           status == ResetContent ||
97                                           status == NotModified    )
98
99                updateRes itr $ deleteHeader "Content-Length"
100                updateRes itr $ deleteHeader "Transfer-Encoding"
101
102                cType <- readHeader itr "Content-Type"
103                when (cType == Nothing)
104                         $ updateRes itr $ setHeader "Content-Type" defaultPageContentType
105
106                if canHaveBody then
107                    when (reqVer == HttpVersion 1 1)
108                             $ do updateRes itr $ setHeader "Transfer-Encoding" "chunked"
109                                  writeItr itr itrWillChunkBody True
110                  else
111                    -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
112                    when (reqMethod req /= HEAD)
113                             $ do updateRes itr $ deleteHeader "Content-Type"
114                                  updateRes itr $ deleteHeader "Etag"
115                                  updateRes itr $ deleteHeader "Last-Modified"
116
117                conn <- readHeader itr "Connection"
118                case fmap (map toLower) conn of
119                  Just "close" -> writeItr itr itrWillClose True
120                  _            -> return ()
121
122                willClose <- readItr itr itrWillClose id
123                when willClose
124                         $ updateRes itr $ setHeader "Connection" "close"
125
126                when (reqMethod req == HEAD || not canHaveBody)
127                         $ writeTVar (itrWillDiscardBody itr) True
128
129       readHeader :: Interaction -> String -> STM (Maybe String)
130       readHeader itr name
131           = readItr itr itrResponse $ getHeader name
132
133       updateRes :: Interaction -> (Response -> Response) -> STM ()
134       updateRes itr updator 
135           = updateItr itr itrResponse updator
136
137
138 completeUnconditionalHeaders :: Config -> Response -> IO Response
139 completeUnconditionalHeaders conf res
140     = return res >>= compServer >>= compDate >>= return
141       where
142         compServer res
143             = case getHeader "Server" res of
144                 Nothing -> return $ addHeader "Server" (cnfServerSoftware conf) res
145                 Just _  -> return res
146
147         compDate res
148             = case getHeader "Date" res of
149                 Nothing -> do time <- getClockTime
150                               return $ addHeader "Date" (formatHTTPDateTime time) res
151                 Just _  -> return res