quic 0.3.7 → 0.3.8
raw patch · 9 files changed
+88/−3 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Network.QUIC: resetReceived :: Stream -> IO (Maybe ApplicationProtocolError)
+ Network.QUIC.Internal: resetReceived :: Stream -> IO (Maybe ApplicationProtocolError)
+ Network.QUIC.Internal: setResetReceived :: Stream -> ApplicationProtocolError -> IO ()
Files
- ChangeLog.md +12/−0
- Network/QUIC.hs +1/−0
- Network/QUIC/Receiver.hs +3/−0
- Network/QUIC/Stream.hs +2/−0
- Network/QUIC/Stream/Misc.hs +16/−0
- Network/QUIC/Stream/Types.hs +3/−0
- quic.cabal +1/−1
- test/Config.hs +10/−1
- test/IOSpec.hs +40/−1
ChangeLog.md view
@@ -1,5 +1,17 @@ # ChangeLog +## 0.3.8++* Tell a stream that was reset from one that ended. After a+ RESET_STREAM, `recvStream` returns an empty ByteString, just as it does+ at the end of a stream, and nothing else said which it was. An+ application protocol may have to know: HTTP/3's QPACK decoder has to+ send a Stream Cancellation for a request stream that was reset+ (RFC 9204 Sec 4.4.2), and a reset that lands between two frames looked+ to it exactly like the end of the request. `resetReceived` answers the+ error code of the peer's RESET_STREAM, or `Nothing` if there was none.+ [#123](https://github.com/kazu-yamamoto/quic/pull/123)+ ## 0.3.7 * Don't open a closed stream again for a late copy of its data. A STREAM
Network/QUIC.hs view
@@ -32,6 +32,7 @@ -- * IO recvStream,+ resetReceived, sendStream, sendStreamMany, sendDatagram,
Network/QUIC/Receiver.hs view
@@ -277,6 +277,9 @@ Nothing -> return () Just strm -> do onResetStreamReceived (connHooks conn) strm aerr+ -- Before the pseudo FIN below, so that whoever reads it can+ -- tell it was not a real one.+ setResetReceived strm aerr setTxStreamClosed strm setRxStreamClosed strm delStream conn strm
Network/QUIC/Stream.hs view
@@ -20,6 +20,8 @@ getRxStreamOffset, isRxStreamClosed, setRxStreamClosed,+ resetReceived,+ setResetReceived, readStreamFlowTx, addTxStreamData, setTxMaxStreamData,
Network/QUIC/Stream/Misc.hs view
@@ -9,6 +9,8 @@ getRxStreamOffset, isRxStreamClosed, setRxStreamClosed,+ resetReceived,+ setResetReceived, -- readStreamFlowTx, addTxStreamData,@@ -25,6 +27,7 @@ import Network.QUIC.Imports import Network.QUIC.Stream.Queue import Network.QUIC.Stream.Types+import Network.QUIC.Types (ApplicationProtocolError) ---------------------------------------------------------------- @@ -66,6 +69,19 @@ putRecvStreamQ strm "" where set (StreamState off _) = StreamState off True++-- | The error code of the RESET_STREAM the peer sent for this stream, if it+-- sent one.+--+-- After a RESET_STREAM, 'recvStream' returns an empty 'ByteString', just as+-- it does at the end of the stream. This tells the two apart: HTTP/3, for+-- one, has to tell its QPACK encoder about a request whose reset it saw+-- (RFC 9204, section 4.4.2), and had no way to know that it was reset.+resetReceived :: Stream -> IO (Maybe ApplicationProtocolError)+resetReceived Stream{..} = readIORef streamResetRx++setResetReceived :: Stream -> ApplicationProtocolError -> IO ()+setResetReceived Stream{..} aerr = writeIORef streamResetRx $ Just aerr ----------------------------------------------------------------
Network/QUIC/Stream/Types.hs view
@@ -41,6 +41,8 @@ , -- input stream fragments to streamQ, and how many of them streamReass :: IORef (Int, Skew RxStreamData) , streamSyncFinTx :: MVar ()+ , streamResetRx :: IORef (Maybe ApplicationProtocolError)+ -- ^ The error code of a RESET_STREAM from the peer } instance Show Stream where@@ -56,6 +58,7 @@ streamRecvQ <- newRecvStreamQ streamReass <- newIORef (0, Skew.empty) streamSyncFinTx <- newEmptyMVar+ streamResetRx <- newIORef Nothing return Stream{..} {- FOURMOLU_ENABLE -}
quic.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: quic-version: 0.3.7+version: 0.3.8 license: BSD3 license-file: LICENSE maintainer: kazu@iij.ad.jp
test/Config.hs view
@@ -292,7 +292,12 @@ shouldDelay (DelayServerPacket k) fromC pn = not fromC && pn == k shouldDelay _ _ _ = False -- The packets that follow go ahead of the one held back.- delayIf True send_ = void $ forkIO $ threadDelay 1000000 >> send_+ --+ -- Long enough for the data to be sent again and the stream closed before+ -- it lands, which is what the test is about, and no longer: it was a+ -- second, and a second is two and a half of these tests' worth of+ -- everything else.+ delayIf True send_ = void $ forkIO $ threadDelay delayTime >> send_ delayIf False send_ = send_ chooseALPN :: Version -> [ByteString] -> IO ByteString@@ -328,3 +333,7 @@ Just (s, d) | s == sid -> return $ Just d | otherwise -> return Nothing++-- | How long 'DelayClientPacket' and 'DelayServerPacket' hold a datagram.+delayTime :: Int+delayTime = 100000
test/IOSpec.hs view
@@ -100,6 +100,8 @@ -- number of bytes sent by the RESET_STREAM sender. it "sends RESET_STREAM with the bytes sent as final size" $ do withPipe (DropClientPacket []) $ testResetStreamFinalSize cc sc waitS+ it "tells a stream that was reset from one that ended" $ do+ withPipe (DropClientPacket []) $ testResetReceived cc sc waitS describe "closed stream" $ do it "ignores a late copy of the data it received on a stream it opened" $ do withPipe (DelayServerPacket 300) $ testLateCopy False cc sc waitS@@ -179,7 +181,7 @@ C.run cc $ \conn -> do exchange conn -- The copy arrives while we wait.- threadDelay 1500000+ threadDelay 200000 exchange conn exchange conn = do strm <- stream conn@@ -195,6 +197,43 @@ assertEndOfStream strm sendStream strm (BS.replicate downLen 0) closeStream strm++-- | After a RESET_STREAM, 'recvStream' returns "" just as at the end of the+-- stream; 'resetReceived' tells the two apart. The client ends one stream+-- and resets another, and the server looks at both.+testResetReceived :: C.ClientConfig -> ServerConfig -> IO () -> IO ()+testResetReceived cc sc waitS = do+ resultVar <- newEmptyMVar+ E.bracket (forkIO $ server resultVar) killThread $ \_ -> client resultVar+ where+ aerr = ApplicationProtocolError 7++ client resultVar = do+ waitS+ C.run cc $ \conn -> do+ strm0 <- stream conn+ sendStream strm0 "ended"+ shutdownStream strm0+ strm1 <- stream conn+ sendStream strm1 "reset"+ -- Let the data go first.+ threadDelay 100000+ resetStream strm1 aerr+ mres <- Timeout.timeout 5000000 $ takeMVar resultVar+ mres `shouldBe` Just (Nothing, Just aerr)++ server resultVar = run sc $ \conn -> do+ strm0 <- acceptStream conn+ strm1 <- acceptStream conn+ let drain strm = do+ bs <- recvStream strm 1024+ unless (BS.null bs) $ drain strm+ drain strm0+ drain strm1+ r0 <- resetReceived strm0+ r1 <- resetReceived strm1+ putMVar resultVar (r0, r1)+ threadDelay 1000000 testRecvStreamClientStopFirst :: C.ClientConfig -> ServerConfig -> IO () -> IO ()