]> gitweb @ CieloNegro.org - Lucu.git/blob - Network/HTTP/Lucu/Preprocess.hs
Documentation
[Lucu.git] / Network / HTTP / Lucu / Preprocess.hs
1 -- #hide
2 module Network.HTTP.Lucu.Preprocess
3     ( preprocess
4     )
5     where
6
7 import           Control.Concurrent.STM
8 import           Control.Monad
9 import           Data.Char
10 import           Data.Maybe
11 import           Network.HTTP.Lucu.Headers
12 import           Network.HTTP.Lucu.HttpVersion
13 import           Network.HTTP.Lucu.Interaction
14 import           Network.HTTP.Lucu.Request
15 import           Network.HTTP.Lucu.Response
16 import           Network.HTTP.Lucu.Utils
17 import           Network.URI
18
19 {-
20
21   * Expect: に問題があった場合は 417 Expectation Failed に設定。
22     100-continue 以外のものは全部 417 に。
23
24   * Transfer-Encoding: に問題があったら 501 Not Implemented にする。具
25     体的には、identity でも chunked でもなければ 501 Not Implemented に
26     する。
27
28   * HTTP/1.1 リクエストであり、URI にホスト名が無く、Host: ヘッダも無い
29     場合には 400 Bad Request にする。
30
31   * メソッドが GET, HEAD, POST, PUT, DELETE の何れでもない場合は 501
32     Not Implemented にする。
33
34   * HTTP/1.0 でも HTTP/1.1 でもないリクエストに對しては 505 HTTP
35     Version Not Supported を返す。
36
37   * POST または PUT に Content-Length も Transfer-Encoding も無い時は、
38     411 Length Required にする。
39
40   * Content-Length の値が數値でなかったり負だったりしたら 400 Bad
41     Request にする。
42
43   * willDiscardBody その他の變數を設定する。
44
45 -}
46
47 import GHC.Conc (unsafeIOToSTM)
48
49 preprocess :: Interaction -> STM ()
50 preprocess itr
51     = do let req    = fromJust $ itrRequest itr
52              reqVer = reqVersion req
53
54          if reqVer /= HttpVersion 1 0 &&
55             reqVer /= HttpVersion 1 1 then
56
57              do setStatus itr HttpVersionNotSupported
58                 writeItr itr itrWillClose True
59
60            else
61              do if reqVer == HttpVersion 1 0 then
62                     -- HTTP/1.0 では Keep-Alive できない
63                     writeItr itr itrWillClose True
64                   else
65                     -- URI または Host: ヘッダのどちらかにホストが無ければ
66                     -- ならない。
67                     when (uriAuthority (reqURI req) == Nothing &&
68                           getHeader "Host" req      == Nothing)
69                              $ setStatus itr BadRequest
70
71                 case reqMethod req of
72                   GET  -> return ()
73                   HEAD -> writeItr itr itrWillDiscardBody True
74                   POST -> writeItr itr itrRequestHasBody True
75                   PUT  -> writeItr itr itrRequestHasBody True
76                   _    -> setStatus itr NotImplemented
77                   
78                 mapM_ (preprocessHeader itr) (reqHeaders req)
79     where
80       setStatus itr status
81           = writeItr itr itrResponse $ Just (Response {
82                                                resVersion = HttpVersion 1 1
83                                              , resStatus  = status
84                                              , resHeaders = []
85                                              })
86
87       preprocessHeader itr (name, value)
88           = case map toLower name of
89
90               "expect"
91                   -> if value `noCaseEq` "100-continue" then
92                          writeItr itr itrExpectedContinue True
93                      else
94                          setStatus itr ExpectationFailed
95
96               "transfer-encoding"
97                   -> case map toLower value of
98                        "identity" -> return ()
99                        "chunked"  -> writeItr itr itrRequestIsChunked True
100                        _          -> setStatus itr NotImplemented
101
102               "content-length"
103                   -> if all isDigit value then
104                          do let len = read value
105                             writeItr itr itrReqChunkLength    $ Just len
106                             writeItr itr itrReqChunkRemaining $ Just len
107                      else
108                          setStatus itr BadRequest
109
110               "connection"
111                   -> case map toLower value of
112                        "close"      -> writeItr itr itrWillClose True
113                        _            -> return ()
114
115               _ -> return ()