]> git.sesse.net Git - cubemap/blobdiff - server.cpp
Fix timeout behavior with persistent connections.
[cubemap] / server.cpp
index 40f3b48afe71425cfa6b0e9f00bdcdeb0a73aa55..78273b00eb7f3b9b49938c376eba4aeb92fe4300 100644 (file)
@@ -1,6 +1,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <pthread.h>
@@ -209,16 +210,7 @@ void Server::add_client(int sock, Acceptor *acceptor)
        assert(inserted.second == true);  // Should not already exist.
        Client *client_ptr = &inserted.first->second;
 
-       // Connection timestamps must be nondecreasing. I can't find any guarantee
-       // that even the monotonic clock can't go backwards by a small amount
-       // (think switching between CPUs with non-synchronized TSCs), so if
-       // this actually should happen, we hack around it by fudging
-       // connect_time.
-       if (!clients_ordered_by_connect_time.empty() &&
-           is_earlier(client_ptr->connect_time, clients_ordered_by_connect_time.back().first)) {
-               client_ptr->connect_time = clients_ordered_by_connect_time.back().first;
-       }
-       clients_ordered_by_connect_time.push(make_pair(client_ptr->connect_time, sock));
+       start_client_timeout_timer(client_ptr);
 
        // Start listening on data from this socket.
        epoll_event ev;
@@ -301,6 +293,24 @@ void Server::add_client_from_serialized(const ClientProto &client, const vector<
        }
 }
 
+void Server::start_client_timeout_timer(Client *client)
+{
+       // Connection timestamps must be nondecreasing. I can't find any guarantee
+       // that even the monotonic clock can't go backwards by a small amount
+       // (think switching between CPUs with non-synchronized TSCs), so if
+       // this actually should happen, we hack around it by fudging
+       // connect_time.
+       if (clock_gettime(CLOCK_MONOTONIC_COARSE, &client->connect_time) == -1) {
+               log_perror("clock_gettime(CLOCK_MONOTONIC_COARSE)");
+       } else {
+               if (!clients_ordered_by_connect_time.empty() &&
+                   is_earlier(client->connect_time, clients_ordered_by_connect_time.back().first)) {
+                       client->connect_time = clients_ordered_by_connect_time.back().first;
+               }
+               clients_ordered_by_connect_time.push(make_pair(client->connect_time, client->sock));
+       }
+}
+
 int Server::lookup_stream_by_url(const string &url) const
 {
        const auto stream_url_it = stream_url_map.find(url);
@@ -748,6 +758,22 @@ sending_data_again:
        }
 }
 
+namespace {
+
+void flush_pending_data(int sock)
+{
+       // Flush pending data, which would otherwise wait for the 200ms TCP_CORK timer
+       // to elapsed; does not cancel out TCP_CORK (since that still takes priority),
+       // but does a one-off flush.
+       int one = 1;
+       if (setsockopt(sock, SOL_TCP, TCP_NODELAY, &one, sizeof(one)) == -1) {
+               log_perror("setsockopt(TCP_NODELAY)");
+               // Can still continue.
+       }
+}
+
+}  // namespace
+
 bool Server::send_pending_tls_data(Client *client)
 {
        // See if there's data from the TLS library to write.
@@ -780,9 +806,15 @@ send_data_again:
                return true;
        }
        if (ret > 0 && size_t(ret) == client->tls_data_left_to_send) {
-               // All data has been sent, so we don't need to go to sleep.
+               // All data has been sent, so we don't need to go to sleep
+               // (although we are likely to do so immediately afterwards,
+               // due to lack of client data).
                tls_buffer_clear(client->tls_context);
                client->tls_data_to_send = nullptr;
+
+               // Flush the data we just wrote, since the client probably
+               // is waiting for it.
+               flush_pending_data(client->sock);
                return false;
        }
 
@@ -911,8 +943,7 @@ int Server::parse_request(Client *client)
        }
 
        // Parse the headers, for logging purposes.
-       // TODO: Case-insensitivity.
-       unordered_multimap<string, string> headers = extract_headers(lines, client->remote_addr);
+       HTTPHeaderMultimap headers = extract_headers(lines, client->remote_addr);
        const auto referer_it = headers.find("Referer");
        if (referer_it != headers.end()) {
                client->referer = referer_it->second;
@@ -1291,13 +1322,7 @@ bool Server::more_requests(Client *client)
        // Log to access_log.
        access_log->write(client->get_stats());
 
-       // Flush pending data; does not cancel out TCP_CORK (since that still takes priority),
-       // but does a one-off flush.
-       int one = 1;
-       if (setsockopt(client->sock, SOL_TCP, TCP_NODELAY, &one, sizeof(one)) == -1) {
-               log_perror("setsockopt(TCP_NODELAY)");
-               // Can still continue.
-       }
+       flush_pending_data(client->sock);
 
        // Switch states and reset the parsers. We don't reset statistics.
        client->state = Client::READING_REQUEST;
@@ -1307,6 +1332,7 @@ bool Server::more_requests(Client *client)
        client->header_or_short_response_holder.clear();
        client->header_or_short_response_ref.reset();
        client->header_or_short_response_bytes_sent = 0;
+       start_client_timeout_timer(client);
 
        change_epoll_events(client, EPOLLIN | EPOLLET | EPOLLRDHUP);  // No TLS handshake, so no EPOLLOUT needed.