commit a5a99557dbce7e25efeaf31f37322748fed8da50 from: Omar Polo via: Thomas Adam date: Fri Jul 29 09:38:22 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 - 57d4848d969f00db854acc82cd5e920532a9f809 commit + a5a99557dbce7e25efeaf31f37322748fed8da50 blob - 3b6350631e42b62f4a9aa477c0a2aee5464fec6b blob + dbca0f24e4e64de735cdcf2fbc84ffabe5faa91c --- gotwebd/fcgi.c +++ gotwebd/fcgi.c @@ -353,7 +353,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; @@ -379,14 +380,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);