2 module Network.HTTP.Lucu.RequestReader
7 import Control.Concurrent.STM
8 import Control.Exception
10 import qualified Data.ByteString.Lazy.Char8 as B
11 import Data.ByteString.Lazy.Char8 (ByteString)
15 import qualified Data.Sequence as S
16 import Data.Sequence (Seq, (<|), ViewR(..))
18 import Network.HTTP.Lucu.Config
19 import Network.HTTP.Lucu.Chunk
20 import Network.HTTP.Lucu.DefaultPage
21 import Network.HTTP.Lucu.HttpVersion
22 import Network.HTTP.Lucu.Interaction
23 import Network.HTTP.Lucu.Parser
24 import Network.HTTP.Lucu.Postprocess
25 import Network.HTTP.Lucu.Preprocess
26 import Network.HTTP.Lucu.Request
27 import Network.HTTP.Lucu.Response
28 import Network.HTTP.Lucu.Resource
29 import Network.HTTP.Lucu.Resource.Tree
30 import Prelude hiding (catch)
34 requestReader :: Config -> ResTree -> Handle -> SockAddr -> InteractionQueue -> IO ()
35 requestReader cnf tree h addr tQueue
36 = cnf `seq` tree `seq` h `seq` addr `seq` tQueue `seq`
37 do catch (do input <- B.hGetContents h
38 acceptRequest input) $ \ exc ->
40 IOException _ -> return ()
41 AsyncException ThreadKilled -> return ()
42 BlockedIndefinitely -> putStrLn "requestReader: blocked indefinitely"
45 acceptRequest :: ByteString -> IO ()
47 -- キューに最大パイプライン深度以上のリクエストが溜まってゐる
49 = do atomically $ do queue <- readTVar tQueue
50 when (S.length queue >= cnfMaxPipelineDepth cnf)
53 -- リクエストを讀む。パースできない場合は直ちに 400 Bad
54 -- Request 應答を設定し、それを出力してから切斷するやう
55 -- に ResponseWriter に通知する。
56 case parse requestP input of
57 (Success req , input') -> acceptParsableRequest req input'
58 (IllegalInput, _ ) -> acceptNonparsableRequest BadRequest
59 (ReachedEOF , _ ) -> acceptNonparsableRequest BadRequest
61 acceptNonparsableRequest :: StatusCode -> IO ()
62 acceptNonparsableRequest status
63 = do itr <- newInteraction cnf addr Nothing
64 atomically $ do updateItr itr itrResponse
68 writeItr itr itrWillClose True
69 writeItr itr itrState Done
74 acceptParsableRequest :: Request -> ByteString -> IO ()
75 acceptParsableRequest req input
76 = do itr <- newInteraction cnf addr (Just req)
80 isErr <- readItr itr itrResponse (isError . resStatus)
82 acceptSemanticallyInvalidRequest itr input
84 case findResource tree $ reqURI req of
85 Nothing -- Resource が無かった
86 -> acceptRequestForNonexistentResource itr input
88 Just (rsrcPath, rsrcDef) -- あった
89 -> acceptRequestForExistentResource itr input rsrcPath 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 updateItr itr itrResponse
106 writeItr itr itrState Done
110 return $ acceptRequest input
112 acceptRequestForExistentResource :: Interaction -> ByteString -> [String] -> ResourceDef -> STM (IO ())
113 acceptRequestForExistentResource oldItr input rsrcPath rsrcDef
114 = do let itr = oldItr { itrResourcePath = Just rsrcPath }
115 requestHasBody <- readItr itr itrRequestHasBody id
117 return $ do runResource rsrcDef itr
118 if requestHasBody then
119 observeRequest itr input
123 observeRequest :: Interaction -> ByteString -> IO ()
124 observeRequest itr input
125 = do isChunked <- atomically $ readItr itr itrRequestIsChunked id
127 observeChunkedRequest itr input
129 observeNonChunkedRequest itr input
131 observeChunkedRequest :: Interaction -> ByteString -> IO ()
132 observeChunkedRequest itr input
135 do isOver <- readItr itr itrReqChunkIsOver id
137 return $ acceptRequest input
139 do wantedM <- readItr itr itrReqBodyWanted id
140 if wantedM == Nothing then
141 do wasteAll <- readItr itr itrReqBodyWasteAll id
144 do remainingM <- readItr itr itrReqChunkRemaining id
145 if fmap (> 0) remainingM == Just True then
148 do let (_, input') = B.splitAt (fromIntegral
149 $ fromJust remainingM) input
150 (footerR, input'') = parse chunkFooterP input'
152 if footerR == Success () then
154 do writeItr itr itrReqChunkRemaining $ Just 0
156 return $ observeChunkedRequest itr input''
158 return $ chunkWasMalformed itr
161 seekNextChunk itr input
167 do remainingM <- readItr itr itrReqChunkRemaining id
168 if fmap (> 0) remainingM == Just True then
171 do let wanted = fromJust wantedM
172 remaining = fromJust remainingM
173 bytesToRead = fromIntegral $ min wanted remaining
174 (chunk, input') = B.splitAt bytesToRead input
175 actualReadBytes = fromIntegral $ B.length chunk
176 newWanted = case wanted - actualReadBytes of
179 newRemaining = Just $ remaining - actualReadBytes
181 = do writeItr itr itrReqChunkRemaining newRemaining
182 writeItr itr itrReqBodyWanted newWanted
183 updateItr itr itrReceivedBody $ flip B.append chunk
185 if newRemaining == Just 0 then
187 case parse chunkFooterP input' of
190 return $ observeChunkedRequest itr input''
191 _ -> return $ chunkWasMalformed itr
195 return $ observeChunkedRequest itr input'
198 seekNextChunk itr input
201 seekNextChunk :: Interaction -> ByteString -> STM (IO ())
202 seekNextChunk itr input
203 = case parse chunkHeaderP input of
206 -> case parse chunkTrailerP input' of
208 -> do writeItr itr itrReqChunkLength $ Nothing
209 writeItr itr itrReqChunkRemaining $ Nothing
210 writeItr itr itrReqChunkIsOver True
212 return $ acceptRequest input''
213 _ -> return $ chunkWasMalformed itr
215 (Success len, input')
216 -> do writeItr itr itrReqChunkLength $ Just len
217 writeItr itr itrReqChunkRemaining $ Just len
219 return $ observeChunkedRequest itr input'
221 _ -> return $ chunkWasMalformed itr
223 chunkWasMalformed :: Interaction -> IO ()
224 chunkWasMalformed itr
225 = atomically $ do updateItr itr itrResponse
227 resStatus = BadRequest
229 writeItr itr itrWillClose True
230 writeItr itr itrState Done
234 observeNonChunkedRequest :: Interaction -> ByteString -> IO ()
235 observeNonChunkedRequest itr input
238 do wantedM <- readItr itr itrReqBodyWanted id
239 if wantedM == Nothing then
240 do wasteAll <- readItr itr itrReqBodyWasteAll id
243 do remainingM <- readItr itr itrReqChunkRemaining id
245 let (_, input') = if remainingM == Nothing then
246 (B.takeWhile (\ _ -> True) input, B.empty)
248 B.splitAt (fromIntegral $ fromJust remainingM) input
250 writeItr itr itrReqChunkRemaining $ Just 0
251 writeItr itr itrReqChunkIsOver True
253 return $ acceptRequest input'
259 do remainingM <- readItr itr itrReqChunkRemaining id
261 let wanted = fromJust wantedM
262 bytesToRead = fromIntegral $ maybe wanted (min wanted) remainingM
263 (chunk, input') = B.splitAt bytesToRead input
265 (\ x -> x - (fromIntegral $ B.length chunk))
267 isOver = B.length chunk < bytesToRead || newRemaining == Just 0
269 writeItr itr itrReqChunkRemaining newRemaining
270 writeItr itr itrReqChunkIsOver isOver
271 writeItr itr itrReqBodyWanted Nothing
272 writeItr itr itrReceivedBody chunk
275 return $ acceptRequest input'
277 return $ observeNonChunkedRequest itr input'
280 enqueue :: Interaction -> STM ()
281 enqueue itr = do queue <- readTVar tQueue
282 writeTVar tQueue (itr <| queue)