1 module Network.HTTP.Lucu.ResponseWriter
6 import qualified Data.ByteString.Lazy.Char8 as B
7 import Control.Concurrent
8 import Control.Concurrent.STM
9 import Control.Exception
11 import qualified Data.Sequence as S
12 import Data.Sequence (ViewR(..))
13 import Network.HTTP.Lucu.Config
14 import Network.HTTP.Lucu.Format
15 import Network.HTTP.Lucu.HttpVersion
16 import Network.HTTP.Lucu.Interaction
17 import Network.HTTP.Lucu.Postprocess
18 import Network.HTTP.Lucu.Response
19 import Prelude hiding (catch)
23 responseWriter :: Config -> Handle -> InteractionQueue -> ThreadId -> IO ()
24 responseWriter cnf h tQueue readerTID
25 = cnf `seq` h `seq` tQueue `seq` readerTID `seq`
26 catch awaitSomethingToWrite $ \ exc ->
28 IOException _ -> return ()
29 AsyncException ThreadKilled -> return ()
30 BlockedIndefinitely -> putStrLn "requestWriter: blocked indefinitely"
33 awaitSomethingToWrite :: IO ()
38 queue <- readTVar tQueue
42 -- GettingBody 状態にあり、Continue が期待され
43 -- てゐて、それがまだ送信前なのであれば、
46 _ :> itr -> do state <- readItr itr itrState id
48 if state == GettingBody then
49 writeContinueIfNecessary itr
51 if state >= DecidingBody then
52 writeHeaderOrBodyIfNecessary itr
57 writeContinueIfNecessary :: Interaction -> STM (IO ())
58 writeContinueIfNecessary itr
60 do expectedContinue <- readItr itr itrExpectedContinue id
61 if expectedContinue then
62 do wroteContinue <- readItr itr itrWroteContinue id
67 do reqBodyWanted <- readItr itr itrReqBodyWanted id
68 if reqBodyWanted /= Nothing then
69 return $ writeContinue itr
75 writeHeaderOrBodyIfNecessary :: Interaction -> STM (IO ())
76 writeHeaderOrBodyIfNecessary itr
77 -- DecidingBody 以降の状態にあり、まだヘッダを出力する前であ
78 -- れば、ヘッダを出力する。ヘッダ出力後であり、bodyToSend が
79 -- 空でなければ、それを出力する。空である時は、もし状態が
82 do wroteHeader <- readItr itr itrWroteHeader id
84 if not wroteHeader then
85 return $ writeHeader itr
87 do bodyToSend <- readItr itr itrBodyToSend id
89 if B.null bodyToSend then
90 do state <- readItr itr itrState id
93 return $! finalize itr
97 return $! writeBodyChunk itr
99 writeContinue :: Interaction -> IO ()
102 do let cont = Response {
103 resVersion = HttpVersion 1 1
104 , resStatus = Continue
107 cont' <- completeUnconditionalHeaders cnf cont
110 atomically $! writeItr itr itrWroteContinue True
111 awaitSomethingToWrite
113 writeHeader :: Interaction -> IO ()
116 do res <- atomically $! do writeItr itr itrWroteHeader True
117 readItr itr itrResponse id
120 awaitSomethingToWrite
122 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 hPutStr h (fmtHex False 0 $! fromIntegral $! B.length chunk)
139 awaitSomethingToWrite
141 finishBodyChunk :: Interaction -> IO ()
144 do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id
145 willChunkBody <- atomically $! readItr itr itrWillChunkBody id
146 when (not willDiscardBody && willChunkBody)
147 $ hPutStr h "0\r\n\r\n" >> hFlush h
149 finalize :: Interaction -> IO ()
152 do finishBodyChunk itr
153 willClose <- atomically $!
154 do queue <- readTVar tQueue
156 case S.viewr queue of
157 remaining :> _ -> writeTVar tQueue remaining
159 readItr itr itrWillClose id
161 -- reader は恐らく hWaitForInput してゐる最中なので、
162 -- スレッドを豫め殺して置かないとをかしくなる。
163 do killThread readerTID
166 awaitSomethingToWrite