Index: benchmark/io/http/main.cfa
===================================================================
--- benchmark/io/http/main.cfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/main.cfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -163,4 +163,5 @@
 	{
 		int pipe_cnt = options.clopts.nworkers * 2;
+		// int pipe_cnt = 0;
 		int pipe_off;
 		int * fds;
Index: benchmark/io/http/printer.cfa
===================================================================
--- benchmark/io/http/printer.cfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/printer.cfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -2,4 +2,5 @@
 #include "options.hfa"
 
+#include <clock.hfa>
 #include <fstream.hfa>
 #include <stats.hfa>
@@ -22,5 +23,7 @@
 	to.maxfd = max( to.maxfd, from.maxfd);
 	to.close += from.close; from.close = 0;
+	to.error += from.error; from.error = 0;
 	to.calls += from.calls; from.calls = 0;
+	to.eagain += from.eagain; from.eagain = 0;
 	to.tries += from.tries; from.tries = 0;
 	to.header += from.header; from.header = 0;
@@ -80,8 +83,10 @@
 			uint64_t accp_open = this.stats.accpt.accepts;
 			uint64_t accp_clse = this.stats.send.close;
+			uint64_t accp_errs = this.stats.send.error;
 			uint64_t accp_live = accp_open - accp_clse;
 
+			sout | "-----" | time() | "-----";
 			sout | "----- Acceptor Stats -----";
-			sout | "accepts : " | eng3(accp_open) |"opened," | eng3(accp_clse) |"closed," | eng3(accp_live) |"live";
+			sout | "accepts : " | eng3(accp_open) |"opened," | eng3(accp_clse) |"closed," | eng3(accp_live) |"live," | eng3(accp_errs) |"errors";
 			sout | "accept  : " | eng3(this.stats.accpt.accepts) | "calls," | eng3(this.stats.accpt.eagains) | "eagains," | eng3(this.stats.accpt.creates) | " thrds";
 			sout | nl;
@@ -89,11 +94,11 @@
 			sout | "----- Connection Stats -----";
 			sout | "max fd    : " | this.stats.send.maxfd;
-			sout | "sendfile  : " | eng3(calls) | "calls," | eng3(tries) | "tries (" | ratio | " try/call)";
+			sout | "sendfile  : " | eng3(calls) | "calls," | eng3(tries) | "tries (" | ratio | " try/call)," | eng3(this.stats.send.eagain) | "eagains";
 			sout | "            " | eng3(header) | "header," | eng3(splcin) | "splice in," | eng3(splcot) | "splice out";
-			sout | " - zipf sizes:";
-			for(i; zipf_cnts) {
-				double written = avgrd[i].calls > 0 ? ((double)avgrd[i].bytes) / avgrd[i].calls : 0;
-				sout | "        " | zipf_sizes[i] | "bytes," | avgrd[i].calls | "shorts," | written | "written";
-			}
+			// sout | " - zipf sizes:";
+			// for(i; zipf_cnts) {
+			// 	double written = avgrd[i].calls > 0 ? ((double)avgrd[i].bytes) / avgrd[i].calls : 0;
+			// 	sout | "        " | zipf_sizes[i] | "bytes," | avgrd[i].calls | "shorts," | written | "written";
+			// }
 		}
 
Index: benchmark/io/http/printer.hfa
===================================================================
--- benchmark/io/http/printer.hfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/printer.hfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -12,5 +12,7 @@
 	volatile uint64_t maxfd;
 	volatile uint64_t close;
+	volatile uint64_t error;
 	volatile uint64_t calls;
+	volatile uint64_t eagain;
 	volatile uint64_t tries;
 	volatile uint64_t header;
Index: benchmark/io/http/protocol.cfa
===================================================================
--- benchmark/io/http/protocol.cfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/protocol.cfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -18,4 +18,5 @@
 extern "C" {
       int snprintf ( char * s, size_t n, const char * format, ... );
+	ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
 	// #include <linux/io_uring.h>
 }
@@ -30,4 +31,5 @@
 #define PLAINTEXT_NOCOPY
 // #define LINKED_IO
+#define TRUE_SENDFILE
 
 static inline __s32 wait_res( io_future_t & this ) {
@@ -71,5 +73,9 @@
 		int ret = cfa_send(fd, it, len, 0, CFA_IO_LAZY);
 		if( ret < 0 ) {
-			if( errno == ECONNRESET || errno == EPIPE ) { close(fd); return -ECONNRESET; }
+			if( errno == ECONNRESET || errno == EPIPE || errno == EBADF ) {
+				ret = close(fd);
+				if( ret != 0 ) abort( "close in 'answer' error: (%d) %s\n", (int)errno, strerror(errno) );
+				return -ECONNRESET;
+			}
 
 			abort( "'answer error' error: (%d) %s\n", (int)errno, strerror(errno) );
@@ -83,9 +89,9 @@
 }
 
-int answer_error( int fd, HttpCode code ) {
-	/* paranoid */ assert( code < KNOWN_CODES && code != OK200 );
-	int idx = (int)code;
-	return answer( fd, http_msgs[idx]->msg, http_msgs[idx]->len );
-}
+// int answer_error( int fd, HttpCode code ) {
+// 	/* paranoid */ assert( code < KNOWN_CODES && code != OK200 );
+// 	int idx = (int)code;
+// 	return answer( fd, http_msgs[idx]->msg, http_msgs[idx]->len );
+// }
 
 static int fill_header(char * it, size_t size) {
@@ -103,51 +109,51 @@
 }
 
-#if defined(PLAINTEXT_NOCOPY)
-int answer_plaintext( int fd ) {
-	return answer(fd, http_msgs[OK200_PlainText]->msg, http_msgs[OK200_PlainText]->len); // +1 cause snprintf doesn't count nullterminator
-}
-#elif defined(PLAINTEXT_MEMCPY)
-#define TEXTSIZE 15
-int answer_plaintext( int fd ) {
-	char text[] = "Hello, World!\n\n";
-	char ts[] = xstr(TEXTSIZE) " \n\n";
-	_Static_assert(sizeof(text) - 1 == TEXTSIZE);
-	char buffer[512 + TEXTSIZE];
-	char * it = buffer;
-	memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len);
-	it += http_msgs[OK200]->len;
-	int len = http_msgs[OK200]->len;
-	memcpy(it, ts, sizeof(ts) - 1);
-	it += sizeof(ts) - 1;
-	len += sizeof(ts) - 1;
-	memcpy(it, text, TEXTSIZE);
-	return answer(fd, buffer, len + TEXTSIZE);
-}
-#elif defined(PLAINTEXT_1WRITE)
-int answer_plaintext( int fd ) {
-	char text[] = "Hello, World!\n\n";
-	char buffer[512 + sizeof(text)];
-	char * it = buffer;
-	memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len);
-	it += http_msgs[OK200]->len;
-	int len = http_msgs[OK200]->len;
-	int r = snprintf(it, 512 - len, "%d \n\n", sizeof(text));
-	it += r;
-	len += r;
-	memcpy(it, text, sizeof(text));
-	return answer(fd, buffer, len + sizeof(text));
-}
-#else
-int answer_plaintext( int fd ) {
-	char text[] = "Hello, World!\n\n";
-	int ret = answer_header(fd, sizeof(text));
-	if( ret < 0 ) return ret;
-	return answer(fd, text, sizeof(text));
-}
-#endif
-
-int answer_empty( int fd ) {
-	return answer_header(fd, 0);
-}
+// #if defined(PLAINTEXT_NOCOPY)
+// int answer_plaintext( int fd ) {
+// 	return answer(fd, http_msgs[OK200_PlainText]->msg, http_msgs[OK200_PlainText]->len); // +1 cause snprintf doesn't count nullterminator
+// }
+// #elif defined(PLAINTEXT_MEMCPY)
+// #define TEXTSIZE 15
+// int answer_plaintext( int fd ) {
+// 	char text[] = "Hello, World!\n\n";
+// 	char ts[] = xstr(TEXTSIZE) " \n\n";
+// 	_Static_assert(sizeof(text) - 1 == TEXTSIZE);
+// 	char buffer[512 + TEXTSIZE];
+// 	char * it = buffer;
+// 	memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len);
+// 	it += http_msgs[OK200]->len;
+// 	int len = http_msgs[OK200]->len;
+// 	memcpy(it, ts, sizeof(ts) - 1);
+// 	it += sizeof(ts) - 1;
+// 	len += sizeof(ts) - 1;
+// 	memcpy(it, text, TEXTSIZE);
+// 	return answer(fd, buffer, len + TEXTSIZE);
+// }
+// #elif defined(PLAINTEXT_1WRITE)
+// int answer_plaintext( int fd ) {
+// 	char text[] = "Hello, World!\n\n";
+// 	char buffer[512 + sizeof(text)];
+// 	char * it = buffer;
+// 	memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len);
+// 	it += http_msgs[OK200]->len;
+// 	int len = http_msgs[OK200]->len;
+// 	int r = snprintf(it, 512 - len, "%d \n\n", sizeof(text));
+// 	it += r;
+// 	len += r;
+// 	memcpy(it, text, sizeof(text));
+// 	return answer(fd, buffer, len + sizeof(text));
+// }
+// #else
+// int answer_plaintext( int fd ) {
+// 	char text[] = "Hello, World!\n\n";
+// 	int ret = answer_header(fd, sizeof(text));
+// 	if( ret < 0 ) return ret;
+// 	return answer(fd, text, sizeof(text));
+// }
+// #endif
+
+// int answer_empty( int fd ) {
+// 	return answer_header(fd, 0);
+// }
 
 static int sendfile( int pipe[2], int fd, int ans_fd, size_t count, sendfile_stats_t & stats ) {
@@ -161,365 +167,406 @@
 	if(zipf_idx < 0) mutex(serr) serr | "SENDFILE" | count | " greated than biggest zipf file";
 
-
-	unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE;
-	off_t offset = 0;
-	ssize_t ret;
-	SPLICE1: while(count > 0) {
-		stats.tries++;
-		// ret = cfa_splice(ans_fd, &offset, pipe[1], 0p, count, sflags, CFA_IO_LAZY);
-		ret = splice(ans_fd, &offset, pipe[1], 0p, count, sflags);
-		if( ret <= 0 ) {
-			if( errno == ECONNRESET ) return -ECONNRESET;
-			if( errno == EPIPE ) return -EPIPE;
-			abort( "splice [0] error: %d (%d) %s\n", ret, (int)errno, strerror(errno) );
-		}
-		count -= ret;
-		stats.splcin++;
-		if(count > 0) stats.avgrd[zipf_idx].calls++;
-		stats.avgrd[zipf_idx].bytes += ret;
-
-		size_t in_pipe = ret;
-		SPLICE2: while(in_pipe > 0) {
-			// ret = cfa_splice(pipe[0], 0p, fd, 0p, in_pipe, sflags, CFA_IO_LAZY);
-			ret = splice(pipe[0], 0p, fd, 0p, in_pipe, sflags);
+	#if defined(TRUE_SENDFILE)
+		off_t offset = 0;
+		ssize_t ret;
+		int flags = fcntl(fd, F_GETFL);
+		if(flags < 0) abort("getfl in 'true sendfile' error: (%d) %s\n", (int)errno, strerror(errno) );
+		ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+		if(ret < 0) abort("setfl in 'true sendfile' error: (%d) %s\n", (int)errno, strerror(errno) );
+
+		while(count) {
+		 	ret = sendfile(fd, ans_fd, &offset, count);
 			if( ret <= 0 ) {
-				if( errno == ECONNRESET ) return -ECONNRESET;
-				if( errno == EPIPE ) return -EPIPE;
-				abort( "splice [1] error: %d (%d) %s\n", ret, (int)errno, strerror(errno) );
+				if( errno == EAGAIN || errno == EWOULDBLOCK ) {
+					stats.eagain++;
+					yield();
+					continue;
+				}
+				if( errno == ECONNRESET || errno == EPIPE ) {
+					ret = close(fd);
+					if( ret != 0 ) abort( "close in 'true sendfile' error: (%d) %s\n", (int)errno, strerror(errno) );
+					return -ECONNRESET;
+				}
+				abort( "sendfile error: %d (%d) %s\n", ret, (int)errno, strerror(errno) );
 			}
-			stats.splcot++;
-			in_pipe -= ret;
-		}
-
-	}
+			count -= ret;
+			stats.splcin++;
+			if(count > 0) stats.avgrd[zipf_idx].calls++;
+			stats.avgrd[zipf_idx].bytes += ret;
+		}
+
+		ret = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+		if(ret < 0) abort("resetfl in 'true sendfile' error: (%d) %s\n", (int)errno, strerror(errno) );
+	#else
+		#error not implemented
+		// unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE;
+		// off_t offset = 0;
+		// ssize_t ret;
+		// SPLICE1: while(count > 0) {
+		// 	stats.tries++;
+		// 	// ret = cfa_splice(ans_fd, &offset, pipe[1], 0p, count, sflags, CFA_IO_LAZY);
+		// 	ret = splice(ans_fd, &offset, pipe[1], 0p, count, sflags);
+		// 	if( ret <= 0 ) {
+		// 		if( errno == ECONNRESET || errno == EPIPE ) {
+		// 			ret = close(fd);
+		// 			if( ret != 0 ) abort( "close in 'sendfile splice in' error: (%d) %s\n", (int)errno, strerror(errno) );
+		// 			return -ECONNRESET;
+		// 		}
+		// 		abort( "splice [0] error: %d (%d) %s\n", ret, (int)errno, strerror(errno) );
+		// 	}
+		// 	count -= ret;
+		// 	stats.splcin++;
+		// 	if(count > 0) stats.avgrd[zipf_idx].calls++;
+		// 	stats.avgrd[zipf_idx].bytes += ret;
+
+		// 	size_t in_pipe = ret;
+		// 	SPLICE2: while(in_pipe > 0) {
+		// 		ret = cfa_splice(pipe[0], 0p, fd, 0p, in_pipe, sflags, CFA_IO_LAZY);
+		// 		// ret = splice(pipe[0], 0p, fd, 0p, in_pipe, sflags);
+		// 		if( ret <= 0 ) {
+		// 			if( errno == ECONNRESET || errno == EPIPE ) {
+		// 				ret = close(fd);
+		// 				if( ret != 0 ) abort( "close in 'sendfile splice out' error: (%d) %s\n", (int)errno, strerror(errno) );
+		// 				return -ECONNRESET;
+		// 			}
+		// 			abort( "splice [1] error: %d (%d) %s\n", ret, (int)errno, strerror(errno) );
+		// 		}
+		// 		stats.splcot++;
+		// 		in_pipe -= ret;
+		// 	}
+
+		// }
+	#endif
+
 	return count;
 }
 
-enum FSM_STATE {
-	Initial,
-	Retry,
-	Error,
-	Done,
-};
-
-struct FSM_Result {
-	FSM_STATE state;
-	int error;
-};
-
-static inline void ?{}(FSM_Result & this) { this.state = Initial; this.error = 0; }
-static inline bool is_error(FSM_Result & this) { return Error == this.state; }
-static inline bool is_done(FSM_Result & this) { return Done == this.state; }
-
-static inline int error(FSM_Result & this, int error) {
-	this.error = error;
-	this.state = Error;
-	return error;
-}
-
-static inline int done(FSM_Result & this) {
-	this.state = Done;
-	return 0;
-}
-
-static inline int retry(FSM_Result & this) {
-	this.state = Retry;
-	return 0;
-}
-
-static inline int need(FSM_Result & this) {
-	switch(this.state) {
-		case Initial:
-		case Retry:
-			return 1;
-		case Error:
-			if(this.error == 0) mutex(serr) serr | "State marked error but code is 0";
-		case Done:
-			return 0;
-	}
-}
-
-// Generator that handles sending the header
-generator header_g {
-	io_future_t f;
-	const char * next;
-	int fd; size_t len;
-	FSM_Result res;
-};
-
-static inline void ?{}(header_g & this, int fd, const char * it, size_t len ) {
-	this.next = it;
-	this.fd = fd;
-	this.len = len;
-}
-
-static inline void fill(header_g & this, struct io_uring_sqe * sqe) {
-	zero_sqe(sqe);
-	sqe->opcode = IORING_OP_SEND;
-	sqe->user_data = (uintptr_t)&this.f;
-	sqe->flags = IOSQE_IO_LINK;
-	sqe->fd = this.fd;
-	sqe->addr = (uintptr_t)this.next;
-	sqe->len = this.len;
-}
-
-static inline int error(header_g & this, int error) {
-	int ret = close(this.fd);
-	if( ret != 0 ) {
-		mutex(serr) serr | "Failed to close fd" | errno;
-	}
-	return error(this.res, error);
-}
-
-static inline int wait_and_process(header_g & this, sendfile_stats_t & stats) {
-	wait(this.f);
-
-	// Did something crazy happen?
-	if(this.f.result > this.len) {
-		mutex(serr) serr | "HEADER sent too much!";
-		return error(this, -ERANGE);
-	}
-
-	// Something failed?
-	if(this.f.result < 0) {
-		int error = -this.f.result;
-		if( error == ECONNRESET ) return error(this, -ECONNRESET);
-		if( error == EPIPE ) return error(this, -EPIPE);
-		if( error == ECANCELED ) {
-			mutex(serr) serr | "HEADER was cancelled, WTF!";
-			return error(this, -ECONNRESET);
-		}
-		if( error == EAGAIN || error == EWOULDBLOCK) {
-			mutex(serr) serr | "HEADER got eagain, WTF!";
-			return error(this, -ECONNRESET);
-		}
-	}
-
-	// Done?
-	if(this.f.result == this.len) {
-		return done(this.res);
-	}
-
-	stats.header++;
-
-	// It must be a Short read
-	this.len  -= this.f.result;
-	this.next += this.f.result;
-	reset(this.f);
-	return retry(this.res);
-}
-
-// Generator that handles splicing in a file
-struct splice_in_t {
-	io_future_t f;
-	int fd; int pipe; size_t len; off_t off;
-	short zipf_idx;
-	FSM_Result res;
-};
-
-static inline void ?{}(splice_in_t & this, int fd, int pipe, size_t len) {
-	this.fd = fd;
-	this.pipe = pipe;
-	this.len = len;
-	this.off = 0;
-	this.zipf_idx = -1;
-	STATS: for(i; zipf_cnts) {
-		if(len <= zipf_sizes[i]) {
-			this.zipf_idx = i;
-			break STATS;
-		}
-	}
-	if(this.zipf_idx < 0) mutex(serr) serr | "SPLICE IN" | len | " greated than biggest zipf file";
-}
-
-static inline void fill(splice_in_t & this, struct io_uring_sqe * sqe) {
-	zero_sqe(sqe);
-	sqe->opcode = IORING_OP_SPLICE;
-	sqe->user_data = (uintptr_t)&this.f;
-	sqe->flags = 0;
-	sqe->splice_fd_in = this.fd;
-	sqe->splice_off_in = this.off;
-	sqe->fd = this.pipe;
-	sqe->off = (__u64)-1;
-	sqe->len = this.len;
-	sqe->splice_flags = SPLICE_F_MOVE;
-}
-
-static inline int wait_and_process(splice_in_t & this, sendfile_stats_t & stats ) {
-	wait(this.f);
-
-	// Something failed?
-	if(this.f.result < 0) {
-		int error = -this.f.result;
-		if( error == ECONNRESET ) return error(this.res, -ECONNRESET);
-		if( error == EPIPE ) return error(this.res, -EPIPE);
-		if( error == ECANCELED ) {
-			mutex(serr) serr | "SPLICE IN was cancelled, WTF!";
-			return error(this.res, -ECONNRESET);
-		}
-		if( error == EAGAIN || error == EWOULDBLOCK) {
-			mutex(serr) serr | "SPLICE IN got eagain, WTF!";
-			return error(this.res, -ECONNRESET);
-		}
-		mutex(serr) serr | "SPLICE IN got" | error | ", WTF!";
-		return error(this.res, -ECONNRESET);
-	}
-
-	// Did something crazy happen?
-	if(this.f.result > this.len) {
-		mutex(serr) serr | "SPLICE IN spliced too much!";
-		return error(this.res, -ERANGE);
-	}
-
-	// Done?
-	if(this.f.result == this.len) {
-		return done(this.res);
-	}
-
-	stats.splcin++;
-	stats.avgrd[this.zipf_idx].calls++;
-	stats.avgrd[this.zipf_idx].bytes += this.f.result;
-
-	// It must be a Short read
-	this.len -= this.f.result;
-	this.off += this.f.result;
-	reset(this.f);
-	return retry(this.res);
-}
-
-generator splice_out_g {
-	io_future_t f;
-	int pipe; int fd; size_t len;
-	FSM_Result res;
-};
-
-static inline void ?{}(splice_out_g & this, int pipe, int fd, size_t len) {
-	this.pipe = pipe;
-	this.fd = fd;
-	this.len = len;
-}
-
-static inline void fill(splice_out_g & this, struct io_uring_sqe * sqe) {
-	zero_sqe(sqe);
-	sqe->opcode = IORING_OP_SPLICE;
-	sqe->user_data = (uintptr_t)&this.f;
-	sqe->flags = 0;
-	sqe->splice_fd_in = this.pipe;
-	sqe->splice_off_in = (__u64)-1;
-	sqe->fd = this.fd;
-	sqe->off = (__u64)-1;
-	sqe->len = this.len;
-	sqe->splice_flags = SPLICE_F_MOVE;
-}
-
-static inline int error(splice_out_g & this, int error) {
-	int ret = close(this.fd);
-	if( ret != 0 ) {
-		mutex(serr) serr | "Failed to close fd" | errno;
-	}
-	return error(this.res, error);
-}
-
-static inline void wait_and_process(splice_out_g & this, sendfile_stats_t & stats ) {
-	wait(this.f);
-
-	// Something failed?
-	if(this.f.result < 0) {
-		int error = -this.f.result;
-		if( error == ECONNRESET ) return error(this, -ECONNRESET);
-		if( error == EPIPE ) return error(this, -EPIPE);
-		if( error == ECANCELED ) {
-			this.f.result = 0;
-			goto SHORT_WRITE;
-		}
-		if( error == EAGAIN || error == EWOULDBLOCK) {
-			mutex(serr) serr | "SPLICE OUT got eagain, WTF!";
-			return error(this, -ECONNRESET);
-		}
-		mutex(serr) serr | "SPLICE OUT got" | error | ", WTF!";
-		return error(this, -ECONNRESET);
-	}
-
-	// Did something crazy happen?
-	if(this.f.result > this.len) {
-		mutex(serr) serr | "SPLICE OUT spliced too much!" | this.f.result | ">" | this.len;
-		return error(this.res, -ERANGE);
-	}
-
-	// Done?
-	if(this.f.result == this.len) {
-		return done(this.res);
-	}
-
-SHORT_WRITE:
-	stats.splcot++;
-
-	// It must be a Short Write
-	this.len -= this.f.result;
-	reset(this.f);
-	return retry(this.res);
-}
+// enum FSM_STATE {
+// 	Initial,
+// 	Retry,
+// 	Error,
+// 	Done,
+// };
+
+// struct FSM_Result {
+// 	FSM_STATE state;
+// 	int error;
+// };
+
+// static inline void ?{}(FSM_Result & this) { this.state = Initial; this.error = 0; }
+// static inline bool is_error(FSM_Result & this) { return Error == this.state; }
+// static inline bool is_done(FSM_Result & this) { return Done == this.state; }
+
+// static inline int error(FSM_Result & this, int error) {
+// 	this.error = error;
+// 	this.state = Error;
+// 	return error;
+// }
+
+// static inline int done(FSM_Result & this) {
+// 	this.state = Done;
+// 	return 0;
+// }
+
+// static inline int retry(FSM_Result & this) {
+// 	this.state = Retry;
+// 	return 0;
+// }
+
+// static inline int need(FSM_Result & this) {
+// 	switch(this.state) {
+// 		case Initial:
+// 		case Retry:
+// 			return 1;
+// 		case Error:
+// 			if(this.error == 0) mutex(serr) serr | "State marked error but code is 0";
+// 		case Done:
+// 			return 0;
+// 	}
+// }
+
+// // Generator that handles sending the header
+// generator header_g {
+// 	io_future_t f;
+// 	const char * next;
+// 	int fd; size_t len;
+// 	FSM_Result res;
+// };
+
+// static inline void ?{}(header_g & this, int fd, const char * it, size_t len ) {
+// 	this.next = it;
+// 	this.fd = fd;
+// 	this.len = len;
+// }
+
+// static inline void fill(header_g & this, struct io_uring_sqe * sqe) {
+// 	zero_sqe(sqe);
+// 	sqe->opcode = IORING_OP_SEND;
+// 	sqe->user_data = (uintptr_t)&this.f;
+// 	sqe->flags = IOSQE_IO_LINK;
+// 	sqe->fd = this.fd;
+// 	sqe->addr = (uintptr_t)this.next;
+// 	sqe->len = this.len;
+// }
+
+// static inline int error(header_g & this, int error) {
+// 	int ret = close(this.fd);
+// 	if( ret != 0 ) {
+// 		mutex(serr) serr | "Failed to close fd" | errno;
+// 	}
+// 	return error(this.res, error);
+// }
+
+// static inline int wait_and_process(header_g & this, sendfile_stats_t & stats) {
+// 	wait(this.f);
+
+// 	// Did something crazy happen?
+// 	if(this.f.result > this.len) {
+// 		mutex(serr) serr | "HEADER sent too much!";
+// 		return error(this, -ERANGE);
+// 	}
+
+// 	// Something failed?
+// 	if(this.f.result < 0) {
+// 		int error = -this.f.result;
+// 		if( error == ECONNRESET ) return error(this, -ECONNRESET);
+// 		if( error == EPIPE ) return error(this, -EPIPE);
+// 		if( error == ECANCELED ) {
+// 			mutex(serr) serr | "HEADER was cancelled, WTF!";
+// 			return error(this, -ECONNRESET);
+// 		}
+// 		if( error == EAGAIN || error == EWOULDBLOCK) {
+// 			mutex(serr) serr | "HEADER got eagain, WTF!";
+// 			return error(this, -ECONNRESET);
+// 		}
+// 	}
+
+// 	// Done?
+// 	if(this.f.result == this.len) {
+// 		return done(this.res);
+// 	}
+
+// 	stats.header++;
+
+// 	// It must be a Short read
+// 	this.len  -= this.f.result;
+// 	this.next += this.f.result;
+// 	reset(this.f);
+// 	return retry(this.res);
+// }
+
+// // Generator that handles splicing in a file
+// struct splice_in_t {
+// 	io_future_t f;
+// 	int fd; int pipe; size_t len; off_t off;
+// 	short zipf_idx;
+// 	FSM_Result res;
+// };
+
+// static inline void ?{}(splice_in_t & this, int fd, int pipe, size_t len) {
+// 	this.fd = fd;
+// 	this.pipe = pipe;
+// 	this.len = len;
+// 	this.off = 0;
+// 	this.zipf_idx = -1;
+// 	STATS: for(i; zipf_cnts) {
+// 		if(len <= zipf_sizes[i]) {
+// 			this.zipf_idx = i;
+// 			break STATS;
+// 		}
+// 	}
+// 	if(this.zipf_idx < 0) mutex(serr) serr | "SPLICE IN" | len | " greated than biggest zipf file";
+// }
+
+// static inline void fill(splice_in_t & this, struct io_uring_sqe * sqe) {
+// 	zero_sqe(sqe);
+// 	sqe->opcode = IORING_OP_SPLICE;
+// 	sqe->user_data = (uintptr_t)&this.f;
+// 	sqe->flags = 0;
+// 	sqe->splice_fd_in = this.fd;
+// 	sqe->splice_off_in = this.off;
+// 	sqe->fd = this.pipe;
+// 	sqe->off = (__u64)-1;
+// 	sqe->len = this.len;
+// 	sqe->splice_flags = SPLICE_F_MOVE;
+// }
+
+// static inline int wait_and_process(splice_in_t & this, sendfile_stats_t & stats ) {
+// 	wait(this.f);
+
+// 	// Something failed?
+// 	if(this.f.result < 0) {
+// 		int error = -this.f.result;
+// 		if( error == ECONNRESET ) return error(this.res, -ECONNRESET);
+// 		if( error == EPIPE ) return error(this.res, -EPIPE);
+// 		if( error == ECANCELED ) {
+// 			mutex(serr) serr | "SPLICE IN was cancelled, WTF!";
+// 			return error(this.res, -ECONNRESET);
+// 		}
+// 		if( error == EAGAIN || error == EWOULDBLOCK) {
+// 			mutex(serr) serr | "SPLICE IN got eagain, WTF!";
+// 			return error(this.res, -ECONNRESET);
+// 		}
+// 		mutex(serr) serr | "SPLICE IN got" | error | ", WTF!";
+// 		return error(this.res, -ECONNRESET);
+// 	}
+
+// 	// Did something crazy happen?
+// 	if(this.f.result > this.len) {
+// 		mutex(serr) serr | "SPLICE IN spliced too much!";
+// 		return error(this.res, -ERANGE);
+// 	}
+
+// 	// Done?
+// 	if(this.f.result == this.len) {
+// 		return done(this.res);
+// 	}
+
+// 	stats.splcin++;
+// 	stats.avgrd[this.zipf_idx].calls++;
+// 	stats.avgrd[this.zipf_idx].bytes += this.f.result;
+
+// 	// It must be a Short read
+// 	this.len -= this.f.result;
+// 	this.off += this.f.result;
+// 	reset(this.f);
+// 	return retry(this.res);
+// }
+
+// generator splice_out_g {
+// 	io_future_t f;
+// 	int pipe; int fd; size_t len;
+// 	FSM_Result res;
+// };
+
+// static inline void ?{}(splice_out_g & this, int pipe, int fd, size_t len) {
+// 	this.pipe = pipe;
+// 	this.fd = fd;
+// 	this.len = len;
+// }
+
+// static inline void fill(splice_out_g & this, struct io_uring_sqe * sqe) {
+// 	zero_sqe(sqe);
+// 	sqe->opcode = IORING_OP_SPLICE;
+// 	sqe->user_data = (uintptr_t)&this.f;
+// 	sqe->flags = 0;
+// 	sqe->splice_fd_in = this.pipe;
+// 	sqe->splice_off_in = (__u64)-1;
+// 	sqe->fd = this.fd;
+// 	sqe->off = (__u64)-1;
+// 	sqe->len = this.len;
+// 	sqe->splice_flags = SPLICE_F_MOVE;
+// }
+
+// static inline int error(splice_out_g & this, int error) {
+// 	int ret = close(this.fd);
+// 	if( ret != 0 ) {
+// 		mutex(serr) serr | "Failed to close fd" | errno;
+// 	}
+// 	return error(this.res, error);
+// }
+
+// static inline void wait_and_process(splice_out_g & this, sendfile_stats_t & stats ) {
+// 	wait(this.f);
+
+// 	// Something failed?
+// 	if(this.f.result < 0) {
+// 		int error = -this.f.result;
+// 		if( error == ECONNRESET ) return error(this, -ECONNRESET);
+// 		if( error == EPIPE ) return error(this, -EPIPE);
+// 		if( error == ECANCELED ) {
+// 			this.f.result = 0;
+// 			goto SHORT_WRITE;
+// 		}
+// 		if( error == EAGAIN || error == EWOULDBLOCK) {
+// 			mutex(serr) serr | "SPLICE OUT got eagain, WTF!";
+// 			return error(this, -ECONNRESET);
+// 		}
+// 		mutex(serr) serr | "SPLICE OUT got" | error | ", WTF!";
+// 		return error(this, -ECONNRESET);
+// 	}
+
+// 	// Did something crazy happen?
+// 	if(this.f.result > this.len) {
+// 		mutex(serr) serr | "SPLICE OUT spliced too much!" | this.f.result | ">" | this.len;
+// 		return error(this.res, -ERANGE);
+// 	}
+
+// 	// Done?
+// 	if(this.f.result == this.len) {
+// 		return done(this.res);
+// 	}
+
+// SHORT_WRITE:
+// 	stats.splcot++;
+
+// 	// It must be a Short Write
+// 	this.len -= this.f.result;
+// 	reset(this.f);
+// 	return retry(this.res);
+// }
 
 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t fsize, sendfile_stats_t & stats ) {
 	stats.calls++;
 	#if defined(LINKED_IO)
-		char buffer[512];
-		int len = fill_header(buffer, fsize);
-		header_g header = { fd, buffer, len };
-		splice_in_t splice_in = { ans_fd, pipe[1], fsize };
-		splice_out_g splice_out = { pipe[0], fd, fsize };
-
-		RETRY_LOOP: for() {
-			stats.tries++;
-			int have = need(header.res) + need(splice_in.res) + 1;
-			int idx = 0;
-			struct io_uring_sqe * sqes[3];
-			__u32 idxs[3];
-			struct $io_context * ctx = cfa_io_allocate(sqes, idxs, have);
-
-			if(need(splice_in.res)) { fill(splice_in, sqes[idx++]); }
-			if(need(   header.res)) { fill(header   , sqes[idx++]); }
-			fill(splice_out, sqes[idx]);
-
-			// Submit everything
-			asm volatile("": : :"memory");
-			cfa_io_submit( ctx, idxs, have, false );
-
-			// wait for the results
-			// Always wait for splice-in to complete as
-			// we may need to kill the connection if it fails
-			// If it already completed, this is a no-op
-			wait_and_process(splice_in, stats);
-
-			if(is_error(splice_in.res)) {
-				if(splice_in.res.error == -EPIPE) return -ECONNRESET;
-				mutex(serr) serr | "SPLICE IN failed with" | splice_in.res.error;
-				close(fd);
-			}
-
-			// Process the other 2
-			wait_and_process(header, stats);
-			wait_and_process(splice_out, stats);
-
-			if(is_done(splice_out.res)) {
-				break RETRY_LOOP;
-			}
-
-			// We need to wait for the completion if
-			// - both completed
-			// - the header failed
-			// -
-
-			if(  is_error(header.res)
-			  || is_error(splice_in.res)
-			  || is_error(splice_out.res)) {
-				return -ECONNRESET;
-			}
-		}
-
-		return len + fsize;
+		// char buffer[512];
+		// int len = fill_header(buffer, fsize);
+		// header_g header = { fd, buffer, len };
+		// splice_in_t splice_in = { ans_fd, pipe[1], fsize };
+		// splice_out_g splice_out = { pipe[0], fd, fsize };
+
+		// RETRY_LOOP: for() {
+		// 	stats.tries++;
+		// 	int have = need(header.res) + need(splice_in.res) + 1;
+		// 	int idx = 0;
+		// 	struct io_uring_sqe * sqes[3];
+		// 	__u32 idxs[3];
+		// 	struct $io_context * ctx = cfa_io_allocate(sqes, idxs, have);
+
+		// 	if(need(splice_in.res)) { fill(splice_in, sqes[idx++]); }
+		// 	if(need(   header.res)) { fill(header   , sqes[idx++]); }
+		// 	fill(splice_out, sqes[idx]);
+
+		// 	// Submit everything
+		// 	asm volatile("": : :"memory");
+		// 	cfa_io_submit( ctx, idxs, have, false );
+
+		// 	// wait for the results
+		// 	// Always wait for splice-in to complete as
+		// 	// we may need to kill the connection if it fails
+		// 	// If it already completed, this is a no-op
+		// 	wait_and_process(splice_in, stats);
+
+		// 	if(is_error(splice_in.res)) {
+		// 		if(splice_in.res.error == -EPIPE) return -ECONNRESET;
+		// 		mutex(serr) serr | "SPLICE IN failed with" | splice_in.res.error;
+		// 		int ret = close(fd);
+		// 		if( ret != 0 ) abort( "close in 'answer sendfile' error: (%d) %s\n", (int)errno, strerror(errno) );
+		// 	}
+
+		// 	// Process the other 2
+		// 	wait_and_process(header, stats);
+		// 	wait_and_process(splice_out, stats);
+
+		// 	if(is_done(splice_out.res)) {
+		// 		break RETRY_LOOP;
+		// 	}
+
+		// 	// We need to wait for the completion if
+		// 	// - both completed
+		// 	// - the header failed
+		// 	// -
+
+		// 	if(  is_error(header.res)
+		// 	  || is_error(splice_in.res)
+		// 	  || is_error(splice_out.res)) {
+		// 		return -ECONNRESET;
+		// 	}
+		// }
+
+		// return len + fsize;
 	#else
 		int ret = answer_header(fd, fsize);
-		if( ret < 0 ) { close(fd); return ret; }
+		if( ret < 0 ) { return ret; }
 		return sendfile(pipe, fd, ans_fd, fsize, stats);
 	#endif
@@ -541,8 +588,15 @@
 		}
 		// int ret = read(fd, (void*)it, count);
