7 module Network.HTTP.Lucu.ResponseWriter
11 import qualified Blaze.ByteString.Builder.HTTP as BB
12 import qualified Data.Ascii as A
13 import Control.Concurrent
14 import Control.Concurrent.STM
15 import Control.Exception
17 import Data.Monoid.Unicode
18 import qualified Data.Sequence as S
19 import Data.Sequence (ViewR(..))
20 import Network.HTTP.Lucu.Config
21 import Network.HTTP.Lucu.HandleLike
22 import Network.HTTP.Lucu.HttpVersion
23 import Network.HTTP.Lucu.Interaction
24 import Network.HTTP.Lucu.Postprocess
25 import Network.HTTP.Lucu.Response
26 import Prelude.Unicode
27 import System.IO (hPutStrLn, stderr)
33 , cQueue ∷ !InteractionQueue
37 responseWriter ∷ HandleLike h ⇒ Config → h → InteractionQueue → ThreadId → IO ()
38 responseWriter cnf h tQueue readerTID
39 = awaitSomethingToWrite (Context cnf h tQueue readerTID)
41 [ Handler $ \ (_ ∷ IOException) → return ()
42 , Handler $ \ e → case e of
43 ThreadKilled → return ()
44 _ → hPutStrLn stderr (show e)
45 , Handler $ \ BlockedIndefinitelyOnSTM → hPutStrLn stderr "requestWriter: blocked indefinitely"
46 , Handler $ \ (e ∷ SomeException) → hPutStrLn stderr (show e)
49 awaitSomethingToWrite ∷ HandleLike h ⇒ Context h → IO ()
50 awaitSomethingToWrite ctx@(Context {..})
54 do queue ← readTVar cQueue
55 -- GettingBody 状態にあり、Continue が期待されてゐて、それがま
56 -- だ送信前なのであれば、Continue を送信する。
59 _ :> itr → do state ← readTVar $ itrState itr
60 if state ≡ GettingBody then
61 writeContinueIfNeeded ctx itr
63 if state ≥ DecidingBody then
64 writeHeaderOrBodyIfNeeded ctx itr
68 writeContinueIfNeeded ∷ HandleLike h ⇒ Context h → Interaction → STM (IO ())
69 writeContinueIfNeeded ctx itr@(Interaction {..})
70 = do expectedContinue ← readTVar itrExpectedContinue
71 if expectedContinue then
72 do wroteContinue ← readTVar itrWroteContinue
77 do reqBodyWanted ← readTVar itrReqBodyWanted
78 if reqBodyWanted ≢ Nothing then
79 return $ writeContinue ctx itr
85 -- DecidingBody 以降の状態にあり、まだヘッダを出力する前であれば、ヘッ
86 -- ダを出力する。ヘッダ出力後であり、bodyToSend が空でなければ、それを
87 -- 出力する。空である時は、もし状態がDone であれば後処理をする。
88 writeHeaderOrBodyIfNeeded ∷ HandleLike h ⇒ Context h → Interaction → STM (IO ())
89 writeHeaderOrBodyIfNeeded ctx itr@(Interaction {..})
90 = do wroteHeader ← readTVar itrWroteHeader
91 if not wroteHeader then
92 return $ writeHeader ctx itr
94 do noBodyToWrite ← isEmptyTMVar itrBodyToSend
96 do state ← readTVar itrState
98 return $ finalize ctx itr
102 return $ writeBodyChunk ctx itr
104 writeContinue ∷ HandleLike h ⇒ Context h → Interaction → IO ()
105 writeContinue ctx@(Context {..}) (Interaction {..})
106 = do let cont = Response {
107 resVersion = HttpVersion 1 1
108 , resStatus = Continue
111 cont' ← completeUnconditionalHeaders cConfig cont
112 hPutBuilder cHandle $ A.toBuilder $ printResponse cont'
114 atomically $ writeTVar itrWroteContinue True
115 awaitSomethingToWrite ctx
117 writeHeader ∷ HandleLike h ⇒ Context h → Interaction → IO ()
118 writeHeader ctx@(Context {..}) (Interaction {..})
119 = do res ← atomically
120 $ do writeTVar itrWroteHeader True
122 hPutBuilder cHandle $ A.toBuilder $ printResponse res
124 awaitSomethingToWrite ctx
126 writeBodyChunk ∷ HandleLike h ⇒ Context h → Interaction → IO ()
127 writeBodyChunk ctx@(Context {..}) (Interaction {..})
130 do willDiscardBody ← readTVar itrWillDiscardBody
131 if willDiscardBody then
132 do _ ← tryTakeTMVar itrBodyToSend
133 return $ awaitSomethingToWrite ctx
135 do willChunkBody ← readTVar itrWillChunkBody
136 chunk ← takeTMVar itrBodyToSend
138 do if willChunkBody then
139 hPutBuilder cHandle $ BB.chunkedTransferEncoding chunk
141 hPutBuilder cHandle chunk
143 awaitSomethingToWrite ctx
145 finishBodyChunk ∷ HandleLike h ⇒ Context h → Interaction → IO ()
146 finishBodyChunk (Context {..}) (Interaction {..})
149 do willDiscardBody ← readTVar itrWillDiscardBody
150 willChunkBody ← readTVar itrWillChunkBody
151 if ((¬) willDiscardBody) ∧ willChunkBody then
153 do hPutBuilder cHandle BB.chunkedTransferTerminator
158 finalize ∷ HandleLike h ⇒ Context h → Interaction → IO ()
159 finalize ctx@(Context {..}) itr@(Interaction {..})
160 = do finishBodyChunk ctx itr
161 willClose ← atomically $
162 do queue ← readTVar cQueue
163 case S.viewr queue of
164 EmptyR → return () -- this should never happen
165 remaining :> _ → writeTVar cQueue remaining
166 readTVar itrWillClose
168 -- reader は恐らく hWaitForInput してゐる最中なので、スレッ
169 -- ドを豫め殺して置かないとをかしくなる。
170 do killThread cReader
173 awaitSomethingToWrite ctx