diff --git a/daemon/io.c b/daemon/io.c
index 13909a3a1508e5a9e996d3bc6a77a4a1acff25e5..d0235f94809e0c8cff17b56a70f27f2218faa056 100644
--- a/daemon/io.c
+++ b/daemon/io.c
@@ -298,6 +298,7 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
 	session->has_tls = tls;
 	if (tls && !session->tls_ctx) {
 		session->tls_ctx = tls_new(master->loop->data);
+		session->tls_ctx->session = session;
 	}
 	uv_timer_t *timer = &session->timeout;
 	uv_timer_start(timer, tcp_timeout_trigger, KR_CONN_RTT_MAX/2, KR_CONN_RTT_MAX/2);
diff --git a/daemon/tls.c b/daemon/tls.c
index b5378f4d4c6771cdac1ea69bbbac9ad75d63ba3b..56117a1b6e0684c236c1026cca170a0870d79ebf 100644
--- a/daemon/tls.c
+++ b/daemon/tls.c
@@ -38,19 +38,6 @@
 #define EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE 60*60*24*7
 #define GNUTLS_PIN_MIN_VERSION  0x030400
 
-struct tls_client_ctx_t {
-	gnutls_session_t tls_session;
-	tls_client_hs_state_t handshake_state;
-
-	struct session *session;
-	tls_handshake_cb handshake_cb;
-	const uint8_t *buf;
-	ssize_t nread;
-	ssize_t consumed;
-	uint8_t recv_buf[4096];
-	const struct tls_client_paramlist_entry *params;
-};
-
 /** @internal Debugging facility. */
 #ifdef DEBUG
 #define DEBUG_MSG(fmt...) kr_log_verbose("[tls] " fmt)
@@ -79,32 +66,6 @@ static int kres_gnutls_set_priority(gnutls_session_t session) {
 	return err;
 }
 
-
-static ssize_t kres_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len)
-{
-	struct tls_ctx_t *t = (struct tls_ctx_t *)h;
-	const uv_buf_t ub = {(void *)buf, len};
-
-	DEBUG_MSG("[tls] push %zu <%p>\n", len, h);
-	if (t == NULL) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	int ret = uv_try_write(t->handle, &ub, 1);
-	if (ret > 0) {
-		return (ssize_t) ret;
-	}
-	if (ret == UV_EAGAIN) {
-		errno = EAGAIN;
-	} else {
-		kr_log_error("[tls] uv_try_write: %s\n", uv_strerror(ret));
-		errno = EIO;
-	}
-	return -1;
-}
-
-
 static ssize_t kres_gnutls_pull(gnutls_transport_ptr_t h, void *buf, size_t len)
 {
 	struct tls_ctx_t *t = (struct tls_ctx_t *)h;
@@ -168,32 +129,48 @@ struct tls_ctx_t *tls_new(struct worker_ctx *worker)
 		return NULL;
 	}
 
-	int err = gnutls_init(&tls->session, GNUTLS_SERVER | GNUTLS_NONBLOCK);
+	int err = gnutls_init(&tls->tls_session, GNUTLS_SERVER | GNUTLS_NONBLOCK);
 	if (err != GNUTLS_E_SUCCESS) {
 		kr_log_error("[tls] gnutls_init(): %s (%d)\n", gnutls_strerror_name(err), err);
 		tls_free(tls);
 		return NULL;
 	}
 	tls->credentials = tls_credentials_reserve(net->tls_credentials);
-	err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, tls->credentials->credentials);
+	err = gnutls_credentials_set(tls->tls_session, GNUTLS_CRD_CERTIFICATE, tls->credentials->credentials);
 	if (err != GNUTLS_E_SUCCESS) {
 		kr_log_error("[tls] gnutls_credentials_set(): %s (%d)\n", gnutls_strerror_name(err), err);
 		tls_free(tls);
 		return NULL;
 	}
-	if (kres_gnutls_set_priority(tls->session) != GNUTLS_E_SUCCESS) {
+	if (kres_gnutls_set_priority(tls->tls_session) != GNUTLS_E_SUCCESS) {
 		tls_free(tls);
 		return NULL;
 	}
 
 	tls->worker = worker;
 
-	gnutls_transport_set_pull_function(tls->session, kres_gnutls_pull);
-	gnutls_transport_set_push_function(tls->session, worker_gnutls_push);
-	gnutls_transport_set_ptr(tls->session, tls);
+	gnutls_transport_set_pull_function(tls->tls_session, kres_gnutls_pull);
+	gnutls_transport_set_push_function(tls->tls_session, worker_gnutls_push);
+	gnutls_transport_set_ptr(tls->tls_session, tls);
 	return tls;
 }
 
+void tls_close(struct tls_ctx_t *ctx)
+{
+	if (ctx == NULL || ctx->tls_session == NULL) {
+		return;
+	}
+
+	assert(ctx->session);
+
+	if (ctx->handshake_done) {
+		DEBUG_MSG("[tls] closing tls connection to `%s`\n",
+			  kr_straddr(&ctx->session->peer.ip));
+		ctx->handshake_done = false;
+		gnutls_bye(ctx->tls_session, GNUTLS_SHUT_RDWR);
+	}
+}
+
 void tls_free(struct tls_ctx_t *tls)
 {
 	if (!tls) {
@@ -202,15 +179,16 @@ void tls_free(struct tls_ctx_t *tls)
 
 	if (tls->session) {
 		/* Don't terminate TLS connection, just tear it down */
-		gnutls_deinit(tls->session);
-		tls->session = NULL;
+		gnutls_deinit(tls->tls_session);
+		tls->tls_session = NULL;
 	}
 
 	tls_credentials_release(tls->credentials);
 	free(tls);
 }
 
-int tls_push(struct qr_task *task, uv_handle_t *handle, knot_pkt_t *pkt)
+int tls_push(struct qr_task *task, uv_handle_t *handle, knot_pkt_t *pkt,
+	     bool server_side)
 {
 	if (!pkt || !handle || !handle->data) {
 		return kr_error(EINVAL);
@@ -218,51 +196,66 @@ int tls_push(struct qr_task *task, uv_handle_t *handle, knot_pkt_t *pkt)
 
 	struct session *session = handle->data;
 	const uint16_t pkt_size = htons(pkt->size);
-	struct tls_ctx_t *tls_p = session->tls_ctx;
-	if (!tls_p) {
-		kr_log_error("[tls] no tls context on push\n");
-		return kr_error(ENOENT);
+	static char const server_logstring[] = "tls";
+	static char const client_logstring[] = "tls-client";
+	const char *logstring = client_logstring;
+	gnutls_session_t tls_session = NULL;
+
+	if (server_side) {
+		logstring = server_logstring;
+		if (!session->tls_ctx) {
+			kr_log_error("[%s] no tls context on push\n", logstring);
+			return kr_error(ENOENT);
+		}
+		session->tls_ctx->task = task;
+		tls_session = session->tls_ctx->tls_session;
+	} else {
+		if (!session->tls_client_ctx) {
+			kr_log_error("[%s] no tls context on push\n", logstring);
+			return kr_error(ENOENT);
+		}
+		session->tls_client_ctx->task = task;
+		tls_session = session->tls_client_ctx->tls_session;
 	}
 
-	assert(gnutls_record_check_corked(tls_p->session) == 0);
+	assert(gnutls_record_check_corked(tls_session) == 0);
 
-	tls_p->task = task;
-
-	gnutls_record_cork(tls_p->session);
+	gnutls_record_cork(tls_session);
 	ssize_t count = 0;
-	if ((count = gnutls_record_send(tls_p->session, &pkt_size, sizeof(pkt_size)) < 0) ||
-	    (count = gnutls_record_send(tls_p->session, pkt->wire, pkt->size) < 0)) {
-		kr_log_error("[tls] gnutls_record_send failed: %s (%zd)\n", gnutls_strerror_name(count), count);
+	if ((count = gnutls_record_send(tls_session, &pkt_size, sizeof(pkt_size)) < 0) ||
+	    (count = gnutls_record_send(tls_session, pkt->wire, pkt->size) < 0)) {
+		kr_log_error("[%s] gnutls_record_send failed: %s (%zd)\n",
+			     logstring, gnutls_strerror_name(count), count);
 		return kr_error(EIO);
 	}
 
 	ssize_t submitted = 0;
 	ssize_t retries = 0;
 	do {
-		count = gnutls_record_uncork(tls_p->session, 0);
+		count = gnutls_record_uncork(tls_session, 0);
 		if (count < 0) {
 			if (gnutls_error_is_fatal(count)) {
-				kr_log_error("[tls] gnutls_record_uncork failed: %s (%zd)\n",
-				             gnutls_strerror_name(count), count);
+				kr_log_error("[%s] gnutls_record_uncork failed: %s (%zd)\n",
+				             logstring, gnutls_strerror_name(count), count);
 				return kr_error(EIO);
 			}
 			if (++retries > TLS_MAX_UNCORK_RETRIES) {
-				kr_log_error("[tls] gnutls_record_uncork: too many sequential non-fatal errors (%zd), last error is: %s (%zd)\n",
-				             retries, gnutls_strerror_name(count), count);
+				kr_log_error("[%s] gnutls_record_uncork: too many sequential non-fatal errors (%zd), last error is: %s (%zd)\n",
+				             logstring, retries, gnutls_strerror_name(count), count);
 				return kr_error(EIO);
 			}
 		} else if (count != 0) {
 			submitted += count;
 			retries = 0;
-		} else if (gnutls_record_check_corked(tls_p->session) != 0) {
+		} else if (gnutls_record_check_corked(tls_session) != 0) {
 			if (++retries > TLS_MAX_UNCORK_RETRIES) {
-				kr_log_error("[tls] gnutls_record_uncork: too many retries (%zd)\n",
-				             retries);
+				kr_log_error("[%s] gnutls_record_uncork: too many retries (%zd)\n",
+				             logstring, retries);
 				return kr_error(EIO);
 			}
 		} else if (submitted != sizeof(pkt_size) + pkt->size) {
-			kr_log_error("[tls] gnutls_record_uncork didn't send all data(%zd of %zd)\n",
-			             submitted, sizeof(pkt_size) + pkt->size);
+			kr_log_error("[%s] gnutls_record_uncork didn't send all data(%zd of %zd)\n",
+			             logstring, submitted, sizeof(pkt_size) + pkt->size);
 			return kr_error(EIO);
 		}
 	} while (submitted != sizeof(pkt_size) + pkt->size);
@@ -278,15 +271,18 @@ int tls_process(struct worker_ctx *worker, uv_stream_t *handle, const uint8_t *b
 		return kr_error(ENOSYS);
 	}
 
+	assert(tls_p->session == session);
+
 	tls_p->buf = buf;
 	tls_p->nread = nread >= 0 ? nread : 0;
-	tls_p->handle = handle;
 	tls_p->consumed = 0;	/* TODO: doesn't handle split TLS records */
 
 	/* Ensure TLS handshake is performed before receiving data. */
 	while (!tls_p->handshake_done) {
-		int err = gnutls_handshake(tls_p->session);
+		int err = gnutls_handshake(tls_p->tls_session);
 		if (err == GNUTLS_E_SUCCESS) {
+			DEBUG_MSG("[tls] TLS handshake with %s has completed\n",
+				  kr_straddr(&session->peer.ip));
 			tls_p->handshake_done = true;
 		} else if (err == GNUTLS_E_AGAIN) {
 			return 0; /* No data, bail out */
@@ -297,7 +293,7 @@ int tls_process(struct worker_ctx *worker, uv_stream_t *handle, const uint8_t *b
 
 	int submitted = 0;
 	while (true) {
-		ssize_t count = gnutls_record_recv(tls_p->session, tls_p->recv_buf, sizeof(tls_p->recv_buf));
+		ssize_t count = gnutls_record_recv(tls_p->tls_session, tls_p->recv_buf, sizeof(tls_p->recv_buf));
 		if (count == GNUTLS_E_AGAIN) {
 			break;    /* No data available */
 		} else if (count == GNUTLS_E_INTERRUPTED) {
@@ -806,38 +802,13 @@ static int client_verify_certificate(gnutls_session_t tls_session)
 	return GNUTLS_E_CERTIFICATE_ERROR;
 }
 
-static ssize_t kres_gnutls_client_push(gnutls_transport_ptr_t h, const void *buf, size_t len)
-{
-	struct tls_client_ctx_t *t = (struct tls_client_ctx_t *)h;
-	const uv_buf_t ub = {(void *)buf, len};
-
-	DEBUG_MSG("[tls_client] push %zu <%p>\n", len, h);
-	if (t == NULL) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	int ret = uv_try_write((uv_stream_t *)t->session->handle, &ub, 1);
-	if (ret > 0) {
-		return (ssize_t) ret;
-	}
-	if (ret == UV_EAGAIN) {
-		errno = EAGAIN;
-	} else {
-		kr_log_error("[tls_client] uv_try_write: %s\n", uv_strerror(ret));
-		errno = EIO;
-	}
-	return -1;
-}
-
-
 static ssize_t kres_gnutls_client_pull(gnutls_transport_ptr_t h, void *buf, size_t len)
 {
 	struct tls_client_ctx_t *t = (struct tls_client_ctx_t *)h;
 	assert(t != NULL);
 
 	ssize_t	avail = t->nread - t->consumed;
-	DEBUG_MSG("[tls] pull wanted: %zu available: %zu\n", len, avail);
+	DEBUG_MSG("[tls_client] pull wanted: %zu available: %zu\n", len, avail);
 	if (t->nread <= t->consumed) {
 		errno = EAGAIN;
 		return -1;
@@ -933,7 +904,8 @@ int tls_client_process(struct worker_ctx *worker, uv_stream_t *handle, const uin
 		if (ctx->handshake_cb) {
 			ctx->handshake_cb(ctx->session, 0);
 		}
-		DEBUG_MSG("[tls_client] TLS handshake with %s has completed.\n", kr_straddr(&session->peer.ip));
+		DEBUG_MSG("[tls_client] TLS handshake with %s has completed.\n",
+			  kr_straddr(&session->peer.ip));
 	}
 
 	int submitted = 0;
@@ -961,7 +933,8 @@ int tls_client_process(struct worker_ctx *worker, uv_stream_t *handle, const uin
 	return submitted;
 }
 
-struct tls_client_ctx_t *tls_client_ctx_new(const struct tls_client_paramlist_entry *entry)
+struct tls_client_ctx_t *tls_client_ctx_new(const struct tls_client_paramlist_entry *entry,
+					    struct worker_ctx *worker)
 {
 	struct tls_client_ctx_t *ctx = calloc(1, sizeof (struct tls_client_ctx_t));
 	if (!ctx) {
@@ -987,8 +960,10 @@ struct tls_client_ctx_t *tls_client_ctx_new(const struct tls_client_paramlist_en
 		return NULL;
 	}
 
+	ctx->worker = worker;
+
 	gnutls_transport_set_pull_function(ctx->tls_session, kres_gnutls_client_pull);
-	gnutls_transport_set_push_function(ctx->tls_session, kres_gnutls_client_push);
+	gnutls_transport_set_push_function(ctx->tls_session, worker_gnutls_client_push);
 	gnutls_transport_set_ptr(ctx->tls_session, ctx);
 	return ctx;
 }
@@ -999,8 +974,9 @@ void tls_client_ctx_free(struct tls_client_ctx_t *ctx)
 		return;
 	}
 
-	if (ctx->session != NULL) {
+	if (ctx->tls_session != NULL) {
 		gnutls_deinit(ctx->tls_session);
+		ctx->tls_session = NULL;
 	}
 
 	free (ctx);
@@ -1035,11 +1011,16 @@ int tls_client_connect_start(struct tls_client_ctx_t *ctx,
 
 void tls_client_close(struct tls_client_ctx_t *ctx)
 {
-	if (ctx == NULL || ctx->session == NULL) {
+	if (ctx == NULL || ctx->tls_session == NULL) {
 		return;
 	}
 
+	assert(ctx->session);
+
 	if (ctx->handshake_state == TLS_HS_DONE) {
+		DEBUG_MSG("[tls client] closing tls connection to `%s`\n",
+			  kr_straddr(&ctx->session->peer.ip));
+		ctx->handshake_state = TLS_HS_CLOSING;
 		gnutls_bye(ctx->tls_session, GNUTLS_SHUT_RDWR);
 	}
 }
diff --git a/daemon/tls.h b/daemon/tls.h
index 45ffcb142cc286ae85790bc1e6a4f8d9e64e7fcc..7285b40482e0135ba4c8b55e0a6cbc690dd50abf 100644
--- a/daemon/tls.h
+++ b/daemon/tls.h
@@ -49,10 +49,10 @@ struct qr_task;
 
 /* gnutls_record_recv and gnutls_record_send */
 struct tls_ctx_t {
-	gnutls_session_t session;
+	gnutls_session_t tls_session;
 	bool handshake_done;
 
-	uv_stream_t *handle;
+	struct session *session;
 
 	/* for reading from the network */
 	const uint8_t *buf;
@@ -68,19 +68,39 @@ typedef enum tls_client_hs_state {
 	TLS_HS_NOT_STARTED = 0,
 	TLS_HS_IN_PROGRESS,
 	TLS_HS_DONE,
+	TLS_HS_CLOSING,
 	TLS_HS_LAST
 } tls_client_hs_state_t;
 
 typedef int (*tls_handshake_cb) (struct session *session, int status);
 
+struct tls_client_ctx_t {
+	gnutls_session_t tls_session;
+	tls_client_hs_state_t handshake_state;
+
+	struct session *session;
+	tls_handshake_cb handshake_cb;
+	const uint8_t *buf;
+	ssize_t nread;
+	ssize_t consumed;
+	uint8_t recv_buf[4096];
+	const struct tls_client_paramlist_entry *params;
+	struct worker_ctx *worker;
+	struct qr_task *task;
+};
+
 /*! Create an empty TLS context in query context */
 struct tls_ctx_t* tls_new(struct worker_ctx *worker);
 
-/*! Close a TLS context */
+/*! Close a TLS context (call gnutls_bye()) */
+void tls_close(struct tls_ctx_t *tls);
+
+/*! Release a TLS context */
 void tls_free(struct tls_ctx_t* tls);
 
 /*! Push new data to TLS context for sending */
-int tls_push(struct qr_task *task, uv_handle_t* handle, knot_pkt_t * pkt);
+int tls_push(struct qr_task *task, uv_handle_t* handle, knot_pkt_t * pkt,
+	     bool server_side);
 
 /*! Unwrap incoming data from a TLS stream and pass them to TCP session.
  * @return the number of newly-completed requests (>=0) or an error code
@@ -115,7 +135,8 @@ int tls_client_params_set(map_t *tls_client_paramlist,
 int tls_client_params_free(map_t *tls_client_paramlist);
 
 /*! Allocate new client TLS context */
-struct tls_client_ctx_t *tls_client_ctx_new(const struct tls_client_paramlist_entry *entry);
+struct tls_client_ctx_t *tls_client_ctx_new(const struct tls_client_paramlist_entry *entry,
+					    struct worker_ctx *worker);
 
 int tls_client_process(struct worker_ctx *worker, uv_stream_t *handle,
 		       const uint8_t *buf, ssize_t nread);
diff --git a/daemon/worker.c b/daemon/worker.c
index e35524b1471e6ef75bb7ef81dbf62cb373195e9f..df73a5babe5e680523488a309ddb29d853d167a4 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -369,6 +369,10 @@ static void session_close(struct session *session)
 		if (session->tls_client_ctx) {
 			tls_client_close(session->tls_client_ctx);
 		}
+		if (session->tls_ctx) {
+			tls_close(session->tls_ctx);
+		}
+
 		session->timeout.data = session;
 		uv_close((uv_handle_t *)&session->timeout, on_session_timer_close);
 	}
@@ -870,7 +874,7 @@ static void on_send(uv_udp_send_t *req, int status)
 	iorequest_release(worker, req);
 }
 
-void on_write(uv_write_t *req, int status)
+static void on_task_write(uv_write_t *req, int status)
 {
 	uv_handle_t *handle = (uv_handle_t *)(req->handle);
 	uv_loop_t *loop = handle->loop;
@@ -882,10 +886,21 @@ void on_write(uv_write_t *req, int status)
 	iorequest_release(worker, req);
 }
 
+static void on_nontask_write(uv_write_t *req, int status)
+{
+	uv_handle_t *handle = (uv_handle_t *)(req->handle);
+	uv_loop_t *loop = handle->loop;
+	struct worker_ctx *worker = loop->data;
+	assert(worker == get_worker());
+	iorequest_release(worker, req);
+}
+
 ssize_t worker_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len)
 {
 	struct tls_ctx_t *t = (struct tls_ctx_t *)h;
-	const uv_buf_t ub = {(void *)buf, len};
+	const uv_buf_t uv_buf[1] = {
+		{ (char *)buf, len }
+	};
 
 	VERBOSE_MSG(NULL,"[tls] push %zu <%p>\n", len, h);
 	if (t == NULL) {
@@ -893,27 +908,69 @@ ssize_t worker_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len
 		return -1;
 	}
 
-	assert(t->handle);
-	assert(t->handle->type == UV_TCP);
+	assert(t->session && t->session->handle &&
+	       t->session->handle->type == UV_TCP);
+
+	struct worker_ctx *worker = t->worker;
+	assert(worker);
 
-	if (!t->handshake_done) {
-		int ret = uv_try_write(t->handle, &ub, 1);
-		if (ret > 0) {
-			return (ssize_t) ret;
+	void *ioreq = worker_iohandle_borrow(worker);
+	if (!ioreq) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	uv_write_t *write_req = (uv_write_t *)ioreq;
+
+	struct qr_task *task = t->task;
+	uv_write_cb write_cb = on_task_write;
+	if (t->handshake_done) {
+		assert(task);
+	} else {
+		task = NULL;
+		write_cb = on_nontask_write;
+	}
+
+	write_req->data = task;
+
+	ssize_t ret = -1;
+	int res = uv_write(write_req, (uv_stream_t *)t->session->handle, uv_buf, 1, write_cb);
+	if (res == 0) {
+		if (task) {
+			qr_task_ref(task); /* Pending ioreq on current task */
 		}
-		if (ret == UV_EAGAIN) {
-			errno = EAGAIN;
-		} else {
-			kr_log_error("[tls] uv_try_write: %s\n", uv_strerror(ret));
-			errno = EIO;
+		if (worker->too_many_open &&
+		    worker->stats.rconcurrent <
+			worker->rconcurrent_highwatermark - 10) {
+			worker->too_many_open = false;
 		}
+		ret = len;
+	} else {
+		VERBOSE_MSG(NULL,"[tls] uv_write: %s\n", uv_strerror(res));
+		iorequest_release(worker, ioreq);
+		errno = EIO;
+		/* TODO ret == UV_EMFILE */
+	}
+	return ret;
+}
+
+ssize_t worker_gnutls_client_push(gnutls_transport_ptr_t h, const void *buf, size_t len)
+{
+	struct tls_client_ctx_t *t = (struct tls_client_ctx_t *)h;
+	const uv_buf_t uv_buf[1] = {
+		{ (char *)buf, len }
+	};
+
+	VERBOSE_MSG(NULL,"[tls client] push %zu <%p>\n", len, h);
+	if (t == NULL) {
+		errno = EFAULT;
 		return -1;
 	}
+	assert(t->session && t->session->handle &&
+	       t->session->handle->type == UV_TCP);
 
 	struct worker_ctx *worker = t->worker;
-	struct qr_task *task = t->task;
-
-	assert(worker && task);
+	assert(worker);
 
 	void *ioreq = worker_iohandle_borrow(worker);
 	if (!ioreq) {
@@ -922,16 +979,24 @@ ssize_t worker_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len
 	}
 
 	uv_write_t *write_req = (uv_write_t *)ioreq;
-	uv_buf_t uv_buf[1] = {
-		{ (char *)buf, len }
-	};
+
+	struct qr_task *task = t->task;
+	uv_write_cb write_cb = on_task_write;
+	if (t->handshake_state == TLS_HS_DONE) {
+		assert(task);
+	} else {
+		task = NULL;
+		write_cb = on_nontask_write;
+	}
 
 	write_req->data = task;
 
 	ssize_t ret = -1;
-	int res = uv_write(write_req, t->handle, uv_buf, 1, &on_write);
+	int res = uv_write(write_req, (uv_stream_t *)t->session->handle, uv_buf, 1, write_cb);
 	if (res == 0) {
-		qr_task_ref(task); /* Pending ioreq on current task */
+		if (task) {
+			qr_task_ref(task); /* Pending ioreq on current task */
+		}
 		if (worker->too_many_open &&
 		    worker->stats.rconcurrent <
 			worker->rconcurrent_highwatermark - 10) {
@@ -939,7 +1004,7 @@ ssize_t worker_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len
 		}
 		ret = len;
 	} else {
-		VERBOSE_MSG(NULL,"[tls] uv_write: %s\n", uv_strerror(res));
+		VERBOSE_MSG(NULL,"[tls_client] uv_write: %s\n", uv_strerror(res));
 		iorequest_release(worker, ioreq);
 		errno = EIO;
 		/* TODO ret == UV_EMFILE */
@@ -958,18 +1023,14 @@ static int qr_task_send(struct qr_task *task, uv_handle_t *handle, struct sockad
 	assert(session->closing == false);
 	if (session->has_tls) {
 		struct kr_request *req = &task->ctx->req;
-		int ret = kr_ok();
-		if (!session->outgoing) {
-			ret = tls_push(task, handle, pkt);
-		} else {
-			ret = kr_resolve_checkout(req, NULL, addr,
-					          SOCK_STREAM, pkt);
+		if (session->outgoing) {
+			int ret = kr_resolve_checkout(req, NULL, addr,
+						      SOCK_STREAM, pkt);
 			if (ret != kr_ok()) {
 				return ret;
 			}
-			ret = tls_client_push(task, handle, pkt);
 		}
-		return ret;
+		return tls_push(task, handle, pkt, !session->outgoing);
 	}
 
 	int ret = 0;
@@ -1015,7 +1076,7 @@ static int qr_task_send(struct qr_task *task, uv_handle_t *handle, struct sockad
 			{ (char *)pkt->wire, pkt->size }
 		};
 		write_req->data = task;
-		ret = uv_write(write_req, (uv_stream_t *)handle, buf, 2, &on_write);
+		ret = uv_write(write_req, (uv_stream_t *)handle, buf, 2, &on_task_write);
 	} else {
 		assert(false);
 	}
@@ -1698,7 +1759,7 @@ static int qr_task_step(struct qr_task *task,
 			struct tls_client_paramlist_entry *entry = map_get(&net->tls_client_params, key);
 			if (entry) {
 				assert(session->tls_client_ctx == NULL);
-				struct tls_client_ctx_t *tls_ctx = tls_client_ctx_new(entry);
+				struct tls_client_ctx_t *tls_ctx = tls_client_ctx_new(entry, worker);
 				if (!tls_ctx) {
 					session_del_tasks(session, task);
 					session_del_waiting(session, task);
diff --git a/daemon/worker.h b/daemon/worker.h
index cac2a624423814a7ba4caba27584704c7907b83b..1a8161ac5103cadbd7a60efc49a01ee8c23e091e 100644
--- a/daemon/worker.h
+++ b/daemon/worker.h
@@ -92,6 +92,8 @@ void worker_iohandle_release(struct worker_ctx *worker, void *h);
 
 ssize_t worker_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len);
 
+ssize_t worker_gnutls_client_push(gnutls_transport_ptr_t h, const void *buf, size_t len);
+
 /** @cond internal */
 
 /** Number of request within timeout window. */