diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,11 @@
 # ChangeLog for http2
 
+## 5.3.2
+
+* Avoid unnecessary empty data frames at end of stream
+  [#140](https://github.com/kazu-yamamoto/http2/pull/140)
+* Removing unnecessary API from ServerIO
+
 ## 5.3.1
 
 * Fix treatment of async exceptions
diff --git a/Network/HTTP2/H2/Sender.hs b/Network/HTTP2/H2/Sender.hs
--- a/Network/HTTP2/H2/Sender.hs
+++ b/Network/HTTP2/H2/Sender.hs
@@ -282,17 +282,23 @@
             _
             reqflush = do
                 let buf = confWriteBuffer `plusPtr` off
-                    off' = off + frameHeaderLength + datPayloadLen
                 (mtrailers, flag) <- do
                     Trailers trailers <- tlrmkr Nothing
                     if null trailers
                         then return (Nothing, setEndStream defaultFlags)
                         else return (Just trailers, defaultFlags)
-                fillFrameHeader FrameData datPayloadLen streamNumber flag buf
+                -- Avoid sending an empty data frame before trailers at the end
+                -- of a stream
+                off' <-
+                  if datPayloadLen /= 0 || isNothing mtrailers then do
+                    decreaseWindowSize ctx strm datPayloadLen
+                    fillFrameHeader FrameData datPayloadLen streamNumber flag buf
+                    return $ off + frameHeaderLength + datPayloadLen
+                  else
+                    return off
                 off'' <- handleTrailers mtrailers off'
                 _ <- sync Nothing
                 halfClosedLocal ctx strm Finished
-                decreaseWindowSize ctx strm datPayloadLen
                 if reqflush
                     then do
                         flushN off''
diff --git a/Network/HTTP2/Server/Run.hs b/Network/HTTP2/Server/Run.hs
--- a/Network/HTTP2/Server/Run.hs
+++ b/Network/HTTP2/Server/Run.hs
@@ -56,14 +56,13 @@
 
 ----------------------------------------------------------------
 
-data ServerIO = ServerIO
+data ServerIO a = ServerIO
     { sioMySockAddr :: SockAddr
     , sioPeerSockAddr :: SockAddr
-    , sioReadRequest :: IO (StreamId, Stream, Request)
-    , sioWriteResponse :: Stream -> Response -> IO ()
+    , sioReadRequest :: IO (a, Request)
+    , sioWriteResponse :: a -> Response -> IO ()
     -- ^ 'Response' MUST be created with 'responseBuilder'.
     -- Others are not supported.
-    , sioWriteBytes :: ByteString -> IO ()
     }
 
 -- | Launching a receiver and a sender without workers.
@@ -71,7 +70,7 @@
 runIO
     :: ServerConfig
     -> Config
-    -> (ServerIO -> IO (IO ()))
+    -> (ServerIO Stream -> IO (IO ()))
     -> IO ()
 runIO sconf conf@Config{..} action = do
     ok <- checkPreface conf
@@ -81,7 +80,7 @@
         ctx@Context{..} <- setup sconf conf lnch
         let get = do
                 (strm, inpObj) <- atomically $ readTQueue inpQ
-                return (streamNumber strm, strm, Request inpObj)
+                return (strm, Request inpObj)
             putR strm (Response OutObj{..}) = do
                 case outObjBody of
                     OutBodyBuilder builder -> do
@@ -90,14 +89,12 @@
                             out = OHeader outObjHeaders (Just next) outObjTrailers
                         enqueueOutput outputQ $ Output strm out sync
                     _ -> error "Response other than OutBodyBuilder is not supported"
-            putB bs = enqueueControl controlQ $ CFrames Nothing [bs]
             serverIO =
                 ServerIO
                     { sioMySockAddr = confMySockAddr
                     , sioPeerSockAddr = confPeerSockAddr
                     , sioReadRequest = get
                     , sioWriteResponse = putR
-                    , sioWriteBytes = putB
                     }
         io <- action serverIO
         concurrently_ io $ runH2 conf ctx
diff --git a/http2.cabal b/http2.cabal
--- a/http2.cabal
+++ b/http2.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               http2
-version:            5.3.1
+version:            5.3.2
 license:            BSD3
 license-file:       LICENSE
 maintainer:         Kazu Yamamoto <kazu@iij.ad.jp>