-		if(ret == 0 ) { close(fd); return [OK200, true, 0, 0]; }
+		if(ret == 0 ) {
+			ret = close(fd);
+			if( ret != 0 ) abort( "close in 'http read good' error: (%d) %s\n", (int)errno, strerror(errno) );
+			return [OK200, true, 0, 0];
+		}
 		if(ret < 0 ) {
-			if( errno == ECONNRESET ) { close(fd); return [E408, true, 0, 0]; }
-			if( errno == EPIPE ) { close(fd); return [E408, true, 0, 0]; }
+			if( errno == ECONNRESET || errno == EPIPE ) {
+				ret = close(fd);
+				if( ret != 0 ) abort( "close in 'http read bad' error: (%d) %s\n", (int)errno, strerror(errno) );
+				return [E408, true, 0, 0];
+			}
 			abort( "read error: (%d) %s\n", (int)errno, strerror(errno) );
 		}
Index: benchmark/io/http/protocol.hfa
===================================================================
--- benchmark/io/http/protocol.hfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/protocol.hfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -18,7 +18,7 @@
 int code_val(HttpCode code);
 
-int answer_error( int fd, HttpCode code );
-int answer_plaintext( int fd );
-int answer_empty( int fd );
+// int answer_error( int fd, HttpCode code );
+// int answer_plaintext( int fd );
+// int answer_empty( int fd );
 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t count, struct sendfile_stats_t & );
 
Index: benchmark/io/http/socket.cfa
===================================================================
--- benchmark/io/http/socket.cfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/socket.cfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -8,4 +8,5 @@
 	#include <sys/socket.h>
 	#include <netinet/in.h>
+	#include <netinet/tcp.h>
 }
 
@@ -33,9 +34,16 @@
 	}
 
+	int on = 1;
+	const struct linger l = { 1, 0 };
+	if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
+		abort( "setsockopt SO_LINGER error: (%d) %s\n", (int)errno, strerror(errno) );
+
+	if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const void*)&on, sizeof(on)) < 0)
+		abort( "setsockopt SO_LINGER error: (%d) %s\n", (int)errno, strerror(errno) );
+
 	if(options.socket.reuseport) {
-		int value = 1;
 		// if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*)&on, sizeof(on)))
 		// 	abort( "setsockopt SO_REUSEADDR error: (%d) %s\n", (int)errno, strerror(errno) );
-		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(int)) < 0)
+		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0)
 			abort( "setsockopt SO_REUSEPORT error: (%d) %s\n", (int)errno, strerror(errno) );
 	}
Index: benchmark/io/http/worker.cfa
===================================================================
--- benchmark/io/http/worker.cfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/worker.cfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -14,5 +14,5 @@
 #include "filecache.hfa"
 
