#include <assert.h>
#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pthread.h>
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;
}
}
+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);
}
}
+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.
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;
}
}
// 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;
// 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;
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.