Skip to content

Find a reliable way to read from socket till end of message #62

@MortezaBashsiz

Description

@MortezaBashsiz

Subjective

In an HTTPS call, the client detects the end of a message using Content-Length Header
The Content-Length header specifies the exact byte length of the HTTP message body. The client reads the response until it has received the specified number of bytes.
How it works:

  • The server includes a Content-Length header in the response.
  • The client reads the header, determines the size of the message body, and continues reading from the network until it has received that exact number of bytes.

We as a proxy server are not able to decrypt and detect the Content-Length header, so we need to find a way to detect when the message is over.

Current solution

I implemented a way to read till end of message like the following, which is not stable.
You can check the function TCPClient::doRead which is reading the socket in a loop and check the available bytes on socket. I also wait at the end of each round in the loop to give a pause for possible data over socket.
Both repeatWait and timeWait used in this function are configurable from config file

    for (auto i = 0; i <= config_->general().repeatWait; i++) {
      while (true) {
        if (!socket_.is_open()) {
          log_->write("[TCPClient doRead] Socket is not OPEN",
                      Log::Level::DEBUG);
          socket_.close();
          return;
        }
        if (socket_.available() == 0) break;
        boost::asio::read(socket_, readBuffer_,
                          boost::asio::transfer_exactly(1), error);
        if (error == boost::asio::error::eof) {
          log_->write("[TCPClient doRead] [EOF] Connection closed by peer.",
                      Log::Level::TRACE);
          socket_.close();
          return;  // Exit after closing the socket
        } else if (error) {
          log_->write(
              std::string("[TCPClient doRead] [error] ") + error.message(),
              Log::Level::ERROR);
          socket_.close();
          return;  // Exit after closing the socket
        }
      }
      timer.expires_from_now(
          boost::posix_time::milliseconds(config_->general().timeWait));
      timer.wait();
    }

Current Issues

We have two basic issues with the solution above

  1. This method is not reliable since the data may come with delay on socket, then our loop will end before reading all data from socket. So the parameter socket_.available() is not reliable in this situation

  2. This method will read from socket while there is something on socket to read and then pack all red data and send it to the nipoAgent. So imagine I want to download a file with size of 100Mb, then following steps will be happened

  • Client send http request to nipoAgent
  • nipoAgent send request to nipoServer
  • nipoServer send request to origin server
  • origin server start to write 100Mb on socket to nipoServer
  • nipoServer read all 100Mb from socket (which is in several packets or chunks)
  • nipoServer pack all 100Mb to a single response and send back to nipoAgent

This is not good and client needs to get the data chunk by chunk and not all once

Metadata

Metadata

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions