When a request handler accepting post data is too slow in consuming stdin,
uhttpd might deadlock with the master process stuck in a blocking write()
to the child and the child stuck with a blocking write() to the master.
Avoid this issue by putting the master side write end of the child pipe
into nonblocking mode right away and by raising the data_blocked flag
when attempts to write to the child yield EAGAIN.
Setting the flag ensures that client_poll_post_data() does not immediately
trigger a write attempt again, which effectively yields the master write
cycle so that the relay ustream has a chance to consume output of the
client process, thus solving the deadlock.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
struct client *cl = container_of(fd, struct client, dispatch.proc.wrfd);
client_poll_post_data(cl);
+ cl->dispatch.data_blocked = false;
}
static void proc_relay_write_cb(struct client *cl)
if (errno == EINTR)
continue;
- if (errno == EAGAIN || errno == EWOULDBLOCK)
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ cl->dispatch.data_blocked = true;
break;
+ }
/* consume all data */
ret = len;
proc->wrfd.fd = wfd[1];
uh_relay_open(cl, &proc->r, rfd[0], pid);
+ uloop_fd_add(&proc->wrfd, ULOOP_WRITE);
d->free = proc_free;
d->close_fds = proc_close_fds;