-static const unsigned long long period = 50_000_000;
+static const unsigned long long period = 5_000_000;
 
 //=============================================================================================
@@ -33,33 +33,36 @@
 
 		// if we are done, break out of the loop
-		if( closed ) break REQUEST;
+		if( closed ) {
+			if( code != OK200 ) this.stats.sendfile.error++;
+			break REQUEST;
+		}
 
 		// If this wasn't a request retrun 400
 		if( code != OK200 ) {
-			sout | "=== Invalid Request :" | code_val(code) | "===";
-			answer_error(fd, code);
-			continue REQUEST;
-		}
-
-		if(0 == strncmp(file, "plaintext", min(name_size, sizeof("plaintext") ))) {
-			if( options.log ) mutex(sout) sout | "=== Request for /plaintext ===";
-
-			int ret = answer_plaintext(fd);
-			if( ret == -ECONNRESET ) break REQUEST;
-
-			if( options.log ) mutex(sout) sout | "=== Answer sent ===";
-			continue REQUEST;
-		}
-
-		if(0 == strncmp(file, "ping", min(name_size, sizeof("ping") ))) {
-			if( options.log ) mutex(sout) sout | "=== Request for /ping ===";
-
-			// Send the header
-			int ret = answer_empty(fd);
-			if( ret == -ECONNRESET ) break REQUEST;
-
-			if( options.log ) mutex(sout) sout | "=== Answer sent ===";
-			continue REQUEST;
-		}
+			abort | "=== Invalid Request :" | code_val(code) | "===";
+			// answer_error(fd, code);
+			// continue REQUEST;
+		}
+
+		// if(0 == strncmp(file, "plaintext", min(name_size, sizeof("plaintext") ))) {
+		// 	if( options.log ) mutex(sout) sout | "=== Request for /plaintext ===";
+
+		// 	int ret = answer_plaintext(fd);
+		// 	if( ret == -ECONNRESET ) { this.stats.sendfile.error++; break REQUEST; }
+
+		// 	if( options.log ) mutex(sout) sout | "=== Answer sent ===";
+		// 	continue REQUEST;
+		// }
+
+		// if(0 == strncmp(file, "ping", min(name_size, sizeof("ping") ))) {
+		// 	if( options.log ) mutex(sout) sout | "=== Request for /ping ===";
+
+		// 	// Send the header
+		// 	int ret = answer_empty(fd);
+		// 	if( ret == -ECONNRESET ) { this.stats.sendfile.error++; break REQUEST; }
+
+		// 	if( options.log ) mutex(sout) sout | "=== Answer sent ===";
+		// 	continue REQUEST;
+		// }
 
 		if( options.log ) {
@@ -70,11 +73,12 @@
 
 		if( !options.file_cache.path ) {
-			if( options.log ) {
-				sout | "=== File Not Found (" | nonl;
-				write(sout, file, name_size);
-				sout | ") ===";
-			}
-			answer_error(fd, E405);
-			continue REQUEST;
+			// if( options.log ) {
+				serr | "=== File Not Found (" | nonl;
+				write(serr, file, name_size);
+				serr | ") ===";
+				abort();
+			// }
+			// answer_error(fd, E405);
+			// continue REQUEST;
 		}
 
@@ -86,11 +90,12 @@
 		// If we can't find the file, return 404
 		if( ans_fd < 0 ) {
-			if( options.log ) {
-				sout | "=== File Not Found (" | nonl;
-				write(sout, file, name_size);
-				sout | ") ===";
-			}
-			answer_error(fd, E404);
-			continue REQUEST;
+			// if( options.log ) {
+				serr | "=== File Not Found 2 (" | nonl;
+				write(serr, file, name_size);
+				serr | ") ===";
+				abort();
+			// }
+			// answer_error(fd, E404);
+			// continue REQUEST;
 		}
 
@@ -98,8 +103,9 @@
 		int ret = answer_sendfile( this.pipe, fd, ans_fd, count, this.stats.sendfile );
 		if(ret < 0) {
-			if( ret == -ECONNABORTED ) break REQUEST;
-			if( ret == -ECONNRESET ) break REQUEST;
-			if( ret == -EPIPE ) break REQUEST;
-			abort( "sendfile error: %d (%d) %s\n", ret, (int)errno, strerror(errno) );
+			if( ret == -ECONNABORTED ) { this.stats.sendfile.error++; break REQUEST; }
+			if( ret == -ECONNRESET ) { this.stats.sendfile.error++; break REQUEST; }
+			if( ret == -EPIPE ) { this.stats.sendfile.error++; break REQUEST; }
+			if( ret == -EBADF ) { this.stats.sendfile.error++; break REQUEST; }
+			abort( "answer sendfile error: %d (%d) %s\n", ret, (int)errno, strerror(errno) );
 		}
 
@@ -107,13 +113,15 @@
 	}
 
