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.Format
17 import Network.HTTP.Lucu.HttpVersion
18 import Network.HTTP.Lucu.Interaction
19 import Network.HTTP.Lucu.Postprocess
20 import Network.HTTP.Lucu.Response
21 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 = cnf `seq` h `seq` tQueue `seq` readerTID `seq`
32 catch awaitSomethingToWrite $ \ exc ->
34 IOException _ -> return ()
35 AsyncException ThreadKilled -> return ()
36 BlockedIndefinitely -> putStrLn "requestWriter: blocked indefinitely"
39 awaitSomethingToWrite :: IO ()
44 queue <- readTVar tQueue
48 -- GettingBody 状態にあり、Continue が期待され
49 -- てゐて、それがまだ送信前なのであれば、
52 _ :> itr -> do state <- readItr itr itrState id
54 if state == GettingBody then
55 writeContinueIfNecessary itr
57 if state >= DecidingBody then
58 writeHeaderOrBodyIfNecessary itr
63 writeContinueIfNecessary :: Interaction -> STM (IO ())
64 writeContinueIfNecessary itr
66 do expectedContinue <- readItr itr itrExpectedContinue id
67 if expectedContinue then
68 do wroteContinue <- readItr itr itrWroteContinue id
73 do reqBodyWanted <- readItr itr itrReqBodyWanted id
74 if reqBodyWanted /= Nothing then
75 return $ writeContinue itr
81 writeHeaderOrBodyIfNecessary :: Interaction -> STM (IO ())
82 writeHeaderOrBodyIfNecessary itr
83 -- DecidingBody 以降の状態にあり、まだヘッダを出力する前であ
84 -- れば、ヘッダを出力する。ヘッダ出力後であり、bodyToSend が
85 -- 空でなければ、それを出力する。空である時は、もし状態が
88 do wroteHeader <- readItr itr itrWroteHeader id
90 if not wroteHeader then
91 return $ writeHeader itr
93 do bodyToSend <- readItr itr itrBodyToSend id
95 if B.null bodyToSend then
96 do state <- readItr itr itrState id
99 return $! finalize itr
103 return $! writeBodyChunk itr
105 writeContinue :: Interaction -> IO ()
108 do let cont = Response {
109 resVersion = HttpVersion 1 1
110 , resStatus = Continue
113 cont' <- completeUnconditionalHeaders cnf cont
116 atomically $! writeItr itr itrWroteContinue True
117 awaitSomethingToWrite
119 writeHeader :: Interaction -> IO ()
122 do res <- atomically $! do writeItr itr itrWroteHeader True
123 readItr itr itrResponse id
126 awaitSomethingToWrite
128 writeBodyChunk :: Interaction -> IO ()
131 do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id
132 willChunkBody <- atomically $! readItr itr itrWillChunkBody id
133 chunk <- atomically $! do chunk <- readItr itr itrBodyToSend id
134 writeItr itr itrBodyToSend B.empty
136 unless willDiscardBody
137 $ do if willChunkBody then
138 do hPutStr h (fmtHex False 0 $! fromIntegral $! B.length chunk)
145 awaitSomethingToWrite
147 finishBodyChunk :: Interaction -> IO ()
150 do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id
151 willChunkBody <- atomically $! readItr itr itrWillChunkBody id
152 when (not willDiscardBody && willChunkBody)
153 $ hPutStr h "0\r\n\r\n" >> hFlush h
155 finalize :: Interaction -> IO ()
158 do finishBodyChunk itr
159 willClose <- atomically $!
160 do queue <- readTVar tQueue
162 case S.viewr queue of
163 remaining :> _ -> writeTVar tQueue remaining
165 readItr itr itrWillClose id
167 -- reader は恐らく hWaitForInput してゐる最中なので、
168 -- スレッドを豫め殺して置かないとをかしくなる。
169 do killThread readerTID
172 awaitSomethingToWrite