Commit 1eed0c75 authored by hyperxor's avatar hyperxor

Fixes in server timeouts and more tests

parent b62a4b37
......@@ -548,7 +548,7 @@ namespace Pistache
class BodyStep : public Step
{
public:
static constexpr auto Id = Meta::Hash::fnv1a("Headers");
static constexpr auto Id = Meta::Hash::fnv1a("Body");
explicit BodyStep(Message* message_)
: Step(message_)
......@@ -611,7 +611,7 @@ namespace Pistache
virtual ~ParserBase() = default;
virtual bool feed(const char* data, size_t len);
bool feed(const char* data, size_t len);
virtual void reset();
State parse();
......@@ -635,7 +635,6 @@ namespace Pistache
public:
explicit ParserImpl(size_t maxDataSize);
bool feed(const char* data, size_t len) override;
void reset() override;
std::chrono::steady_clock::time_point time() const
......
......@@ -1053,12 +1053,6 @@ namespace Pistache
time_ = std::chrono::steady_clock::now();
}
bool Private::ParserImpl<Http::Request>::feed(const char* data, size_t len)
{
time_ = std::chrono::steady_clock::now();
return ParserBase::feed(data, len);
}
Private::ParserImpl<Http::Response>::ParserImpl(size_t maxDataSize)
: ParserBase(maxDataSize)
, response()
......
......@@ -112,12 +112,12 @@ namespace Pistache
auto elapsed = now - time;
auto* step = parser->step();
if (step->id() == Private::RequestLineStep::Id)
if (step->id() == Private::RequestLineStep::Id || step->id() == Private::HeadersStep::Id)
{
if (elapsed > headerTimeout_ || elapsed > bodyTimeout_)
idlePeers.push_back(peer);
}
else if (step->id() == Private::HeadersStep::Id)
else if (step->id() == Private::BodyStep::Id)
{
if (elapsed > bodyTimeout_)
idlePeers.push_back(peer);
......
......@@ -163,6 +163,8 @@ struct AddressEchoHandler : public Http::Handler
}
};
constexpr const char* ExpectedResponseLine = "HTTP/1.1 408 Request Timeout";
struct PingHandler : public Http::Handler
{
HTTP_PROTOTYPE(PingHandler)
......@@ -534,17 +536,18 @@ TEST(http_server_test, response_size_captured)
ASSERT_EQ(rcode, Http::Code::Ok);
}
TEST(http_server_test, client_request_header_timeout_raises_http_408)
TEST(http_server_test, client_request_timeout_on_only_connect_raises_http_408)
{
Pistache::Address address("localhost", Pistache::Port(0));
auto timeout = std::chrono::seconds(2);
const auto headerTimeout = std::chrono::seconds(2);
Http::Endpoint server(address);
auto flags = Tcp::Options::ReuseAddr;
auto opts = Http::Endpoint::options()
.flags(flags)
.headerTimeout(timeout);
.headerTimeout(headerTimeout);
server.init(opts);
server.setHandler(Http::make_handler<PingHandler>());
server.serveThreaded();
......@@ -553,24 +556,109 @@ TEST(http_server_test, client_request_header_timeout_raises_http_408)
auto addr = "localhost:" + port.toString();
LOGGER("test", "Server address: " << addr)
char recvBuf[1024];
std::memset(recvBuf, 0, sizeof(recvBuf));
TcpClient client;
EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError();
char recvBuf[1024] = {
0,
};
size_t bytes;
EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError();
EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine)));
server.shutdown();
}
TEST(http_server_test, client_request_timeout_on_delay_in_header_send_raises_http_408)
{
Pistache::Address address("localhost", Pistache::Port(0));
const auto headerTimeout = std::chrono::seconds(1);
Http::Endpoint server(address);
auto flags = Tcp::Options::ReuseAddr;
auto opts = Http::Endpoint::options()
.flags(flags)
.headerTimeout(headerTimeout);
server.init(opts);
server.setHandler(Http::make_handler<PingHandler>());
server.serveThreaded();
auto port = server.getPort();
auto addr = "localhost:" + port.toString();
LOGGER("test", "Server address: " << addr);
const std::string reqStr = "GET /ping HTTP/1.1\r\n";
const std::string headerStr = "Host: localhost\r\nUser-Agent: test\r\n";
TcpClient client;
EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError();
EXPECT_TRUE(client.send(reqStr)) << client.lastError();
std::this_thread::sleep_for(headerTimeout / 2);
EXPECT_TRUE(client.send(headerStr)) << client.lastError();
char recvBuf[1024] = {
0,
};
size_t bytes;
EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError();
EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine)));
server.shutdown();
}
TEST(http_server_test, client_request_timeout_on_delay_in_request_line_send_raises_http_408)
{
Pistache::Address address("localhost", Pistache::Port(0));
const auto headerTimeout = std::chrono::seconds(2);
Http::Endpoint server(address);
auto flags = Tcp::Options::ReuseAddr;
auto opts = Http::Endpoint::options()
.flags(flags)
.headerTimeout(headerTimeout);
server.init(opts);
server.setHandler(Http::make_handler<PingHandler>());
server.serveThreaded();
auto port = server.getPort();
auto addr = "localhost:" + port.toString();
LOGGER("test", "Server address: " << addr);
const std::string reqStr { "GET /ping HTTP/1.1\r\n" };
TcpClient client;
ASSERT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError();
EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError();
for (size_t i = 0; i < reqStr.size(); ++i)
{
if (!client.send(reqStr.substr(i, 1)))
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(300));
}
EXPECT_EQ(client.lastErrno(), EPIPE) << "Errno: " << client.lastErrno();
ASSERT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError();
char recvBuf[1024] = {
0,
};
size_t bytes;
EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError();
EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine)));
server.shutdown();
}
TEST(http_server_test, client_request_body_timeout_raises_http_408)
TEST(http_server_test, client_request_timeout_on_delay_in_body_send_raises_http_408)
{
Pistache::Address address("localhost", Pistache::Port(0));
auto headerTimeout = std::chrono::seconds(1);
auto bodyTimeout = std::chrono::seconds(1);
const auto headerTimeout = std::chrono::seconds(1);
const auto bodyTimeout = std::chrono::seconds(2);
Http::Endpoint server(address);
auto flags = Tcp::Options::ReuseAddr;
......@@ -587,24 +675,62 @@ TEST(http_server_test, client_request_body_timeout_raises_http_408)
auto addr = "localhost:" + port.toString();
LOGGER("test", "Server address: " << addr);
std::string reqStr = "GET /ping HTTP/1.1\r\n";
std::string headerStr = "Host: localhost\r\nUser-Agent: test\r\n";
const std::string reqStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 32\r\n\r\nabc";
char recvBuf[1024];
std::memset(recvBuf, 0, sizeof(recvBuf));
TcpClient client;
EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError();
EXPECT_TRUE(client.send(reqStr)) << client.lastError();
char recvBuf[1024] = {
0,
};
size_t bytes;
EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError();
EXPECT_EQ(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine)));
server.shutdown();
}
TEST(http_server_test, client_request_no_timeout)
{
Pistache::Address address("localhost", Pistache::Port(0));
const auto headerTimeout = std::chrono::seconds(2);
const auto bodyTimeout = std::chrono::seconds(4);
Http::Endpoint server(address);
auto flags = Tcp::Options::ReuseAddr;
auto opts = Http::Endpoint::options()
.flags(flags)
.headerTimeout(headerTimeout)
.bodyTimeout(bodyTimeout);
server.init(opts);
server.setHandler(Http::make_handler<PingHandler>());
server.serveThreaded();
auto port = server.getPort();
auto addr = "localhost:" + port.toString();
LOGGER("test", "Server address: " << addr);
const std::string headerStr = "POST /ping HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/plain\r\nContent-Length: 8\r\n\r\n";
const std::string bodyStr = "abcdefgh\r\n\r\n";
TcpClient client;
ASSERT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError();
ASSERT_TRUE(client.send(reqStr)) << client.lastError();
EXPECT_TRUE(client.connect(Pistache::Address("localhost", port))) << client.lastError();
std::this_thread::sleep_for(headerTimeout / 2);
ASSERT_TRUE(client.send(headerStr)) << client.lastError();
EXPECT_TRUE(client.send(headerStr)) << client.lastError();
static constexpr const char* ExpectedResponseLine = "HTTP/1.1 408 Request Timeout";
std::this_thread::sleep_for(bodyTimeout / 2);
EXPECT_TRUE(client.send(bodyStr)) << client.lastError();
ASSERT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError();
ASSERT_TRUE(!strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine)));
char recvBuf[1024] = {
0,
};
size_t bytes;
EXPECT_TRUE(client.receive(recvBuf, sizeof(recvBuf), &bytes, std::chrono::seconds(5))) << client.lastError();
EXPECT_NE(0, strncmp(recvBuf, ExpectedResponseLine, strlen(ExpectedResponseLine)));
server.shutdown();
}
......
......@@ -18,6 +18,7 @@ namespace Pistache
if (ret < 0) \
{ \
lastError_ = strerror(errno); \
lastErrno_ = errno; \
return false; \
} \
} while (0)
......@@ -66,9 +67,28 @@ namespace Pistache
return send(data.c_str(), data.size());
}
bool send(const void* data, size_t size)
bool send(const char* data, size_t len)
{
CLIENT_TRY(::send(fd_, data, size, 0));
size_t total = 0;
while (total < len)
{
ssize_t n = ::send(fd_, data + total, len - total, MSG_NOSIGNAL);
if (n == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
else
{
CLIENT_TRY(n);
}
}
else
{
total += static_cast<size_t>(n);
}
}
return true;
}
......@@ -115,9 +135,15 @@ namespace Pistache
return lastError_;
}
int lastErrno() const
{
return lastErrno_;
}
private:
int fd_;
int fd_ = -1;
std::string lastError_;
int lastErrno_ = 0;
};
#undef CLIENT_TRY
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment