6 module Network.HTTP.Lucu.RequestReader
11 import Control.Concurrent.STM
12 import Control.Exception
14 import qualified Data.ByteString.Lazy.Char8 as B
15 import Data.ByteString.Lazy.Char8 (ByteString)
17 import qualified Data.Sequence as S
18 import Data.Sequence ((<|))
19 import GHC.Conc (unsafeIOToSTM)
21 import Network.HTTP.Lucu.Config
22 import Network.HTTP.Lucu.Chunk
23 import Network.HTTP.Lucu.DefaultPage
24 import Network.HTTP.Lucu.HandleLike
25 import Network.HTTP.Lucu.Interaction
26 import Network.HTTP.Lucu.Parser
27 import Network.HTTP.Lucu.Postprocess
28 import Network.HTTP.Lucu.Preprocess
29 import Network.HTTP.Lucu.Request
30 import Network.HTTP.Lucu.Response
31 import Network.HTTP.Lucu.Resource.Tree
32 import Prelude hiding (catch)
33 import System.IO (stderr)
35 requestReader :: HandleLike h => Config -> ResTree -> [FallbackHandler] -> h -> PortNumber -> SockAddr -> InteractionQueue -> IO ()
36 requestReader !cnf !tree !fbs !h !port !addr !tQueue
37 = do input <- hGetLBS h
40 [ Handler (( \ _ -> return () ) :: IOException -> IO ())
41 , Handler ( \ ThreadKilled -> return () )
42 , Handler ( \ BlockedIndefinitelyOnSTM -> hPutStrLn stderr "requestReader: blocked indefinitely" )
43 , Handler (( \ e -> hPutStrLn stderr (show e) ) :: SomeException -> IO ())
46 acceptRequest :: ByteString -> IO ()
48 -- キューに最大パイプライン深度以上のリクエストが溜まってゐる
50 = {-# SCC "acceptRequest" #-}
51 do atomically $ do queue <- readTVar tQueue
52 when (S.length queue >= cnfMaxPipelineDepth cnf)
55 -- リクエストを讀む。パースできない場合は直ちに 400 Bad
56 -- Request 應答を設定し、それを出力してから切斷するやう
57 -- に ResponseWriter に通知する。
58 case parse requestP input of
59 (# Success req , input' #) -> acceptParsableRequest req input'
60 (# IllegalInput, _ #) -> acceptNonparsableRequest BadRequest
61 (# ReachedEOF , _ #) -> acceptNonparsableRequest BadRequest
63 acceptNonparsableRequest :: StatusCode -> IO ()
64 acceptNonparsableRequest status
65 = {-# SCC "acceptNonparsableRequest" #-}
66 do itr <- newInteraction cnf port addr Nothing Nothing
67 atomically $ do updateItr itr itrResponse
71 writeItr itr itrWillClose True
72 writeItr itr itrState Done
77 acceptParsableRequest :: Request -> ByteString -> IO ()
78 acceptParsableRequest req input
79 = {-# SCC "acceptParsableRequest" #-}
80 do cert <- hGetPeerCert h
81 itr <- newInteraction cnf port addr cert (Just req)
85 isErr <- readItr itr itrResponse (isError . resStatus)
87 acceptSemanticallyInvalidRequest itr input
89 do rsrcM <- unsafeIOToSTM $ findResource tree fbs $ reqURI req
91 Nothing -- Resource が無かった
92 -> acceptRequestForNonexistentResource itr input
94 Just (rsrcPath, rsrcDef) -- あった
95 -> acceptRequestForExistentResource itr input rsrcPath rsrcDef
98 acceptSemanticallyInvalidRequest :: Interaction -> ByteString -> STM (IO ())
99 acceptSemanticallyInvalidRequest itr input
100 = {-# SCC "acceptSemanticallyInvalidRequest" #-}
101 do writeItr itr itrState Done
105 return $ acceptRequest input
107 acceptRequestForNonexistentResource :: Interaction -> ByteString -> STM (IO ())
108 acceptRequestForNonexistentResource itr input
109 = {-# SCC "acceptRequestForNonexistentResource" #-}
110 do updateItr itr itrResponse
114 writeItr itr itrState Done
118 return $ acceptRequest input
120 acceptRequestForExistentResource :: Interaction -> ByteString -> [String] -> ResourceDef -> STM (IO ())
121 acceptRequestForExistentResource oldItr input rsrcPath rsrcDef
122 = {-# SCC "acceptRequestForExistentResource" #-}
123 do let itr = oldItr { itrResourcePath = Just rsrcPath }
124 requestHasBody <- readItr itr itrRequestHasBody id
126 return $ do _ <- runResource rsrcDef itr
127 if requestHasBody then
128 observeRequest itr input
132 observeRequest :: Interaction -> ByteString -> IO ()
133 observeRequest itr input
134 = {-# SCC "observeRequest" #-}
135 do isChunked <- atomically $ readItr itr itrRequestIsChunked id
137 observeChunkedRequest itr input
139 observeNonChunkedRequest itr input
141 observeChunkedRequest :: Interaction -> ByteString -> IO ()
142 observeChunkedRequest itr input
143 = {-# SCC "observeChunkedRequest" #-}
146 do isOver <- readItr itr itrReqChunkIsOver id
148 return $ acceptRequest input
150 do wantedM <- readItr itr itrReqBodyWanted id
151 if wantedM == Nothing then
152 do wasteAll <- readItr itr itrReqBodyWasteAll id
155 do remainingM <- readItr itr itrReqChunkRemaining id
156 if fmap (> 0) remainingM == Just True then
159 do let (_, input') = B.splitAt (fromIntegral
160 $ fromJust remainingM) input
161 (# footerR, input'' #) = parse chunkFooterP input'
163 if footerR == Success () then
165 do writeItr itr itrReqChunkRemaining $ Just 0
167 return $ observeChunkedRequest itr input''
169 return $ chunkWasMalformed itr
172 seekNextChunk itr input
178 do remainingM <- readItr itr itrReqChunkRemaining id
179 if fmap (> 0) remainingM == Just True then
182 do let wanted = fromJust wantedM
183 remaining = fromJust remainingM
184 bytesToRead = fromIntegral $ min wanted remaining
185 (chunk, input') = B.splitAt bytesToRead input
186 actualReadBytes = fromIntegral $ B.length chunk
187 newWanted = case wanted - actualReadBytes of
190 newRemaining = Just $ remaining - actualReadBytes
192 = do writeItr itr itrReqChunkRemaining newRemaining
193 writeItr itr itrReqBodyWanted newWanted
194 updateItr itr itrReceivedBody $ flip B.append chunk
196 if newRemaining == Just 0 then
198 case parse chunkFooterP input' of
199 (# Success _, input'' #)
201 return $ observeChunkedRequest itr input''
203 -> return $ chunkWasMalformed itr
207 return $ observeChunkedRequest itr input'
210 seekNextChunk itr input
213 seekNextChunk :: Interaction -> ByteString -> STM (IO ())
214 seekNextChunk itr input
215 = {-# SCC "seekNextChunk" #-}
216 case parse chunkHeaderP input of
218 (# Success 0, input' #)
219 -> case parse chunkTrailerP input' of
220 (# Success _, input'' #)
221 -> do writeItr itr itrReqChunkLength $ Nothing
222 writeItr itr itrReqChunkRemaining $ Nothing
223 writeItr itr itrReqChunkIsOver True
225 return $ acceptRequest input''
227 -> return $ chunkWasMalformed itr
229 (# Success len, input' #)
230 -> do writeItr itr itrReqChunkLength $ Just len
231 writeItr itr itrReqChunkRemaining $ Just len
233 return $ observeChunkedRequest itr input'
236 -> return $ chunkWasMalformed itr
238 chunkWasMalformed :: Interaction -> IO ()
239 chunkWasMalformed itr
240 = {-# SCC "chunkWasMalformed" #-}
241 atomically $ do updateItr itr itrResponse
243 resStatus = BadRequest
245 writeItr itr itrWillClose True
246 writeItr itr itrState Done
250 observeNonChunkedRequest :: Interaction -> ByteString -> IO ()
251 observeNonChunkedRequest itr input
252 = {-# SCC "observeNonChunkedRequest" #-}
255 do wantedM <- readItr itr itrReqBodyWanted id
256 if wantedM == Nothing then
257 do wasteAll <- readItr itr itrReqBodyWasteAll id
260 do remainingM <- readItr itr itrReqChunkRemaining id
262 let (_, input') = if remainingM == Nothing then
263 (B.takeWhile (\ _ -> True) input, B.empty)
265 B.splitAt (fromIntegral $ fromJust remainingM) input
267 writeItr itr itrReqChunkRemaining $ Just 0
268 writeItr itr itrReqChunkIsOver True
270 return $ acceptRequest input'
276 do remainingM <- readItr itr itrReqChunkRemaining id
278 let wanted = fromJust wantedM
279 bytesToRead = fromIntegral $ maybe wanted (min wanted) remainingM
280 (chunk, input') = B.splitAt bytesToRead input
282 (\ x -> x - (fromIntegral $ B.length chunk))
284 isOver = B.length chunk < bytesToRead || newRemaining == Just 0
286 writeItr itr itrReqChunkRemaining newRemaining
287 writeItr itr itrReqChunkIsOver isOver
288 writeItr itr itrReqBodyWanted Nothing
289 writeItr itr itrReceivedBody chunk
292 return $ acceptRequest input'
294 return $ observeNonChunkedRequest itr input'
297 enqueue :: Interaction -> STM ()
298 enqueue itr = {-# SCC "enqueue" #-}
299 do queue <- readTVar tQueue
300 writeTVar tQueue (itr <| queue)