2 module Network.HTTP.Lucu.ResponseWriter
7 import qualified Data.ByteString.Lazy.Char8 as B
8 import Data.ByteString.Lazy.Char8 (ByteString)
9 import Control.Concurrent.STM
10 import Control.Exception
13 import qualified Data.Sequence as S
14 import Data.Sequence (Seq, ViewR(..))
15 import Network.HTTP.Lucu.Config
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 import Control.Concurrent
26 import GHC.Conc (unsafeIOToSTM)
29 responseWriter :: Config -> Handle -> InteractionQueue -> ThreadId -> IO ()
30 responseWriter cnf h tQueue readerTID
31 = catch awaitSomethingToWrite $ \ exc ->
33 IOException _ -> return ()
34 AsyncException ThreadKilled -> return ()
35 BlockedIndefinitely -> putStrLn "requestWriter: blocked indefinitely"
38 awaitSomethingToWrite :: IO ()
43 queue <- readTVar tQueue
46 let _ :> itr = S.viewr queue
48 -- GettingBody 状態にあり、Continue が期待され
49 -- てゐて、それがまだ送信前なのであれば、
51 state <- readItr itr itrState id
53 if state == GettingBody then
54 writeContinueIfNecessary itr
56 if state >= DecidingBody then
57 writeHeaderOrBodyIfNecessary itr
62 writeContinueIfNecessary :: Interaction -> STM (IO ())
63 writeContinueIfNecessary itr
64 = do expectedContinue <- readItr itr itrExpectedContinue id
65 if expectedContinue then
66 do wroteContinue <- readItr itr itrWroteContinue id
71 do reqBodyWanted <- readItr itr itrReqBodyWanted id
72 if reqBodyWanted /= Nothing then
73 return $ writeContinue itr
79 writeHeaderOrBodyIfNecessary :: Interaction -> STM (IO ())
80 writeHeaderOrBodyIfNecessary itr
81 -- DecidingBody 以降の状態にあり、まだヘッダを出力する前であ
82 -- れば、ヘッダを出力する。ヘッダ出力後であり、bodyToSend が
83 -- 空でなければ、それを出力する。空である時は、もし状態が
85 = do wroteHeader <- readItr itr itrWroteHeader id
87 if not wroteHeader then
88 return $ writeHeader itr
90 do bodyToSend <- readItr itr itrBodyToSend id
92 if B.null bodyToSend then
93 do state <- readItr itr itrState id
100 return $ writeBodyChunk itr
102 writeContinue :: Interaction -> IO ()
104 = do let cont = Response {
105 resVersion = HttpVersion 1 1
106 , resStatus = Continue
109 cont' <- completeUnconditionalHeaders cnf cont
112 atomically $ writeItr itr itrWroteContinue True
113 awaitSomethingToWrite
115 writeHeader :: Interaction -> IO ()
117 = do res <- atomically $ do writeItr itr itrWroteHeader True
118 readItr itr itrResponse id
119 hPutResponse h (fromJust res)
121 awaitSomethingToWrite
123 writeBodyChunk :: Interaction -> IO ()
125 = do willDiscardBody <- atomically $ readItr itr itrWillDiscardBody id
126 willChunkBody <- atomically $ readItr itr itrWillChunkBody id
127 chunk <- atomically $ do chunk <- readItr itr itrBodyToSend id
128 writeItr itr itrBodyToSend B.empty
130 unless willDiscardBody
131 $ do if willChunkBody then
132 do hPrintf h "%x\r\n" (toInteger $ B.length chunk)
138 awaitSomethingToWrite
140 finishBodyChunk :: Interaction -> IO ()
142 = do willDiscardBody <- atomically $ readItr itr itrWillDiscardBody id
143 willChunkBody <- atomically $ readItr itr itrWillChunkBody id
144 when (not willDiscardBody && willChunkBody)
145 $ hPutStr h "0\r\n\r\n" >> hFlush h
147 finalize :: Interaction -> IO ()
149 = do finishBodyChunk itr
150 willClose <- atomically $ do queue <- readTVar tQueue
152 let (remaining :> _) = S.viewr queue
153 writeTVar tQueue remaining
155 readItr itr itrWillClose id
157 -- reader は恐らく hWaitForInput してゐる最中なので、
158 -- スレッドを豫め殺して置かないとをかしくなる。
159 do killThread readerTID
162 awaitSomethingToWrite