on master help sign in
hashing for HTTP/2.
commit333a83d61699176b8a79a49039b42774e1b4cf7c authorKazu Yamamoto <kazu@iij.ad.jp> dateSat 26 Dec 2015, 21:39 (11 years ago) parent5763b0c
4 files changed+103−74
warp/Network/Wai/Handler/Warp/HTTP2/HPACK.hs+3−5 warp/Network/Wai/Handler/Warp/HTTP2/Sender.hs+48−40 warp/Network/Wai/Handler/Warp/HTTP2/Types.hs+26−12 warp/Network/Wai/Handler/Warp/HTTP2/Worker.hs+26−17
warp/Network/Wai/Handler/Warp/HTTP2/HPACK.hs+3−5
@@ -13,7 +13,6 @@ import Data.IORef (readIORef, writeIORef)
1313import Network.HPACK
1414import qualified Network.HTTP.Types as H
1515import Network.HTTP2
16−import Network.Wai
1716import Network.Wai.Handler.Warp.HTTP2.Types
1817import Network.Wai.Handler.Warp.Header
1918import Network.Wai.Handler.Warp.Response
@@ -25,15 +24,14 @@ import Network.Wai.Handler.Warp.Types
2524
2625-- Set-Cookie: contains only one cookie value.
2726-- So, we don't need to split it.
28−hpackEncodeHeader :: Context -> InternalInfo -> S.Settings -> Response
27+hpackEncodeHeader :: Context -> InternalInfo -> S.Settings
28+ -> H.Status -> H.ResponseHeaders
2929 -> IO Builder
30−hpackEncodeHeader ctx ii settings rsp = do
30+hpackEncodeHeader ctx ii settings s h = do
3131 hdr1 <- addServerAndDate h
3232 let hdr2 = (":status", status) : map (first foldedCase) hdr1
3333 hpackEncodeRawHeaders ctx hdr2
3434 where
35− s = responseStatus rsp
36− h = responseHeaders rsp
3735 status = B8.pack $ show $ H.statusCode s
3836 dc = dateCacher ii
3937 rspidxhdr = indexResponseHeader h
warp/Network/Wai/Handler/Warp/HTTP2/Sender.hs+48−40
@@ -11,6 +11,7 @@ import Control.Concurrent.STM
1111import qualified Control.Exception as E
1212import Control.Monad (void, when)
1313import qualified Data.ByteString as BS
14+import Data.ByteString.Builder (Builder)
1415import qualified Data.ByteString.Builder.Extra as B
1516import Data.Maybe (isNothing)
1617import Foreign.Ptr
@@ -26,7 +27,6 @@ import Network.Wai.Handler.Warp.HTTP2.Types
2627import Network.Wai.Handler.Warp.IORef
2728import qualified Network.Wai.Handler.Warp.Settings as S
2829import Network.Wai.Handler.Warp.Types
29−import Network.Wai.Internal (Response(..))
3030
3131#ifdef WINDOWS
3232import qualified System.IO as IO
@@ -110,54 +110,61 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
110110 setLimitForEncoding siz dyntbl
111111 return True
112112
113− output (ONext strm binfo curr) off0 lim = do
113+ output (ONext strm curr mtbq) off0 lim = do
114114 -- Data frame payload
115115 let !buf = connWriteBuffer `plusPtr` off0
116116 !siz = connBufferSize - off0
117117 Next datPayloadLen mnext <- curr buf siz lim
118118 off <- fillDataHeader strm off0 datPayloadLen mnext
119− maybeEnqueueNext strm binfo mnext
119+ maybeEnqueueNext strm mtbq mnext
120120 return off
121121
122− output (OResponse strm binfo rsp) off0 lim = do
122+ output (ORspn strm rspn) off0 lim = do
123123 -- Header frame and Continuation frame
124124 let sid = streamNumber strm
125− endOfStream = case binfo of
126− Persist{} -> False
127− OneshotWithBody -> False
128− OneshotWithoutBody -> True
129− kvlen <- headerContinue sid rsp endOfStream off0
125+ endOfStream = case rspn of
126+ RspnNobody _ _ -> True
127+ _ -> False
128+ kvlen <- headerContinue sid rspn endOfStream off0
130129 off <- sendHeadersIfNecessary $ off0 + frameHeaderLength + kvlen
131− case binfo of
132− OneshotWithoutBody -> do
130+ case rspn of
131+ RspnNobody _ _ -> do
133132 closed ctx strm Finished
134133 return off
135− OneshotWithBody -> do
134+ RspnFile _ _ h path mpart -> do
136135 -- Data frame payload
137136 let payloadOff = off + frameHeaderLength
138137 Next datPayloadLen mnext <-
139− fillResponseBodyGetNext conn ii payloadOff lim rsp
138+ fillFileBodyGetNext conn ii payloadOff lim h path mpart
140139 off' <- fillDataHeader strm off datPayloadLen mnext
141− maybeEnqueueNext strm binfo mnext
140+ maybeEnqueueNext strm Nothing mnext
142141 return off'
143− Persist sq -> do
142+ RspnBuilder _ _ builder -> do
143+ -- Data frame payload
144+ let payloadOff = off + frameHeaderLength
145+ Next datPayloadLen mnext <-
146+ fillBuilderBodyGetNext conn ii payloadOff lim builder
147+ off' <- fillDataHeader strm off datPayloadLen mnext
148+ maybeEnqueueNext strm Nothing mnext
149+ return off'
150+ RspnStreaming _ _ tbq -> do
144151 let payloadOff = off + frameHeaderLength
145152 Next datPayloadLen mnext <-
146− fillStreamBodyGetNext conn payloadOff lim sq strm
153+ fillStreamBodyGetNext conn payloadOff lim tbq strm
147154 off' <- fillDataHeader strm off datPayloadLen mnext
148− maybeEnqueueNext strm binfo mnext
155+ maybeEnqueueNext strm (Just tbq) mnext
149156 return off'
150157
151158 whenReadyOrEnqueueAgain out off body = E.handle resetStream $ do
152159 state <- readIORef $ streamState strm
153160 if isClosed state then
154161 return off
155− else case binfo of
156− Persist tbq -> checkStreaming tbq
157− _ -> checkStreamWindowSize
162+ else case mtbq of
163+ Just tbq -> checkStreaming tbq
164+ _ -> checkStreamWindowSize
158165 where
159166 strm = outputStream out
160− binfo = outputBodyInfo out
167+ mtbq = outputMaybeTBQueue out
161168 checkStreaming tbq = do
162169 isEmpty <- atomically $ isEmptyTBQueue tbq
163170 if isEmpty then do
@@ -185,8 +192,10 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
185192 flushN :: Int -> IO ()
186193 flushN n = bufferIO connWriteBuffer n connSendAll
187194
188− headerContinue sid rsp endOfStream off = do
189− builder <- hpackEncodeHeader ctx ii settings rsp
195+ headerContinue sid rspn endOfStream off = do
196+ let !s = rspnStatus rspn
197+ !h = rspnHeaders rspn
198+ builder <- hpackEncodeHeader ctx ii settings s h
190199 let !offkv = off + frameHeaderLength
191200 let !bufkv = connWriteBuffer `plusPtr` offkv
192201 !limkv = connBufferSize - offkv
@@ -225,11 +234,11 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
225234 continue sid kvlen' (B.Chunk bs2 writer)
226235
227236 -- Re-enqueue the stream in the output queue.
228− maybeEnqueueNext :: Stream -> BodyInfo -> Maybe DynaNext -> IO ()
229− maybeEnqueueNext _ _ Nothing = return ()
230− maybeEnqueueNext strm binfo (Just next) = enqueueOutput outputQ out
237+ maybeEnqueueNext :: Stream -> Maybe (TBQueue Sequence) -> Maybe DynaNext -> IO ()
238+ maybeEnqueueNext _ _ Nothing = return ()
239+ maybeEnqueueNext strm mtbq (Just next) = enqueueOutput outputQ out
231240 where
232− !out = ONext strm binfo next
241+ !out = ONext strm next mtbq
233242
234243 -- Send headers if there is not room for a 1-byte data frame, and return
235244 -- the offset of the next frame's first header byte.
@@ -270,32 +279,33 @@ ResponseStream Status ResponseHeaders StreamingBody
270279ResponseRaw (IO ByteString -> (ByteString -> IO ()) -> IO ()) Response
271280-}
272281
273−fillResponseBodyGetNext :: Connection -> InternalInfo -> Int -> WindowSize -> Response -> IO Next
274−fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
275− _ off lim (ResponseBuilder _ _ bb) = do
282+fillBuilderBodyGetNext :: Connection -> InternalInfo -> Int -> WindowSize -> Builder -> IO Next
283+fillBuilderBodyGetNext Connection{connWriteBuffer,connBufferSize}
284+ _ off lim bb = do
276285 let datBuf = connWriteBuffer `plusPtr` off
277286 room = min (connBufferSize - off) lim
278287 (len, signal) <- B.runBuilder bb datBuf room
279288 return $ nextForBuilder len signal
280289
290+fillFileBodyGetNext :: Connection -> InternalInfo -> Int -> WindowSize -> Int -> FilePath -> Maybe FilePart -> IO Next
281291#ifdef WINDOWS
282−fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
283− _ off lim (ResponseFile _ _ path mpart) = do
292+fillFileBodyGetNext Connection{connWriteBuffer,connBufferSize}
293+ _ off lim _ path mpart = do
284294 let datBuf = connWriteBuffer `plusPtr` off
285295 room = min (connBufferSize - off) lim
286296 (start, bytes) <- fileStartEnd path mpart
287297 -- fixme: how to close Handle? GC does it at this moment.
288− h <- IO.openBinaryFile path IO.ReadMode
298+ hdl <- IO.openBinaryFile path IO.ReadMode
289299 IO.hSeek h IO.AbsoluteSeek start
290− len <- IO.hGetBufSome h datBuf (mini room bytes)
300+ len <- IO.hGetBufSome hdl datBuf (mini room bytes)
291301 let bytes' = bytes - fromIntegral len
292302 -- fixme: connWriteBuffer connBufferSize
293− return $ nextForFile len h bytes' (return ())
303+ return $ nextForFile len hdl bytes' (return ())
294304#else
295−fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
296− ii off lim (ResponseFile _ _ path mpart) = do
305+fillFileBodyGetNext Connection{connWriteBuffer,connBufferSize}
306+ ii off lim h path mpart = do
297307 (fd, refresh) <- case fdCacher ii of
298− Just fdcache -> getFd fdcache path
308+ Just fdcache -> getFd' fdcache h path
299309 Nothing -> do
300310 fd' <- openFd path ReadOnly Nothing defaultFileFlags{nonBlock=True}
301311 th <- T.register (timeoutManager ii) (closeFd fd')
@@ -309,8 +319,6 @@ fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
309319 return $ nextForFile len fd (start + len') (bytes - len') refresh
310320#endif
311321
312−fillResponseBodyGetNext _ _ _ _ _ = error "fillResponseBodyGetNext"
313−
314322fileStartEnd :: FilePath -> Maybe FilePart -> IO (Integer, Integer)
315323fileStartEnd _ (Just part) =
316324 return (filePartOffset part, filePartByteCount part)
warp/Network/Wai/Handler/Warp/HTTP2/Types.hs+26−12
@@ -17,7 +17,7 @@ import qualified Data.ByteString as BS
1717import Data.IntMap.Strict (IntMap, IntMap)
1818import qualified Data.IntMap.Strict as M
1919import qualified Network.HTTP.Types as H
20−import Network.Wai (Request, Response)
20+import Network.Wai (Request, FilePart)
2121import Network.Wai.Handler.Warp.HTTP2.Manager
2222import Network.Wai.Handler.Warp.IORef
2323import Network.Wai.Handler.Warp.Types
@@ -51,16 +51,34 @@ type BytesFilled = Int
5151
5252data Next = Next !BytesFilled (Maybe DynaNext)
5353
54−data Output = OResponse !Stream !BodyInfo !Response
55− | ONext !Stream !BodyInfo !DynaNext
54+data Rspn = RspnNobody H.Status H.ResponseHeaders
55+ | RspnStreaming H.Status H.ResponseHeaders (TBQueue Sequence)
56+ | RspnBuilder H.Status H.ResponseHeaders Builder
57+ | RspnFile H.Status H.ResponseHeaders Int FilePath (Maybe FilePart)
58+
59+rspnStatus :: Rspn -> H.Status
60+rspnStatus (RspnNobody s _) = s
61+rspnStatus (RspnStreaming s _ _) = s
62+rspnStatus (RspnBuilder s _ _) = s
63+rspnStatus (RspnFile s _ _ _ _ ) = s
64+
65+rspnHeaders :: Rspn -> H.ResponseHeaders
66+rspnHeaders (RspnNobody _ h) = h
67+rspnHeaders (RspnStreaming _ h _) = h
68+rspnHeaders (RspnBuilder _ h _) = h
69+rspnHeaders (RspnFile _ h _ _ _ ) = h
70+
71+data Output = ORspn !Stream !Rspn
72+ | ONext !Stream !DynaNext !(Maybe (TBQueue Sequence))
5673
5774outputStream :: Output -> Stream
58−outputStream (OResponse strm _ _) = strm
59−outputStream (ONext strm _ _) = strm
75+outputStream (ORspn strm _) = strm
76+outputStream (ONext strm _ _) = strm
6077
61−outputBodyInfo :: Output -> BodyInfo
62−outputBodyInfo (OResponse _ binfo _) = binfo
63−outputBodyInfo (ONext _ binfo _) = binfo
78+outputMaybeTBQueue :: Output -> Maybe (TBQueue Sequence)
79+outputMaybeTBQueue (ORspn _ (RspnStreaming _ _ tbq)) = Just tbq
80+outputMaybeTBQueue (ORspn _ _) = Nothing
81+outputMaybeTBQueue (ONext _ _ mtbq) = mtbq
6482
6583data Control = CFinish
6684 | CGoaway !ByteString
@@ -73,10 +91,6 @@ data Sequence = SFinish
7391 | SFlush
7492 | SBuilder Builder
7593
76−data BodyInfo = OneshotWithBody
77− | OneshotWithoutBody
78− | Persist (TBQueue Sequence)
79−
8094----------------------------------------------------------------
8195
8296-- | The context for HTTP/2 connection.
warp/Network/Wai/Handler/Warp/HTTP2/Worker.hs+26−17
@@ -20,6 +20,7 @@ import Control.Exception (SomeException(..), AsyncException(..))
2020import qualified Control.Exception as E
2121import Control.Monad (when)
2222import Data.ByteString.Builder (byteString)
23+import Data.Hashable (hash)
2324import qualified Network.HTTP.Types as H
2425import Network.HTTP2
2526import Network.Wai
@@ -71,37 +72,44 @@ response ii settings Context{outputQ} mgr tconf th strm req rsp
7172 -- log message are written here even the window size of streams
7273 -- is 0.
7374
74− responseNoBody s hs = responseBuilderCore s hs mempty OneshotWithoutBody
75−
76− responseBuilderBody s hs bdy = responseBuilderCore s hs bdy OneshotWithBody
75+ responseNoBody s hs = do
76+ logger req s Nothing
77+ setThreadContinue tconf True
78+ let rspn = RspnNobody s hs
79+ out = ORspn strm rspn
80+ enqueueOutput outputQ out
81+ return ResponseReceived
7782
78− responseBuilderCore s hs bdy binfo = do
83+ responseBuilderBody s hs bdy = do
7984 logger req s Nothing
8085 setThreadContinue tconf True
81− let rsp' = ResponseBuilder s hs bdy
82− out = OResponse strm binfo rsp'
86+ let rspn = RspnBuilder s hs bdy
87+ out = ORspn strm rspn
8388 enqueueOutput outputQ out
8489 return ResponseReceived
8590
8691 responseFileXXX path Nothing = do
87− efinfo <- E.try $ fileInfo ii path
92+ let !h = hash $ rawPathInfo req
93+ efinfo <- E.try $ fileInfo' ii h path
8894 case efinfo of
8995 Left (_ex :: E.IOException) -> response404
9096 Right finfo -> case conditionalRequest finfo hs0 (indexRequestHeader (requestHeaders req)) of
9197 WithoutBody s -> responseNoBody s hs0
92− WithBody s hs beg len -> responseFile2XX s hs path (Just (FilePart beg len (fileInfoSize finfo)))
98+ WithBody s hs beg len -> responseFile2XX s hs h path (Just (FilePart beg len (fileInfoSize finfo)))
9399
94− responseFileXXX path mpart = responseFile2XX s0 hs0 path mpart
100+ responseFileXXX path mpart = responseFile2XX s0 hs0 h path mpart
101+ where
102+ !h = hash $ rawPathInfo req
95103
96− responseFile2XX s hs path mpart
104+ responseFile2XX s hs h path mpart
97105 | isHead = do
98106 logger req s Nothing
99107 responseNoBody s hs
100108 | otherwise = do
101109 logger req s (filePartByteCount <$> mpart)
102110 setThreadContinue tconf True
103− let rsp' = ResponseFile s hs path mpart
104− out = OResponse strm OneshotWithBody rsp'
111+ let rspn = RspnFile s hs h path mpart
112+ out = ORspn strm rspn
105113 enqueueOutput outputQ out
106114 return ResponseReceived
107115
@@ -124,15 +132,16 @@ response ii settings Context{outputQ} mgr tconf th strm req rsp
124132 setThreadContinue tconf False
125133 -- Since 'StreamingBody' is loop, we cannot control it.
126134 -- So, let's serialize 'Builder' with a designated queue.
127− sq <- newTBQueueIO 10 -- fixme: hard coding: 10
128− let out = OResponse strm (Persist sq) rsp
135+ tbq <- newTBQueueIO 10 -- fixme: hard coding: 10
136+ let rspn = RspnStreaming s0 hs0 tbq
137+ out = ORspn strm rspn
129138 enqueueOutput outputQ out
130139 let push b = do
131− atomically $ writeTBQueue sq (SBuilder b)
140+ atomically $ writeTBQueue tbq (SBuilder b)
132141 T.tickle th
133− flush = atomically $ writeTBQueue sq SFlush
142+ flush = atomically $ writeTBQueue tbq SFlush
134143 _ <- strmbdy push flush
135− atomically $ writeTBQueue sq SFinish
144+ atomically $ writeTBQueue tbq SFinish
136145 deleteMyId mgr
137146 return ResponseReceived
138147