# HG changeset patch
# User Kim Alvefur <zash@zash.se>
# Date 1629110092 -7200
# Node ID dcf38ac6a38c88d314c851f477ccca156d94df35
# Parent  b92f2abe0bda17b481f9d675e04331de16efffd7
net.server: Add a predrain callaback just before writes

Allows sneaking in things in the write buffer just before it's sent to
the network stack. For example ack requests, compression flushes or
other things that make sense to send after stanzas or other things.
This ensures any additional trailing data sent is included in the same
write, and possibly the same TCP packet. Other methods used such as
timers or nextTick might not have the same effect as it depends on
scheduling.

diff -r b92f2abe0bda -r dcf38ac6a38c net/server_epoll.lua
--- a/net/server_epoll.lua	Mon Aug 16 11:37:51 2021 +0200
+++ b/net/server_epoll.lua	Mon Aug 16 12:34:52 2021 +0200
@@ -472,6 +472,7 @@
 function interface:onwritable()
 	self:onconnect();
 	if not self.conn then return; end -- could have been closed in onconnect
+	self:on("predrain");
 	local buffer = self.writebuffer;
 	local data = buffer or "";
 	if type(buffer) == "table" then
diff -r b92f2abe0bda -r dcf38ac6a38c net/server_event.lua
--- a/net/server_event.lua	Mon Aug 16 11:37:51 2021 +0200
+++ b/net/server_event.lua	Mon Aug 16 12:34:52 2021 +0200
@@ -449,6 +449,7 @@
 	self.onstatus = listener.onstatus;
 	self.ondetach = listener.ondetach;
 	self.onattach = listener.onattach;
+	self.onpredrain = listener.onpredrain;
 	self.ondrain = listener.ondrain;
 	self:onattach(data);
 end
@@ -464,6 +465,8 @@
 end
 function interface_mt:onreadtimeout()
 end
+function interface_mt:onpredrain()
+end
 function interface_mt:ondrain()
 end
 function interface_mt:ondetach()
@@ -490,6 +493,7 @@
 		onincoming = listener.onincoming;  -- will be called when client sends data
 		ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs
 		onreadtimeout = listener.onreadtimeout; -- called when socket inactivity timeout occurs
+		onpredrain = listener.onpredrain; -- called before writes
 		ondrain = listener.ondrain; -- called when writebuffer is empty
 		ondetach = listener.ondetach; -- called when disassociating this listener from this connection
 		onstatus = listener.onstatus; -- called for status changes (e.g. of SSL/TLS)
@@ -540,6 +544,7 @@
 					interface.eventwritetimeout = false
 				end
 			end
+			interface:onpredrain();
 			interface.writebuffer = { t_concat(interface.writebuffer) }
 			local succ, err, byte = interface.conn:send( interface.writebuffer[1], 1, interface.writebufferlen )
 			--vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte )
diff -r b92f2abe0bda -r dcf38ac6a38c net/server_select.lua
--- a/net/server_select.lua	Mon Aug 16 11:37:51 2021 +0200
+++ b/net/server_select.lua	Mon Aug 16 12:34:52 2021 +0200
@@ -294,6 +294,7 @@
 	local dispatch = listeners.onincoming
 	local status = listeners.onstatus
 	local disconnect = listeners.ondisconnect
+	local predrain = listeners.onpredrain
 	local drain = listeners.ondrain
 	local onreadtimeout = listeners.onreadtimeout;
 	local detach = listeners.ondetach
@@ -338,6 +339,7 @@
 		dispatch = listeners.onincoming
 		disconnect = listeners.ondisconnect
 		status = listeners.onstatus
+		predrain = listeners.onpredrain
 		drain = listeners.ondrain
 		handler.onreadtimeout = listeners.onreadtimeout
 		detach = listeners.ondetach
@@ -552,6 +554,9 @@
 					listeners.onconnect(handler);
 				end
 			end
+			if predrain then
+				predrain(handler);
+			end
 			buffer = table_concat( bufferqueue, "", 1, bufferqueuelen )
 			succ, err, byte = send( socket, buffer, 1, bufferlen )
 			count = ( succ or byte or 0 ) * STAT_UNIT