diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,8 @@
+## 0.1.8
+
+- Announcing MaxStreams properly.
+- Terminating a connection if the peer violates flow controls.
+
 ## 0.1.7
 
 - Using System.Timeout.timeout.
diff --git a/Network/QUIC/Connection/State.hs b/Network/QUIC/Connection/State.hs
--- a/Network/QUIC/Connection/State.hs
+++ b/Network/QUIC/Connection/State.hs
@@ -19,6 +19,7 @@
   , addRxMaxData
   , getRxMaxData
   , getRxDataWindow
+  , checkRxMaxData
   , addTxBytes
   , getTxBytes
   , addRxBytes
@@ -128,6 +129,18 @@
 
 getRxDataWindow :: Connection -> IO Int
 getRxDataWindow Connection{..} = flowWindow <$> readIORef flowRx
+
+----------------------------------------------------------------
+
+checkRxMaxData :: Connection -> Int -> IO Bool
+checkRxMaxData Connection{..} len = do
+    received <- readIORef flowBytesRx
+    maxData <- flowMaxData <$> readIORef flowRx
+    if received + len < maxData then do
+        modifyIORef' flowBytesRx (+ len)
+        return True
+      else
+        return False
 
 ----------------------------------------------------------------
 
diff --git a/Network/QUIC/Connection/Stream.hs b/Network/QUIC/Connection/Stream.hs
--- a/Network/QUIC/Connection/Stream.hs
+++ b/Network/QUIC/Connection/Stream.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE BinaryLiterals #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards #-}
 
@@ -5,16 +6,20 @@
     getMyStreamId
   , waitMyNewStreamId
   , waitMyNewUniStreamId
-  , setMyMaxStreams
-  , setMyUniMaxStreams
-  , getPeerMaxStreams
-  , setPeerMaxStreams
+  , setTxMaxStreams
+  , setTxUniMaxStreams
+  , checkRxMaxStreams
+  , updatePeerStreamId
+  , checkStreamIdRoom
   ) where
 
 import UnliftIO.STM
 
+import Network.QUIC.Connection.Misc
 import Network.QUIC.Connection.Types
+import Network.QUIC.Connector
 import Network.QUIC.Imports
+import Network.QUIC.Parameters
 import Network.QUIC.Types
 
 getMyStreamId :: Connection -> IO Int
@@ -31,27 +36,70 @@
 get :: TVar Concurrency -> IO Int
 get tvar = atomically $ do
     conc@Concurrency{..} <- readTVar tvar
