+ switch (status) {
+ case RP_OUT_OF_SPACE:
+ log(WARNING, "[%s] Client sent overlong request!", client->remote_addr.c_str());
+ close_client(client);
+ return;
+ case RP_NOT_FINISHED_YET:
+ // OK, we don't have the entire header yet. Fine; we'll get it later.
+ // See if there's more data for us.
+ goto read_request_again;
+ case RP_EXTRA_DATA:
+ log(WARNING, "[%s] Junk data after request!", client->remote_addr.c_str());
+ close_client(client);
+ return;
+ case RP_FINISHED:
+ break;
+ }
+
+ assert(status == RP_FINISHED);
+
+ int error_code = parse_request(client);
+ if (error_code == 200) {
+ construct_header(client);
+ } else {
+ construct_error(client, error_code);
+ }
+
+ // We've changed states, so fall through.
+ assert(client->state == Client::SENDING_ERROR ||
+ client->state == Client::SENDING_HEADER);
+ }
+ case Client::SENDING_ERROR:
+ case Client::SENDING_HEADER: {
+sending_header_or_error_again:
+ int ret;
+ do {
+ ret = write(client->sock,
+ client->header_or_error.data() + client->header_or_error_bytes_sent,
+ client->header_or_error.size() - client->header_or_error_bytes_sent);
+ } while (ret == -1 && errno == EINTR);
+
+ 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.
+ log_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->stream_pos = client->stream->bytes_received;
+ client->stream->put_client_to_sleep(client);
+ return;
+ }
+ case Client::SENDING_DATA: {
+sending_data_again:
+ // 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->bytes_received - client->stream_pos;
+ if (bytes_to_send == 0) {
+ return;
+ }
+ if (bytes_to_send > stream->backlog_size) {
+ log(WARNING, "[%s] Client lost %lld bytes, maybe too slow connection",
+ client->remote_addr.c_str(),
+ (long long int)(bytes_to_send - stream->backlog_size));
+ client->stream_pos = stream->bytes_received - stream->backlog_size;
+ client->bytes_lost += bytes_to_send - stream->backlog_size;
+ ++client->num_loss_events;
+ bytes_to_send = stream->backlog_size;
+ }
+
+ // See if we need to split across the circular buffer.
+ bool more_data = false;
+ if ((client->stream_pos % stream->backlog_size) + bytes_to_send > stream->backlog_size) {
+ bytes_to_send = stream->backlog_size - (client->stream_pos % stream->backlog_size);
+ more_data = true;
+ }
+
+ ssize_t ret;
+ do {
+ loff_t offset = client->stream_pos % stream->backlog_size;
+ ret = sendfile(client->sock, stream->data_fd, &offset, 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.
+ log_perror("sendfile");
+ close_client(client);
+ return;
+ }
+ client->stream_pos += ret;
+ client->bytes_sent += ret;
+
+ if (client->stream_pos == stream->bytes_received) {
+ // 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 if (more_data && size_t(ret) == bytes_to_send) {
+ goto sending_data_again;
+ }
+ break;
+ }
+ default:
+ assert(false);
+ }
+}
+
+int Server::parse_request(Client *client)