#include <assert.h>
#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pthread.h>
}
}
+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;
}
if (!client->close_after_response) {
assert(client->stream_pos_end != Client::STREAM_POS_NO_END);
- // We've already sent a Content-length, so we can't just skip data.
+ // We've already sent a Content-Length, so we can't just skip data.
// Close the connection immediately and hope the other side
// is able to figure out that there was an error and it needs to skip.
client->close_after_response = true;
}
// 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;
string response = stream->http_header;
if (client->stream_pos == Client::STREAM_POS_HEADER_ONLY) {
char buf[64];
- snprintf(buf, sizeof(buf), "Content-length: %zu\r\n", stream->stream_header.size());
+ snprintf(buf, sizeof(buf), "Content-Length: %zu\r\n", stream->stream_header.size());
response.append(buf);
} else if (client->stream_pos_end != Client::STREAM_POS_NO_END) {
char buf[64];
- snprintf(buf, sizeof(buf), "Content-length: %" PRIu64 "\r\n", client->stream_pos_end - client->stream_pos);
+ snprintf(buf, sizeof(buf), "Content-Length: %" PRIu64 "\r\n", client->stream_pos_end - client->stream_pos);
response.append(buf);
}
if (client->http_11) {
if (stream->encoding == Stream::STREAM_ENCODING_RAW) {
response.append("\r\n");
} else if (stream->encoding == Stream::STREAM_ENCODING_METACUBE) {
- response.append("Content-encoding: metacube\r\n\r\n");
+ response.append("Content-Encoding: metacube\r\n\r\n");
if (!stream->stream_header.empty()) {
metacube2_block_header hdr;
memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync));
char error[256];
if (client->http_11 && client->close_after_response) {
snprintf(error, sizeof(error),
- "HTTP/1.1 %d Error\r\nContent-type: text/plain\r\nConnection: close\r\n\r\nSomething went wrong. Sorry.\r\n",
+ "HTTP/1.1 %d Error\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nSomething went wrong. Sorry.\r\n",
error_code);
} else {
snprintf(error, sizeof(error),
- "HTTP/1.%d %d Error\r\nContent-type: text/plain\r\nContent-length: 30\r\n\r\nSomething went wrong. Sorry.\r\n",
+ "HTTP/1.%d %d Error\r\nContent-Type: text/plain\r\nContent-Length: 30\r\n\r\nSomething went wrong. Sorry.\r\n",
client->http_11, error_code);
}
client->header_or_short_response_holder = error;
// 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;