1 module Network.HTTP.Lucu.RequestReader
2 ( requestReader -- Config -> ResTree -> Handle -> HostName -> InteractionQueue -> IO ()
6 import Control.Concurrent.STM
7 import Control.Exception
9 import qualified Data.ByteString.Lazy.Char8 as B
10 import Data.ByteString.Lazy.Char8 (ByteString)
14 import qualified Data.Sequence as S
15 import Data.Sequence (Seq, (<|), ViewR(..))
17 import Network.HTTP.Lucu.Config
18 import Network.HTTP.Lucu.Chunk
19 import Network.HTTP.Lucu.DefaultPage
20 import Network.HTTP.Lucu.HttpVersion
21 import Network.HTTP.Lucu.Interaction
22 import Network.HTTP.Lucu.Parser
23 import Network.HTTP.Lucu.Postprocess
24 import Network.HTTP.Lucu.Preprocess
25 import Network.HTTP.Lucu.Request
26 import Network.HTTP.Lucu.Response
27 import Network.HTTP.Lucu.Resource
28 import Prelude hiding (catch)
31 import GHC.Conc (unsafeIOToSTM)
33 requestReader :: Config -> ResTree -> Handle -> HostName -> InteractionQueue -> IO ()
34 requestReader cnf tree h host tQueue
35 = do catch (do input <- B.hGetContents h
36 acceptRequest input) $ \ exc ->
38 IOException _ -> return ()
39 AsyncException ThreadKilled -> return ()
40 BlockedIndefinitely -> putStrLn "requestReader: blocked indefinitely"
43 acceptRequest :: ByteString -> IO ()
45 -- キューに最大パイプライン深度以上のリクエストが溜まってゐる
47 = do atomically $ do queue <- readTVar tQueue
48 when (S.length queue >= cnfMaxPipelineDepth cnf)
51 -- リクエストを讀む。パースできない場合は直ちに 400 Bad
52 -- Request 應答を設定し、それを出力してから切斷するやう
53 -- に ResponseWriter に通知する。
54 case parse requestP input of
55 (Success req , input') -> acceptParsableRequest req input'
56 (IllegalInput, _ ) -> acceptNonparsableRequest BadRequest
57 (ReachedEOF , _ ) -> acceptNonparsableRequest BadRequest
59 acceptNonparsableRequest :: StatusCode -> IO ()
60 acceptNonparsableRequest status
61 = do itr <- newInteraction cnf host Nothing
63 resVersion = HttpVersion 1 1
67 atomically $ do writeItr itr itrResponse $ Just res
68 writeItr itr itrWillClose True
69 writeItr itr itrState Done
74 acceptParsableRequest :: Request -> ByteString -> IO ()
75 acceptParsableRequest req input
76 = do itr <- newInteraction cnf host (Just req)
80 isErr <- readItrF itr itrResponse (isError . resStatus)
81 if isErr == Just True then
82 acceptSemanticallyInvalidRequest itr input
84 case findResource tree $ (reqURI . fromJust . itrRequest) itr of
85 Nothing -- Resource が無かった
86 -> acceptRequestForNonexistentResource itr input
89 -> acceptRequestForExistentResource itr input rsrcDef
92 acceptSemanticallyInvalidRequest :: Interaction -> ByteString -> STM (IO ())
93 acceptSemanticallyInvalidRequest itr input
94 = do writeItr itr itrState Done
98 return $ acceptRequest input
100 acceptRequestForNonexistentResource :: Interaction -> ByteString -> STM (IO ())
101 acceptRequestForNonexistentResource itr input
102 = do let res = Response {
103 resVersion = HttpVersion 1 1
104 , resStatus = NotFound
107 writeItr itr itrResponse $ Just res
108 writeItr itr itrState Done
112 return $ acceptRequest input
114 acceptRequestForExistentResource :: Interaction -> ByteString -> ResourceDef -> STM (IO ())
115 acceptRequestForExistentResource itr input rsrcDef
116 = do requestHasBody <- readItr itr itrRequestHasBody id
117 writeItr itr itrState (if requestHasBody
121 return $ do runResource rsrcDef itr
122 if requestHasBody then
123 observeRequest itr input
127 observeRequest :: Interaction -> ByteString -> IO ()
128 observeRequest itr input
129 = do isChunked <- atomically $ readItr itr itrRequestIsChunked id
131 observeChunkedRequest itr input
133 observeNonChunkedRequest itr input
135 observeChunkedRequest :: Interaction -> ByteString -> IO ()
136 observeChunkedRequest itr input
139 do isOver <- readItr itr itrReqChunkIsOver id
141 return $ acceptRequest input
143 do wantedM <- readItr itr itrReqBodyWanted id
144 if wantedM == Nothing then
145 do wasteAll <- readItr itr itrReqBodyWasteAll id
148 do remainingM <- readItr itr itrReqChunkRemaining id
149 if fmap (> 0) remainingM == Just True then
152 do let (_, input') = B.splitAt (fromIntegral
153 $ fromJust remainingM) input
154 (footerR, input'') = parse chunkFooterP input'
156 if footerR == Success () then
158 do writeItr itr itrReqChunkRemaining $ Just 0
160 return $ observeChunkedRequest itr input''
162 return $ chunkWasMalformed itr
165 seekNextChunk itr input
171 do remainingM <- readItr itr itrReqChunkRemaining id
172 if fmap (> 0) remainingM == Just True then
175 do let wanted = fromJust wantedM
176 remaining = fromJust remainingM
177 bytesToRead = fromIntegral $ min wanted remaining
178 (chunk, input') = B.splitAt bytesToRead input
179 actualReadBytes = fromIntegral $ B.length chunk
180 newWanted = case wanted - actualReadBytes of
183 newRemaining = Just $ remaining - actualReadBytes
185 = do writeItr itr itrReqChunkRemaining newRemaining
186 writeItr itr itrReqBodyWanted newWanted
187 updateItr itr itrReceivedBody $ flip B.append chunk
189 if newRemaining == Just 0 then
191 case parse chunkFooterP input' of
194 return $ observeChunkedRequest itr input''
195 _ -> return $ chunkWasMalformed itr
199 return $ observeChunkedRequest itr input'
202 seekNextChunk itr input
205 seekNextChunk :: Interaction -> ByteString -> STM (IO ())
206 seekNextChunk itr input
207 = case parse chunkHeaderP input of
210 -> case parse chunkTrailerP input' of
212 -> do writeItr itr itrReqChunkLength $ Nothing
213 writeItr itr itrReqChunkRemaining $ Nothing
214 writeItr itr itrReqChunkIsOver True
216 return $ acceptRequest input''
217 _ -> return $ chunkWasMalformed itr
219 (Success len, input')
220 -> do writeItr itr itrReqChunkLength $ Just len
221 writeItr itr itrReqChunkRemaining $ Just len
223 return $ observeChunkedRequest itr input'
225 _ -> return $ chunkWasMalformed itr
227 chunkWasMalformed :: Interaction -> IO ()
228 chunkWasMalformed itr
229 = let res = Response {
230 resVersion = HttpVersion 1 1
231 , resStatus = BadRequest
235 atomically $ do writeItr itr itrResponse $ Just res
236 writeItr itr itrWillClose True
237 writeItr itr itrState Done
241 observeNonChunkedRequest :: Interaction -> ByteString -> IO ()
242 observeNonChunkedRequest itr input
245 do wantedM <- readItr itr itrReqBodyWanted id
246 if wantedM == Nothing then
247 do wasteAll <- readItr itr itrReqBodyWasteAll id
250 do remainingM <- readItr itr itrReqChunkRemaining id
252 let (_, input') = if remainingM == Nothing then
253 (B.takeWhile (\ _ -> True) input, B.empty)
255 B.splitAt (fromIntegral $ fromJust remainingM) input
257 writeItr itr itrReqChunkRemaining $ Just 0
258 writeItr itr itrReqChunkIsOver True
260 return $ acceptRequest input'
266 do remainingM <- readItr itr itrReqChunkRemaining id
268 let wanted = fromJust wantedM
269 bytesToRead = fromIntegral $ maybe wanted (min wanted) remainingM
270 (chunk, input') = B.splitAt bytesToRead input
272 (\ x -> x - (fromIntegral $ B.length chunk))
274 isOver = B.length chunk < bytesToRead || newRemaining == Just 0
276 writeItr itr itrReqChunkRemaining newRemaining
277 writeItr itr itrReqChunkIsOver isOver
278 writeItr itr itrReqBodyWanted Nothing
279 writeItr itr itrReceivedBody chunk
282 return $ acceptRequest input'
284 return $ observeNonChunkedRequest itr input'
287 enqueue :: Interaction -> STM ()
288 enqueue itr = do queue <- readTVar tQueue
289 writeTVar tQueue (itr <| queue)