-    checkSTM (currentStream < maxStreams * 4 + streamType)
+    let streamType = currentStream .&. 0b11
+        StreamIdBase base = maxStreams
+    checkSTM (currentStream < base * 4 + streamType)
     let currentStream' = currentStream + 4
     writeTVar tvar conc { currentStream = currentStream' }
     return currentStream
 
-setMyMaxStreams :: Connection -> Int -> IO ()
-setMyMaxStreams Connection{..} = set myStreamId
+-- From "Peer", but set it to "My".
+-- So, using "Tx".
+setTxMaxStreams :: Connection -> Int -> IO ()
+setTxMaxStreams Connection{..} = set myStreamId
 
-setMyUniMaxStreams :: Connection -> Int -> IO ()
-setMyUniMaxStreams Connection{..} = set myUniStreamId
+setTxUniMaxStreams :: Connection -> Int -> IO ()
+setTxUniMaxStreams Connection{..} = set myUniStreamId
 
 set :: TVar Concurrency -> Int -> IO ()
-set tvar mx = atomically $ modifyTVar tvar $ \c -> c { maxStreams = mx }
+set tvar mx = atomically $ modifyTVar tvar $ \c -> c { maxStreams = StreamIdBase mx }
 
-setPeerMaxStreams :: Connection -> Int -> IO ()
-setPeerMaxStreams Connection{..} n =
-    atomicModifyIORef'' peerStreamId $ \c -> c { maxStreams = n }
+updatePeerStreamId :: Connection -> StreamId -> IO ()
+updatePeerStreamId conn sid = do
+    when ((isClient conn && isServerInitiatedBidirectional sid)
+       || (isServer conn && isClientInitiatedBidirectional sid)) $ do
+        atomicModifyIORef'' (peerStreamId conn) check
+    when ((isClient conn && isServerInitiatedUnidirectional sid)
+       || (isServer conn && isClientInitiatedUnidirectional sid)) $ do
+        atomicModifyIORef'' (peerUniStreamId conn) check
+  where
+    check conc@Concurrency{..}
+      | currentStream < sid = conc { currentStream = sid }
+      | otherwise           = conc
 
-getPeerMaxStreams :: Connection -> IO Int
-getPeerMaxStreams Connection{..} = atomicModifyIORef' peerStreamId inc
+checkRxMaxStreams :: Connection -> StreamId -> IO Bool
+checkRxMaxStreams conn@Connection{..} sid = do
+    Concurrency{..} <- if isClient conn then readForClient else readForServer
+    let StreamIdBase base = maxStreams
+        ok = sid < base * 4 + streamType
+    return ok
   where
-    inc c = (c { maxStreams = next}, next)
-      where
-        next = maxStreams c + 1
+    streamType = sid .&. 0b11
+    readForClient = case streamType of
+      0 -> readTVarIO myStreamId
+      1 -> readIORef  peerStreamId
+      2 -> readTVarIO myUniStreamId
+      3 -> readIORef  peerUniStreamId
+      _ -> error "never reach"
+    readForServer = case streamType of
+      0 -> readIORef  peerStreamId
+      1 -> readTVarIO myStreamId
+      2 -> readIORef  peerUniStreamId
+      3 -> readTVarIO myUniStreamId
+      _ -> error "never reach"
+
+checkStreamIdRoom :: Connection -> Direction -> IO (Maybe Int)
+checkStreamIdRoom conn dir = do
+    let ref | dir == Bidirectional = peerStreamId conn
+            | otherwise            = peerUniStreamId conn
+    atomicModifyIORef' ref check
+  where
+    check conc@Concurrency{..} =
+        let StreamIdBase base = maxStreams
+            initialStreams = initialMaxStreamsBidi $ getMyParameters conn
+            cbase = currentStream !>>. 2
+        in if (base - cbase < (initialStreams !>>. 3)) then
+               let base' = base + initialStreams
+               in (conc { maxStreams = StreamIdBase base' }, Just base')
+           else
+               (conc, Nothing)
diff --git a/Network/QUIC/Connection/StreamTable.hs b/Network/QUIC/Connection/StreamTable.hs
--- a/Network/QUIC/Connection/StreamTable.hs
+++ b/Network/QUIC/Connection/StreamTable.hs
@@ -32,12 +32,16 @@
 addStream :: Connection -> StreamId -> IO Stream
 addStream conn@Connection{..} sid = do
     strm <- newStream conn sid
-    peerParams <- getPeerParameters conn
-    let txMaxStreamData | isClient conn = clientInitial sid peerParams
-                        | otherwise     = serverInitial sid peerParams
-    setTxMaxStreamData strm txMaxStreamData
-    let rxMaxStreamData = initialRxMaxStreamData conn sid
-    setRxMaxStreamData strm rxMaxStreamData
+    if isClient conn then do
+         let clientParams = getMyParameters conn
+         setRxMaxStreamData strm $ clientInitial sid clientParams
+         serverParams <- getPeerParameters conn
+         setTxMaxStreamData strm $ serverInitial sid serverParams
+      else do
+         let serverParams = getMyParameters conn
+         setRxMaxStreamData strm $ serverInitial sid serverParams
+         clientParams <- getPeerParameters conn
+         setTxMaxStreamData strm $ clientInitial sid clientParams
     atomicModifyIORef'' streamTable $ insertStream sid strm
     return strm
 
@@ -54,15 +58,16 @@
 
 clientInitial :: StreamId -> Parameters -> Int
 clientInitial sid params
-  | isClientInitiatedBidirectional  sid = initialMaxStreamDataBidiRemote params
-  | isServerInitiatedBidirectional  sid = initialMaxStreamDataBidiLocal  params
-  -- intentionally not using isClientInitiatedUnidirectional
+  | isClientInitiatedBidirectional  sid = initialMaxStreamDataBidiLocal  params
+  | isServerInitiatedBidirectional  sid = initialMaxStreamDataBidiRemote params
+  -- intentionally not using isServerInitiatedUnidirectional
   | otherwise                           = initialMaxStreamDataUni        params
 
 serverInitial :: StreamId -> Parameters -> Int
 serverInitial sid params
-  | isServerInitiatedBidirectional  sid = initialMaxStreamDataBidiRemote params
-  | isClientInitiatedBidirectional  sid = initialMaxStreamDataBidiLocal  params
+  | isServerInitiatedBidirectional  sid = initialMaxStreamDataBidiLocal  params
+  | isClientInitiatedBidirectional  sid = initialMaxStreamDataBidiRemote params
+  -- intentionally not using isClientInitiatedUnidirectional
   | otherwise                           = initialMaxStreamDataUni        params
 
 ----------------------------------------------------------------
diff --git a/Network/QUIC/Connection/Types.hs b/Network/QUIC/Connection/Types.hs
--- a/Network/QUIC/Connection/Types.hs
+++ b/Network/QUIC/Connection/Types.hs
@@ -151,17 +151,19 @@
 
 ----------------------------------------------------------------
 
+newtype StreamIdBase = StreamIdBase { fromStreamIdBase :: Int }
+    deriving (Eq, Show)
+
 data Concurrency = Concurrency {
-    currentStream :: Int
-  , streamType    :: Int
-  , maxStreams    :: Int
+    currentStream :: StreamId
+  , maxStreams    :: StreamIdBase
   }
 
 newConcurrency :: Role -> Direction -> Int -> Concurrency
-newConcurrency rl dir n = Concurrency typ typ n
+newConcurrency rl dir n = Concurrency ini $ StreamIdBase n
  where
    bidi = dir == Bidirectional
-   typ | rl == Client = if bidi then 0 else 2
+   ini | rl == Client = if bidi then 0 else 2
        | otherwise    = if bidi then 1 else 3
 
 ----------------------------------------------------------------
@@ -206,15 +208,17 @@
   -- State
   , peerPacketNumber  :: IORef PacketNumber      -- for RTT1
   , streamTable       :: IORef StreamTable
-  , myStreamId        :: TVar Concurrency
-  , myUniStreamId     :: TVar Concurrency
-  , peerStreamId      :: IORef Concurrency
+  , myStreamId        :: TVar Concurrency  -- C:0 S:1
+  , myUniStreamId     :: TVar Concurrency  -- C:2 S:3
+  , peerStreamId      :: IORef Concurrency -- C:1 S:0
+  , peerUniStreamId   :: IORef Concurrency -- C:3 S:2
   , flowTx            :: TVar Flow
   , flowRx            :: IORef Flow
+  , flowBytesRx       :: IORef Int
   , migrationState    :: TVar MigrationState
   , minIdleTimeout    :: IORef Microseconds
-  , bytesTx           :: TVar Int
-  , bytesRx           :: TVar Int
+  , bytesTx           :: TVar Int -- TVar for anti amplification
+  , bytesRx           :: TVar Int -- TVar for anti amplification
   , addressValidated  :: TVar Bool
   -- TLS
   , pendingQ          :: Array   EncryptionLevel (TVar [ReceivedPacket])
@@ -302,8 +306,10 @@
         <*> newTVarIO (newConcurrency rl Bidirectional  0)
         <*> newTVarIO (newConcurrency rl Unidirectional 0)
         <*> newIORef  peerConcurrency
+        <*> newIORef  peerUniConcurrency
         <*> newTVarIO defaultFlow
         <*> newIORef defaultFlow { flowMaxData = initialMaxData myparams }
+        <*> newIORef 0
         <*> newTVarIO NonMigration
         <*> newIORef (milliToMicro $ maxIdleTimeout myparams)
         <*> newTVarIO 0
@@ -336,6 +342,7 @@
     peer | isclient  = Server
          | otherwise = Client
     peerConcurrency = newConcurrency peer Bidirectional (initialMaxStreamsBidi myparams)
+    peerUniConcurrency = newConcurrency peer Unidirectional (initialMaxStreamsUni myparams)
 
 defaultTrafficSecrets :: (ClientTrafficSecret a, ServerTrafficSecret a)
 defaultTrafficSecrets = (ClientTrafficSecret "", ServerTrafficSecret "")
diff --git a/Network/QUIC/Crypto/Nite.hs b/Network/QUIC/Crypto/Nite.hs
--- a/Network/QUIC/Crypto/Nite.hs
+++ b/Network/QUIC/Crypto/Nite.hs
@@ -22,6 +22,7 @@
 import qualified Data.ByteString.Internal as BS
 import Foreign.ForeignPtr (withForeignPtr, newForeignPtr_)
 import Foreign.Marshal.Alloc (mallocBytes)
+import Foreign.Marshal.Utils (copyBytes)
 import Foreign.Ptr (Ptr, plusPtr, nullPtr)
 import Foreign.Storable (peek, poke)
 import Network.TLS hiding (Version)
@@ -118,7 +119,7 @@
         let src0 = p0 `plusPtr` off0
         let src1 = p1 `plusPtr` off1
         let diff = len0 - len1
-        BS.memcpy dst src0 diff
+        copyBytes dst src0 diff
         loop (dst `plusPtr` diff) (src0 `plusPtr` diff) src1 len1
   where
     loop :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Int -> IO ()
diff --git a/Network/QUIC/Handshake.hs b/Network/QUIC/Handshake.hs
--- a/Network/QUIC/Handshake.hs
+++ b/Network/QUIC/Handshake.hs
@@ -265,8 +265,8 @@
         setTxMaxData conn $ initialMaxData params
         setMinIdleTimeout conn $ milliToMicro $ maxIdleTimeout params
         setMaxAckDaley (connLDCC conn) $ milliToMicro $ maxAckDelay params
-        setMyMaxStreams conn $ initialMaxStreamsBidi params
-        setMyUniMaxStreams conn $ initialMaxStreamsUni params
+        setTxMaxStreams conn $ initialMaxStreamsBidi params
+        setTxUniMaxStreams conn $ initialMaxStreamsUni params
 
     serverVersionNegotiation Nothing = return ()
     serverVersionNegotiation (Just peerVerInfo) = do
diff --git a/Network/QUIC/IO.hs b/Network/QUIC/IO.hs
--- a/Network/QUIC/IO.hs
+++ b/Network/QUIC/IO.hs
@@ -16,12 +16,14 @@
 -- | Creating a bidirectional stream.
 stream :: Connection -> IO Stream
 stream conn = do
+    -- FLOW CONTROL: MAX_STREAMS: send: respecting peer's limit
     sid <- waitMyNewStreamId conn
     addStream conn sid
 
 -- | Creating a unidirectional stream.
 unidirectionalStream :: Connection -> IO Stream
 unidirectionalStream conn = do
+    -- FLOW CONTROL: MAX_STREAMS: send: respecting peer's limit
     sid <- waitMyNewUniStreamId conn
     addStream conn sid
 
@@ -60,6 +62,8 @@
     conn = streamConnection s
     flowControl dats len wait = do
         -- 1-RTT
+        -- FLOW CONTROL: MAX_STREAM_DATA: send: respecting peer's limit
+        -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit
         eblocked <- checkBlocked s len wait
         case eblocked of
           Right n
@@ -145,8 +149,21 @@
     delStream conn s
     when ((isClient conn && isServerInitiatedBidirectional sid)
        || (isServer conn && isClientInitiatedBidirectional sid)) $ do
-        n <- getPeerMaxStreams conn
-        putOutput conn $ OutControl RTT1Level [MaxStreams Unidirectional n] $ return ()
+        -- FLOW CONTROL: MAX_STREAMS: recv: announcing my limit properly
+        checkMaxStreams conn Bidirectional
+    when ((isClient conn && isServerInitiatedUnidirectional sid)
+       || (isServer conn && isClientInitiatedUnidirectional sid)) $ do
+        -- FLOW CONTROL: MAX_STREAMS: recv: announcing my limit properly
+        checkMaxStreams conn Unidirectional
+  where
+    checkMaxStreams conn dir = do
+        mx <- checkStreamIdRoom conn dir
+        case mx of
+          Nothing -> return ()
+          Just nms -> do
+            sendFrames conn RTT1Level [MaxStreams dir nms]
+            fire conn (Microseconds 50000) $
+                sendFrames conn RTT1Level [MaxStreams dir nms]
 
 -- | Accepting a stream initiated by the peer.
 acceptStream :: Connection -> IO Stream
@@ -166,20 +183,20 @@
     window <- getRxStreamWindow s
     let sid = streamId s
         initialWindow = initialRxMaxStreamData conn sid
+    -- FLOW CONTROL: MAX_STREAM_DATA: recv: announcing my limit properly
     when (window <= (initialWindow !>>. 1)) $ do
         newMax <- addRxMaxStreamData s initialWindow
         sendFrames conn RTT1Level [MaxStreamData sid newMax]
-        fire conn (Microseconds 50000) $ do
-            newMax' <- getRxMaxStreamData s
-            sendFrames conn RTT1Level [MaxStreamData sid newMax']
+        fire conn (Microseconds 50000) $
+            sendFrames conn RTT1Level [MaxStreamData sid newMax]
+    -- FLOW CONTROL: MAX_DATA: recv: announcing my limit properly
     cwindow <- getRxDataWindow conn
     let cinitialWindow = initialMaxData $ getMyParameters conn
     when (cwindow <= (cinitialWindow !>>. 1)) $ do
         newMax <- addRxMaxData conn cinitialWindow
         sendFrames conn RTT1Level [MaxData newMax]
-        fire conn (Microseconds 50000) $ do
-            newMax' <- getRxMaxData conn
-            sendFrames conn RTT1Level [MaxData newMax']
+        fire conn (Microseconds 50000) $
+            sendFrames conn RTT1Level [MaxData newMax]
     return bs
 
 -- | Closing a stream with an error code.
diff --git a/Network/QUIC/Imports.hs b/Network/QUIC/Imports.hs
--- a/Network/QUIC/Imports.hs
+++ b/Network/QUIC/Imports.hs
@@ -33,7 +33,7 @@
 import Data.Array.IO
 import Data.Bits
 import Data.ByteString.Builder (Builder)
-import Data.ByteString.Internal (ByteString(..), memcpy)
+import Data.ByteString.Internal (ByteString(..))
 import Data.ByteString.Short.Internal (ShortByteString(..))
 import Data.Foldable
 import Data.IORef
@@ -43,6 +43,7 @@
 import Data.Ord
 import Data.Word
 import Foreign.ForeignPtr
+import Foreign.Marshal.Utils (copyBytes)
 import Foreign.Ptr
 import Network.ByteOrder
 import Network.QUIC.Utils
@@ -68,5 +69,5 @@
 copyBS :: Buffer -> ByteString -> IO Int
 copyBS dst (PS fptr off len) = withForeignPtr fptr $ \src0 -> do
     let src = src0 `plusPtr` off
-    memcpy dst src len
+    copyBytes dst src len
     return len
diff --git a/Network/QUIC/Receiver.hs b/Network/QUIC/Receiver.hs
--- a/Network/QUIC/Receiver.hs
+++ b/Network/QUIC/Receiver.hs
@@ -248,6 +248,10 @@
         closeConnection ProtocolViolation "NEW_TOKEN for server or in 1-RTT"
     when (isClient conn) $ setNewToken conn token
 processFrame conn RTT0Level (StreamF sid off (dat:_) fin) = do
+    when (off == 0) $ updatePeerStreamId conn sid
+    -- FLOW CONTROL: MAX_STREAMS: recv: rejecting if over my limit
+    ok <- checkRxMaxStreams conn sid
+    unless ok $ closeConnection StreamLimitError "stream id is too large"
     when (isSendOnly conn sid) $
         closeConnection StreamStateError "send-only stream"
     mstrm <- findStream conn sid
@@ -255,14 +259,28 @@
     strm <- maybe (createStream conn sid) return mstrm
     let len = BS.length dat
         rx = RxStreamData dat off len fin
-    ok <- putRxStreamData strm rx
-    unless ok $ closeConnection FlowControlError "Flow control error in 0-RTT"
+    fc <- putRxStreamData strm rx
+    case fc of
+      -- FLOW CONTROL: MAX_STREAM_DATA: recv: rejecting if over my limit
+      OverLimit -> closeConnection FlowControlError "Flow control error for stream in 0-RTT"
+      Duplicated -> return ()
+      Reassembled -> do
+          ok' <- checkRxMaxData conn len
+          -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit
+          unless ok' $ closeConnection FlowControlError "Flow control error for connection in 0-RTT"
 processFrame conn RTT1Level (StreamF sid _ [""] False) = do
+    -- FLOW CONTROL: MAX_STREAMS: recv: rejecting if over my limit
+    ok <- checkRxMaxStreams conn sid
+    unless ok $ closeConnection StreamLimitError "stream id is too large"
     when (isSendOnly conn sid) $
         closeConnection StreamStateError "send-only stream"
     mstrm <- findStream conn sid
     guardStream conn sid mstrm
 processFrame conn RTT1Level (StreamF sid off (dat:_) fin) = do
+    when (off == 0) $ updatePeerStreamId conn sid
+    -- FLOW CONTROL: MAX_STREAMS: recv: rejecting if over my limit
+    ok <- checkRxMaxStreams conn sid
+    unless ok $ closeConnection StreamLimitError "stream id is too large"
     when (isSendOnly conn sid) $
         closeConnection StreamStateError "send-only stream"
     mstrm <- findStream conn sid
@@ -270,8 +288,15 @@
     strm <- maybe (createStream conn sid) return mstrm
     let len = BS.length dat
         rx = RxStreamData dat off len fin
-    ok <- putRxStreamData strm rx
-    unless ok $ closeConnection FlowControlError "Flow control error in 1-RTT"
+    fc <- putRxStreamData strm rx
+    case fc of
+      -- FLOW CONTROL: MAX_STREAM_DATA: recv: rejecting if over my limit
+      OverLimit -> closeConnection FlowControlError "Flow control error for stream in 1-RTT"
+      Duplicated -> return ()
+      Reassembled -> do
+          ok' <- checkRxMaxData conn len
+          -- FLOW CONTROL: MAX_DATA: send: respecting peer's limit
+          unless ok' $ closeConnection FlowControlError "Flow control error for connection in 1-RTT"
 processFrame conn lvl (MaxData n) = do
     when (lvl == InitialLevel || lvl == HandshakeLevel) $
         closeConnection ProtocolViolation "MAX_DATA in Initial or Handshake"
@@ -293,9 +318,9 @@
     when (n > 2^(60 :: Int)) $
         closeConnection FrameEncodingError "Too large MAX_STREAMS"
     if dir == Bidirectional then
-        setMyMaxStreams conn n
+        setTxMaxStreams conn n
       else
-        setMyUniMaxStreams conn n
+        setTxUniMaxStreams conn n
 processFrame _conn _lvl DataBlocked{} = return ()
 processFrame _conn _lvl (StreamDataBlocked _sid _) = return ()
 processFrame _conn lvl (StreamsBlocked _dir n) = do
diff --git a/Network/QUIC/Stream.hs b/Network/QUIC/Stream.hs
--- a/Network/QUIC/Stream.hs
+++ b/Network/QUIC/Stream.hs
@@ -33,6 +33,7 @@
   -- * Reass
   , takeRecvStreamQwithSize
   , putRxStreamData
+  , FlowCntl(..)
   , tryReassemble
   -- * Table
   , StreamTable
diff --git a/Network/QUIC/Stream/Reass.hs b/Network/QUIC/Stream/Reass.hs
--- a/Network/QUIC/Stream/Reass.hs
+++ b/Network/QUIC/Stream/Reass.hs
@@ -4,6 +4,7 @@
 module Network.QUIC.Stream.Reass (
     takeRecvStreamQwithSize
   , putRxStreamData
+  , FlowCntl(..)
   , tryReassemble
   ) where
 
@@ -87,14 +88,16 @@
 ----------------------------------------------------------------
 ----------------------------------------------------------------
 
-putRxStreamData :: Stream -> RxStreamData -> IO Bool
-putRxStreamData s rx@(RxStreamData dat off _ _) = do
+data FlowCntl = OverLimit | Duplicated | Reassembled
+
+putRxStreamData :: Stream -> RxStreamData -> IO FlowCntl
+putRxStreamData s rx@(RxStreamData _ off len _) = do
     lim <- getRxMaxStreamData s
-    if BS.length dat + off > lim then
-        return False
+    if len + off > lim then
+        return OverLimit
       else do
-        _ <- tryReassemble s rx put putFin
-        return True
+        dup <- tryReassemble s rx put putFin
+        return $ if dup then Duplicated else Reassembled
   where
     put "" = return ()
     put d  = putRecvStreamQ s d
diff --git a/Network/QUIC/Types/Frame.hs b/Network/QUIC/Types/Frame.hs
--- a/Network/QUIC/Types/Frame.hs
+++ b/Network/QUIC/Types/Frame.hs
@@ -69,6 +69,12 @@
 isServerInitiated :: StreamId -> Bool
 isServerInitiated sid = (0b1 .&. sid) == 1
 
+isBidirectional :: StreamId -> Bool
+isBidirectional sid = (0b10 .&. sid) == 0
+
+isUnidirectional :: StreamId -> Bool
+isUnidirectional sid = (0b10 .&. sid) == 2
+
 type Delay = Milliseconds
 
 type Fin = Bool
diff --git a/quic.cabal b/quic.cabal
--- a/quic.cabal
+++ b/quic.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               quic
-version:            0.1.7
+version:            0.1.8
 license:            BSD3
 license-file:       LICENSE
 maintainer:         kazu@iij.ad.jp
diff --git a/test/FrameSpec.hs b/test/FrameSpec.hs
--- a/test/FrameSpec.hs
+++ b/test/FrameSpec.hs
@@ -1,9 +1,8 @@
 module FrameSpec where
 
-import Control.Monad
-import Data.ByteString.Internal (memset)
 import Data.Word
 import Foreign.Marshal.Alloc
+import Foreign.Marshal.Utils (fillBytes)
 import Foreign.Ptr
 import Foreign.Storable
 import Test.Hspec
@@ -19,7 +18,7 @@
             E.bracket (mallocBytes siz) free $ \beg -> do
                 let (+.) = plusPtr
                     end  = beg +. siz
-                void $ memset beg 0 $ fromIntegral siz
+                fillBytes beg 0 $ fromIntegral siz
                 countZero beg end `shouldReturn` siz
                 countZero (beg +. 1) end `shouldReturn` (siz - 1)
                 countZero (beg +. 2) end `shouldReturn` (siz - 2)
diff --git a/test/TransportError.hs b/test/TransportError.hs
--- a/test/TransportError.hs
+++ b/test/TransportError.hs
@@ -43,6 +43,9 @@
         it "MUST send FLOW_CONTROL_ERROR if a STREAM frame with a large offset is received [Transport 4.1]" $ \_ -> do
             let cc = addHook cc0 $ setOnPlainCreated largeOffset
             runCnoOp cc ms `shouldThrow` transportErrorsIn [FlowControlError]
+        it "MUST send STREAM_LIMIT_ERROR if a stream ID exceeding the limit" $ \_ -> do
+            let cc = addHook cc0 $ setOnPlainCreated largeStreamId
+            runCnoOp cc ms `shouldThrow` transportErrorsIn [StreamLimitError]
         it "MUST send TRANSPORT_PARAMETER_ERROR if initial_source_connection_id is missing [Transport 7.3]" $ \_ -> do
             let cc = addHook cc0 $ setOnTransportParametersCreated dropInitialSourceConnectionId
             runCnoOp cc ms `shouldThrow` transportErrorsIn [TransportParameterError]
@@ -228,6 +231,13 @@
   | otherwise        = plain
   where
     fake = StreamF 0 100000000 ["GET /\r\n"] True
+
+largeStreamId :: EncryptionLevel -> Plain -> Plain
+largeStreamId lvl plain
+  | lvl == RTT1Level = plain { plainFrames = fake : plainFrames plain }
+  | otherwise        = plain
+  where
+    fake = StreamF 1000000000 0 ["GET /\r\n"] True
 
 unknownFrame :: EncryptionLevel -> Plain -> Plain
 unknownFrame lvl plain
diff --git a/util/ClientX.hs b/util/ClientX.hs
--- a/util/ClientX.hs
+++ b/util/ClientX.hs
@@ -34,7 +34,7 @@
     loop n = do
         auxDebug "GET"
         get
-        threadDelay 1000000
+        threadDelay 100000
         loop (n - 1)
     get = do
         s <- stream conn
@@ -63,7 +63,7 @@
     loop n hdrblk = do
         auxDebug "GET"
         get hdrblk
-        threadDelay 1000000
+        threadDelay 100000
         loop (n - 1) hdrblk
     get hdrblk = do
         s <- stream conn
