2 module Network.HTTP.Lucu.ResponseWriter
7 import qualified Data.ByteString.Lazy.Char8 as B
8 import Control.Concurrent
9 import Control.Concurrent.STM
10 import Control.Exception
12 import qualified Data.Sequence as S
13 import Data.Sequence (ViewR(..))
14 import Network.HTTP.Lucu.Config
15 import Network.HTTP.Lucu.Format
16 import Network.HTTP.Lucu.HttpVersion
17 import Network.HTTP.Lucu.Interaction
18 import Network.HTTP.Lucu.Postprocess
19 import Network.HTTP.Lucu.Response
20 import Prelude hiding (catch)
24 responseWriter :: Config -> Handle -> InteractionQueue -> ThreadId -> IO ()
25 responseWriter cnf h tQueue readerTID
26 = cnf `seq` h `seq` tQueue `seq` readerTID `seq`
27 catch awaitSomethingToWrite $ \ exc ->
29 IOException _ -> return ()
30 AsyncException ThreadKilled -> return ()
31 BlockedIndefinitely -> putStrLn "requestWriter: blocked indefinitely"
34 awaitSomethingToWrite :: IO ()
39 queue <- readTVar tQueue
43 -- GettingBody 状態にあり、Continue が期待され
44 -- てゐて、それがまだ送信前なのであれば、
47 _ :> itr -> do state <- readItr itr itrState id
49 if state == GettingBody then
50 writeContinueIfNecessary itr
52 if state >= DecidingBody then
53 writeHeaderOrBodyIfNecessary itr
58 writeContinueIfNecessary :: Interaction -> STM (IO ())
59 writeContinueIfNecessary itr
61 do expectedContinue <- readItr itr itrExpectedContinue id
62 if expectedContinue then
63 do wroteContinue <- readItr itr itrWroteContinue id
68 do reqBodyWanted <- readItr itr itrReqBodyWanted id
69 if reqBodyWanted /= Nothing then
70 return $ writeContinue itr
76 writeHeaderOrBodyIfNecessary :: Interaction -> STM (IO ())
77 writeHeaderOrBodyIfNecessary itr
78 -- DecidingBody 以降の状態にあり、まだヘッダを出力する前であ
79 -- れば、ヘッダを出力する。ヘッダ出力後であり、bodyToSend が
80 -- 空でなければ、それを出力する。空である時は、もし状態が
83 do wroteHeader <- readItr itr itrWroteHeader id
85 if not wroteHeader then
86 return $ writeHeader itr
88 do bodyToSend <- readItr itr itrBodyToSend id
90 if B.null bodyToSend then
91 do state <- readItr itr itrState id
94 return $! finalize itr
98 return $! writeBodyChunk itr
100 writeContinue :: Interaction -> IO ()
103 do let cont = Response {
104 resVersion = HttpVersion 1 1
105 , resStatus = Continue
108 cont' <- completeUnconditionalHeaders cnf cont
111 atomically $! writeItr itr itrWroteContinue True
112 awaitSomethingToWrite
114 writeHeader :: Interaction -> IO ()
117 do res <- atomically $! do writeItr itr itrWroteHeader True
118 readItr itr itrResponse id
121 awaitSomethingToWrite
123 writeBodyChunk :: Interaction -> IO ()
126 do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id
127 willChunkBody <- atomically $! readItr itr itrWillChunkBody id
128 chunk <- atomically $! do chunk <- readItr itr itrBodyToSend id
129 writeItr itr itrBodyToSend B.empty
131 unless willDiscardBody
132 $ do if willChunkBody then
133 do hPutStr h (fmtHex False 0 $! fromIntegral $! B.length chunk)
140 awaitSomethingToWrite
142 finishBodyChunk :: Interaction -> IO ()
145 do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id
146 willChunkBody <- atomically $! readItr itr itrWillChunkBody id
147 when (not willDiscardBody && willChunkBody)
148 $ hPutStr h "0\r\n\r\n" >> hFlush h
150 finalize :: Interaction -> IO ()
153 do finishBodyChunk itr
154 willClose <- atomically $!
155 do queue <- readTVar tQueue
157 case S.viewr queue of
158 remaining :> _ -> writeTVar tQueue remaining
160 readItr itr itrWillClose id
162 -- reader は恐らく hWaitForInput してゐる最中なので、
163 -- スレッドを豫め殺して置かないとをかしくなる。
164 do killThread readerTID
167 awaitSomethingToWrite