diff --git a/webcc/client.cc b/webcc/client.cc index 4fe137c..77b281b 100644 --- a/webcc/client.cc +++ b/webcc/client.cc @@ -174,32 +174,49 @@ void Client::ReadResponse() { void Client::DoReadResponse() { boost::system::error_code ec = boost::asio::error::would_block; + std::size_t length = 0; - auto handler = [this, &ec](boost::system::error_code inner_ec, - std::size_t length) { + // The read handler. + auto handler = [&ec, &length](boost::system::error_code inner_ec, + std::size_t inner_length) { ec = inner_ec; + length = inner_length; + }; - LOG_VERB("Socket async read handler."); + // Read the first piece of data asynchronously so that the timer could also + // be async-waited. + socket_->AsyncReadSome(std::move(handler), &buffer_); - // Stop the deadline timer once the read has started (or failed). - CancelTimer(); + // Block until the asynchronous operation has completed. + do { + io_context_.run_one(); + } while (ec == boost::asio::error::would_block); + + // Now we have read the first piece of data. + // The left data will be read synchronously to void stack overflow because of + // too many recursive calls. - // TODO: Is it necessary to check `length == 0`? + // Stop the timer. + CancelTimer(); + + do { if (ec || length == 0) { + // For the first async-read, the error normally is caused by timeout. + // See OnTimer(). Close(); error_.Set(Error::kSocketReadError, "Socket read error"); LOG_ERRO("Socket read error (%s).", ec.message().c_str()); - return; + break; } LOG_INFO("Read data, length: %u.", length); - // Parse the response piece just read. + // Parse the piece of data just read. if (!response_parser_.Parse(buffer_.data(), length)) { Close(); error_.Set(Error::kParseError, "HTTP parse error"); - LOG_ERRO("Failed to parse HTTP response."); - return; + LOG_ERRO("Failed to parse the HTTP response."); + break; } if (response_parser_.finished()) { @@ -213,23 +230,15 @@ void Client::DoReadResponse() { Close(); } - LOG_INFO("Finished to read and parse HTTP response."); - // Stop reading. - return; + LOG_INFO("Finished to read the HTTP response."); + break; } - if (!closed_) { - DoReadResponse(); - } - }; - - socket_->AsyncReadSome(std::move(handler), &buffer_); + // Read next piece of data synchronously. + socket_->ReadSome(&buffer_, &length, &ec); - // Block until the asynchronous operation has completed. - do { - io_context_.run_one(); - } while (ec == boost::asio::error::would_block); + } while (true); } void Client::DoWaitTimer() { diff --git a/webcc/socket.cc b/webcc/socket.cc index 5f3e4c8..026ccb6 100644 --- a/webcc/socket.cc +++ b/webcc/socket.cc @@ -45,6 +45,12 @@ bool Socket::Write(const Payload& payload, boost::system::error_code* ec) { return !(*ec); } +bool Socket::ReadSome(std::vector* buffer, std::size_t* size, + boost::system::error_code* ec) { + *size = socket_.read_some(boost::asio::buffer(*buffer), *ec); + return (*size != 0 && !(*ec)); +} + void Socket::AsyncReadSome(ReadHandler&& handler, std::vector* buffer) { socket_.async_read_some(boost::asio::buffer(*buffer), std::move(handler)); } @@ -143,6 +149,12 @@ bool SslSocket::Write(const Payload& payload, boost::system::error_code* ec) { return !(*ec); } +bool SslSocket::ReadSome(std::vector* buffer, std::size_t* size, + boost::system::error_code* ec) { + *size = ssl_socket_.read_some(boost::asio::buffer(*buffer), *ec); + return (*size != 0 && !(*ec)); +} + void SslSocket::AsyncReadSome(ReadHandler&& handler, std::vector* buffer) { ssl_socket_.async_read_some(boost::asio::buffer(*buffer), std::move(handler)); diff --git a/webcc/socket.h b/webcc/socket.h index f9a0f73..0c628d2 100644 --- a/webcc/socket.h +++ b/webcc/socket.h @@ -30,6 +30,9 @@ public: virtual bool Write(const Payload& payload, boost::system::error_code* ec) = 0; + virtual bool ReadSome(std::vector* buffer, std::size_t* size, + boost::system::error_code* ec) = 0; + virtual void AsyncReadSome(ReadHandler&& handler, std::vector* buffer) = 0; @@ -46,6 +49,9 @@ public: bool Write(const Payload& payload, boost::system::error_code* ec) override; + bool ReadSome(std::vector* buffer, std::size_t* size, + boost::system::error_code* ec) override; + void AsyncReadSome(ReadHandler&& handler, std::vector* buffer) override; bool Close() override; @@ -67,6 +73,9 @@ public: bool Write(const Payload& payload, boost::system::error_code* ec) override; + bool ReadSome(std::vector* buffer, std::size_t* size, + boost::system::error_code* ec) override; + void AsyncReadSome(ReadHandler&& handler, std::vector* buffer) override; bool Close() override;