+ if (ret == -1 && errno == EAGAIN) {
+ // We're out of socket space, so now we're at the “low edge” of epoll's
+ // edge triggering. epoll will tell us when there is more room, so for now,
+ // just return.
+ // This is postcondition #4.
+ return;
+ }
+
+ if (ret == -1) {
+ // Error! Postcondition #1.
+ perror("write");
+ close_client(client);
+ return;
+ }
+
+ client->header_or_error_bytes_sent += ret;
+ assert(client->header_or_error_bytes_sent <= client->header_or_error.size());
+
+ if (client->header_or_error_bytes_sent < client->header_or_error.size()) {
+ // We haven't sent all yet. Fine; go another round.
+ goto sending_header_or_error_again;
+ }
+
+ // We're done sending the header or error! Clear it to release some memory.
+ client->header_or_error.clear();
+
+ if (client->state == Client::SENDING_ERROR) {
+ // We're done sending the error, so now close.
+ // This is postcondition #1.
+ close_client(client);
+ return;
+ }
+
+ // Start sending from the end. In other words, we won't send any of the backlog,
+ // but we'll start sending immediately as we get data.
+ // This is postcondition #3.
+ client->state = Client::SENDING_DATA;
+ client->bytes_sent = client->stream->data_size;
+ client->stream->put_client_to_sleep(client);
+ return;
+ }
+ case Client::SENDING_DATA: {
+ // See if there's some data we've lost. Ideally, we should drop to a block boundary,
+ // but resync will be the mux's problem.
+ Stream *stream = client->stream;
+ size_t bytes_to_send = stream->data_size - client->bytes_sent;
+ if (bytes_to_send == 0) {
+ return;
+ }
+ if (bytes_to_send > BACKLOG_SIZE) {
+ fprintf(stderr, "WARNING: fd %d lost %lld bytes, maybe too slow connection\n",
+ client->sock,
+ (long long int)(bytes_to_send - BACKLOG_SIZE));
+ client->bytes_sent = stream->data_size - BACKLOG_SIZE;
+ bytes_to_send = BACKLOG_SIZE;
+ }
+
+ // See if we need to split across the circular buffer.
+ ssize_t ret;
+ if ((client->bytes_sent % BACKLOG_SIZE) + bytes_to_send > BACKLOG_SIZE) {
+ size_t bytes_first_part = BACKLOG_SIZE - (client->bytes_sent % BACKLOG_SIZE);
+
+ iovec iov[2];
+ iov[0].iov_base = const_cast<char *>(stream->data + (client->bytes_sent % BACKLOG_SIZE));
+ iov[0].iov_len = bytes_first_part;
+
+ iov[1].iov_base = const_cast<char *>(stream->data);
+ iov[1].iov_len = bytes_to_send - bytes_first_part;
+
+ do {
+ ret = writev(client->sock, iov, 2);
+ } while (ret == -1 && errno == EINTR);
+ } else {
+ do {
+ ret = write(client->sock,
+ stream->data + (client->bytes_sent % BACKLOG_SIZE),
+ bytes_to_send);
+ } while (ret == -1 && errno == EINTR);
+ }
+ if (ret == -1 && errno == EAGAIN) {
+ // We're out of socket space, so return; epoll will wake us up
+ // when there is more room.
+ // This is postcondition #4.
+ return;
+ }
+ if (ret == -1) {
+ // Error, close; postcondition #1.
+ perror("write/writev");
+ close_client(client);
+ return;
+ }
+ client->bytes_sent += ret;
+
+ if (client->bytes_sent == stream->data_size) {
+ // We don't have any more data for this client, so put it to sleep.
+ // This is postcondition #3.
+ stream->put_client_to_sleep(client);
+ } else {
+ // XXX: Do we need to go another round here to explicitly
+ // get the EAGAIN?