Revamp of EOF handling in all network connections, pipes and other
[u/mdw/putty] / windows / winhandl.c
index c0e8232..286f79a 100644 (file)
@@ -250,6 +250,7 @@ struct handle_output {
      * Data only ever read or written by the main thread.
      */
     bufchain queued_data;             /* data still waiting to be written */
+    enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
 
     /*
      * Callback function called when the backlog in the bufchain
@@ -320,6 +321,11 @@ static void handle_try_output(struct handle_output *ctx)
        ctx->len = sendlen;
        SetEvent(ctx->ev_from_main);
        ctx->busy = TRUE;
+    } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 &&
+               ctx->outgoingeof == EOF_PENDING) {
+        CloseHandle(ctx->h);
+        ctx->h = INVALID_HANDLE_VALUE;
+        ctx->outgoingeof = EOF_SENT;
     }
 }
 
@@ -408,6 +414,7 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
     h->u.o.done = FALSE;
     h->u.o.privdata = privdata;
     bufchain_init(&h->u.o.queued_data);
+    h->u.o.outgoingeof = EOF_NO;
     h->u.o.sentdata = sentdata;
     h->u.o.flags = flags;
 
@@ -424,11 +431,28 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
 int handle_write(struct handle *h, const void *data, int len)
 {
     assert(h->output);
+    assert(h->u.o.outgoingeof == EOF_NO);
     bufchain_add(&h->u.o.queued_data, data, len);
     handle_try_output(&h->u.o);
     return bufchain_size(&h->u.o.queued_data);
 }
 
+void handle_write_eof(struct handle *h)
+{
+    /*
+     * This function is called when we want to proactively send an
+     * end-of-file notification on the handle. We can only do this by
+     * actually closing the handle - so never call this on a
+     * bidirectional handle if we're still interested in its incoming
+     * direction!
+     */
+    assert(h->output);
+    if (!h->u.o.outgoingeof == EOF_NO) {
+        h->u.o.outgoingeof = EOF_PENDING;
+        handle_try_output(&h->u.o);
+    }
+}
+
 HANDLE *handle_get_events(int *nevents)
 {
     HANDLE *ret;