commit cb8b898671a36b65caec01bef948f24df2a3c3a8 from: Omar Polo date: Thu Jul 28 20:39:57 2022 UTC gotwebd: handle partial writes fcgi_send_response can end up trying to send big amount of data, in particular when serving blobs. if a write failure occurs, gotwebd and httpd go out of sync and "bad stuff happens". debugged with and ok tracey@ commit - 0d8d489a9ef3cda200d0e71337bc1b6d1c7bc03f commit + cb8b898671a36b65caec01bef948f24df2a3c3a8 blob - 6037bbba2a23ca1e59d0c1a0109c442fbe0595a5 blob + 2fc28dbf71ef0e110ace7352fe04fa4512d98e2d --- gotwebd/fcgi.c +++ gotwebd/fcgi.c @@ -352,7 +352,8 @@ fcgi_send_response(struct request *c, struct fcgi_resp { struct fcgi_record_header *header; struct timespec ts; - size_t padded_len; + size_t padded_len, off; + ssize_t nw; int err = 0, th = 2000; ts.tv_sec = 0; @@ -378,14 +379,27 @@ fcgi_send_response(struct request *c, struct fcgi_resp * disconnect. Let's at least try to write the data a few times before * giving up. */ - while ((write(c->fd, resp->data + resp->data_pos, - resp->data_len)) == -1) { - nanosleep(&ts, NULL); - err++; - if (err == th) { + for (off = 0; off < resp->data_len; off += nw) { + nw = write(c->fd, resp->data + off, resp->data_len - off); + if (nw == 0) { + c->sock->client_status = CLIENT_DISCONNECT; + break; + } + if (nw == -1) { + err++; + if (errno == EAGAIN && err < th) { + nw = 0; + nanosleep(&ts, NULL); + continue; + } + log_warn("write"); c->sock->client_status = CLIENT_DISCONNECT; break; } + + if (nw != resp->data_len - off) + log_debug("partial write: %zu vs %zu", nw, + resp->data_len - off); } free(resp);