Stiffstream / restinio

Cross-platform, efficient, customizable, and robust asynchronous HTTP(S)/WebSocket server C++ library with the right balance between performance and ease of use
Other
1.15k stars 93 forks source link

Missing socket::shutdown before socket::close in the server shutdown procedure. #77

Closed deadem closed 4 years ago

deadem commented 4 years ago

I've try to process only one request and then shutdown.

Here the example based on restinio 0.4.2 (using VS2015)

When WORDS_COUNT variable is relatively small (e.q. 10) server send full body content before shutown. But if I make it greater than 10000, server closes socket with pending data. I think there is socket::shutdown() missed before call the acceptor::close() function.

#include <cstdint>
#include <ostream>
#define ASIO_STANDALONE
#define FMT_HEADER_ONLY
#include <restinio/common_types.hpp>
#include <restinio/all.hpp>

std::string content()
{
  const static int WORDS_COUNT = 10;  // Change to 10000+ will cause an incomplete body

  std::string text;
  for (auto i = 0; i < WORDS_COUNT; ++i)
  {
    text += "Some text. ";
  }
  return text;
}

class Body : public std::string
{
public:
  Body(std::function<void()> callback) : std::string(content()), m_callback(callback)
  {
  }

  ~Body()
  {
    m_callback();
  }

private:
  std::function<void()> m_callback;
};

int main()
{
  using Traits = restinio::default_single_thread_traits_t;
  using Server = restinio::http_server_t<Traits>;

  std::unique_ptr<Server> server;

  auto shutdown = [&server]() {
    server->close_sync();
    server->io_context().stop();
  };

  server = std::make_unique<restinio::http_server_t<Traits>>(restinio::own_io_context(),
    restinio::run_on_this_thread_settings_t<Traits>().port(8888).address("0.0.0.0").request_handler([shutdown](auto req) {
      req->create_response().set_body(std::make_shared<Body>([shutdown]() { shutdown(); })).done();
      return restinio::request_accepted();
    }));

  server->open_sync();
  server->io_context().run();

  return 0;
}
eao197 commented 4 years ago

Thanks for reporting this issue! I'll address it as soon as the current work on issue #76 will be completed.

eao197 commented 4 years ago

@deadem Can you run cl -? from the VC++ command prompt and tell us your compiler version? Mine is 19.00.24210, and I had had to patch RESTinio-0.4.9.1 code to compile the example.

deadem commented 4 years ago

Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86 I'm using restinio 0.4.2, but problem exists in newer versions too

eao197 commented 4 years ago

Can you modify your example that way and try it:

  server = std::make_unique<restinio::http_server_t<Traits>>(restinio::own_io_context(),
    restinio::run_on_this_thread_settings_t<Traits>().port(8888).address("0.0.0.0").request_handler([shutdown](auto req) {
      auto & resp = req->create_response();
      resp.header().should_keep_alive( false );
      resp.set_body(std::make_shared<Body>([shutdown]() { shutdown(); })).done();
      return restinio::request_accepted();
    }));
deadem commented 4 years ago

Sure. That line fix the problem: resp.header().should_keep_alive(false); But how to get same result with keep-alive enabled?

deadem commented 4 years ago

Ow! I can disable keep-alive in the last request only. Thanks! ;)

eao197 commented 4 years ago

I can disable keep-alive in the last request only.

Yes. Because it seems that there is no sense to call shutdown and close methods for underlying socket in the destructor of connection object in your scenario. io_context will be stopped at that moment and calls to shutdown/close won't have an effect. So you have to disable keep-alive for the last request manually.