+	this.stats.sendfile.close++;
+
 	if (stats_thrd) {
-		unsigned long long next = rdtscl();
-		if(next > (last + period)) {
+		// unsigned long long next = rdtscl();
+		// if(next > (last + period)) {
 			if(try_lock(stats_thrd->stats.lock __cfaabi_dbg_ctx2)) {
 				push(this.stats.sendfile, stats_thrd->stats.send);
 				unlock(stats_thrd->stats.lock);
-				last = next;
-			}
-		}
+				// last = next;
+			}
+		// }
 	}
 }
@@ -144,4 +152,10 @@
 		if(this.done) break;
 
+		this.stats.accepts++;
+		if (stats_thrd && try_lock(stats_thrd->stats.lock)) {
+			push(this.stats, stats_thrd->stats.accpt);
+			unlock(stats_thrd->stats.lock);
+		}
+
 		if( options.log ) mutex(sout) sout | "=== New connection" | fd | "" | ", waiting for requests ===";
 		size_t len = options.socket.buflen;
@@ -149,5 +163,4 @@
 		handle_connection( this.conn, fd, buffer, len, 0p, last );
 		this.conn.stats.sendfile.maxfd = max(this.conn.stats.sendfile.maxfd, fd);
-		this.conn.stats.sendfile.close++;
 
 		if( options.log ) mutex(sout) sout | "=== Connection closed ===";
@@ -172,4 +185,5 @@
 	/* paranoid */ assert( this.conn.pipe[1] != -1 );
 	this.conn.stats.sendfile.maxfd = max(this.conn.pipe[0], this.conn.pipe[1]);
+	// this.conn.stats.sendfile.maxfd = 0;
 	for() {
 		size_t len = options.socket.buflen;
@@ -220,4 +234,5 @@
 		p = pop(*this.queue);
 		if(p) break;
+		// abort( "Too few threads" );
 		yield();
 		this.stats.creates++;
@@ -288,12 +303,12 @@
 
 		if (stats_thrd) {
-			unsigned long long next = rdtscl();
-			if(next > (last + period)) {
+			// unsigned long long next = rdtscl();
+			// if(next > (last + period)) {
 				if(try_lock(stats_thrd->stats.lock)) {
 					push(this.stats, stats_thrd->stats.accpt);
 					unlock(stats_thrd->stats.lock);
-					last = next;
+					// last = next;
 				}
-			}
+			// }
 		}
 
Index: benchmark/io/http/worker.hfa
===================================================================
--- benchmark/io/http/worker.hfa	(revision c4c8571a1aaa3100966db2d603b3a32857faebdd)
+++ benchmark/io/http/worker.hfa	(revision 32d13834026c45a070b8ffbeecf6d71de04dd54a)
@@ -34,4 +34,5 @@
 	int flags;
 	volatile bool done;
+	acceptor_stats_t stats;
 };
 void ?{}( AcceptWorker & this);
