]> gitweb @ CieloNegro.org - Lucu.git/blob - Network/HTTP/Lucu/ResponseWriter.hs
63174b7cb5ea3d8f727c860d77efdea0710134dd
[Lucu.git] / Network / HTTP / Lucu / ResponseWriter.hs
1 module Network.HTTP.Lucu.ResponseWriter
2     ( responseWriter
3     )
4     where
5
6 import qualified Data.ByteString.Lazy.Char8 as C8
7 import           Control.Concurrent
8 import           Control.Concurrent.STM
9 import           Control.Exception
10 import           Control.Monad
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.HandleLike
16 import           Network.HTTP.Lucu.Headers
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)
22 import           System.IO (stderr)
23
24
25 responseWriter :: HandleLike h => Config -> h -> InteractionQueue -> ThreadId -> IO ()
26 responseWriter !cnf !h !tQueue !readerTID
27     = awaitSomethingToWrite
28       `catches`
29       [ Handler (( \ _                   -> return () ) :: IOException -> IO ())
30       , Handler  ( \ ThreadKilled        -> return () )
31       , Handler  ( \ BlockedIndefinitely -> hPutStrLn stderr "requestWriter: blocked indefinitely" )
32       , Handler (( \ e                   -> hPutStrLn stderr (show e) ) :: SomeException -> IO ())
33       ]
34     where
35       awaitSomethingToWrite :: IO ()
36       awaitSomethingToWrite 
37           = {-# SCC "awaitSomethingToWrite" #-}
38             do action
39                    <- atomically $!
40                       -- キューが空でなくなるまで待つ
41                       do queue <- readTVar tQueue
42                          -- GettingBody 状態にあり、Continue が期待され
43                          -- てゐて、それがまだ送信前なのであれば、
44                          -- Continue を送信する。
45                          case S.viewr queue of
46                            EmptyR   -> retry
47                            _ :> itr -> do state <- readItr itr itrState id
48
49                                           if state == GettingBody then
50                                               writeContinueIfNecessary itr
51                                             else
52                                               if state >= DecidingBody then
53                                                   writeHeaderOrBodyIfNecessary itr
54                                               else
55                                                   retry
56                action
57
58       writeContinueIfNecessary :: Interaction -> STM (IO ())
59       writeContinueIfNecessary itr
60           = {-# SCC "writeContinueIfNecessary" #-}
61             itr `seq`
62             do expectedContinue <- readItr itr itrExpectedContinue id
63                if expectedContinue then
64                    do wroteContinue <- readItr itr itrWroteContinue id
65                       if wroteContinue then
66                           -- 既に Continue を書込み濟
67                           retry
68                         else
69                           do reqBodyWanted <- readItr itr itrReqBodyWanted id
70                              if reqBodyWanted /= Nothing then
71                                  return $ writeContinue itr
72                                else
73                                  retry
74                  else
75                    retry
76
77       writeHeaderOrBodyIfNecessary :: Interaction -> STM (IO ())
78       writeHeaderOrBodyIfNecessary itr
79           -- DecidingBody 以降の状態にあり、まだヘッダを出力する前であ
80           -- れば、ヘッダを出力する。ヘッダ出力後であり、bodyToSend が
81           -- 空でなければ、それを出力する。空である時は、もし状態が
82           -- Done であれば後処理をする。
83           = {-# SCC "writeHeaderOrBodyIfNecessary" #-}
84             itr `seq`
85             do wroteHeader <- readItr itr itrWroteHeader id
86                
87                if not wroteHeader then
88                    return $! writeHeader itr
89                  else
90                    do bodyToSend <- readItr itr itrBodyToSend id
91
92                       if C8.null bodyToSend then
93                           do state <- readItr itr itrState id
94
95                              if state == Done then
96                                  return $! finalize itr
97                                else
98                                  retry
99                         else
100                           return $! writeBodyChunk itr
101
102       writeContinue :: Interaction -> IO ()
103       writeContinue itr
104           = {-# SCC "writeContinue" #-}
105             itr `seq`
106             do let cont = Response {
107                             resVersion = HttpVersion 1 1
108                           , resStatus  = Continue
109                           , resHeaders = emptyHeaders
110                           }
111                cont' <- completeUnconditionalHeaders cnf cont
112                hPutResponse h cont'
113                hFlush h
114                atomically $! writeItr itr itrWroteContinue True
115                awaitSomethingToWrite
116
117       writeHeader :: Interaction -> IO ()
118       writeHeader itr
119           = {-# SCC "writeHeader" #-}
120             itr `seq`
121             do res <- atomically $! do writeItr itr itrWroteHeader True
122                                        readItr itr itrResponse id
123                hPutResponse h res
124                hFlush h
125                awaitSomethingToWrite
126       
127       writeBodyChunk :: Interaction -> IO ()
128       writeBodyChunk itr
129           = {-# SCC "writeBodyChunk" #-}
130             itr `seq`
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 C8.empty
135                                                    return chunk
136                unless willDiscardBody
137                           $ do if willChunkBody then
138                                    do hPutStr h (fmtHex False 0 $! fromIntegral $! C8.length chunk)
139                                       hPutLBS h (C8.pack "\r\n")
140                                       hPutLBS h chunk
141                                       hPutLBS h (C8.pack "\r\n")
142                                  else
143                                    hPutLBS h chunk
144                                hFlush h
145                awaitSomethingToWrite
146
147       finishBodyChunk :: Interaction -> IO ()
148       finishBodyChunk itr
149           = {-# SCC "finishBodyChunk" #-}
150             itr `seq`
151             do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id
152                willChunkBody   <- atomically $! readItr itr itrWillChunkBody   id
153                when (not willDiscardBody && willChunkBody)
154                         $ hPutLBS h (C8.pack "0\r\n\r\n") >> hFlush h
155
156       finalize :: Interaction -> IO ()
157       finalize itr
158           = {-# SCC "finalize" #-}
159             itr `seq`
160             do finishBodyChunk itr
161                willClose <- atomically $!
162                             do queue <- readTVar tQueue
163
164                                case S.viewr queue of
165                                  EmptyR         -> return () -- this should never happen
166                                  remaining :> _ -> writeTVar tQueue remaining
167
168                                readItr itr itrWillClose id
169                if willClose then
170                    -- reader は恐らく hWaitForInput してゐる最中なので、
171                    -- スレッドを豫め殺して置かないとをかしくなる。
172                    do killThread readerTID
173                       hClose h
174                  else
175                    awaitSomethingToWrite