提升asio async_read标头连接太早关闭

时间:2016-12-15 13:23:10

标签: c++ boost boost-asio

提供MCVE会很困难,方案如下:

  • 使用boost asio以c ++编写的服务器提供了一些服务
  • 使用boost asio请求服务以c ++编写的客户端

有自定义标题,大多数通信都是使用multipart / form完成的。 但是,在服务器返回401以进行未授权访问的情况下, 客户端收到损坏的管道(系统错误32)。

AFAIK当服务器连接过早关闭时会发生这种情况。 因此,运行到gdb,我可以看到问题确实是从发送请求的async_write到读取HTTP头的第一行的async_read_until的转换:

connect例程将请求从客户端发送到服务器:

    boost::asio::async_write(*socket_.get(),
                             request_,
                             boost::bind(&asio_handler<http_socket>::write_request, 
                                         this,
                                         boost::asio::placeholders::error,
                                         boost::asio::placeholders::bytes_transferred));

write_request回调,检查请求是否已发送正常,然后读取第一行(直到第一个换行符):

template <class T> 
void asio_handler<T>::write_request(const boost::system::error_code & err,
                                    const std::size_t bytes)
{       
    if (!err) {
        // read until first newline
        boost::asio::async_read_until(*socket_,
                                      buffer_,
                                      "\r\n",
                                      boost::bind(&asio_handler::read_status_line, 
                                                  this, 
                                                  boost::asio::placeholders::error,
                                                  boost::asio::placeholders::bytes_transferred));
    }
    else {
        end(err);       
    }
}

问题是始终使用损坏的管道调用end(err)(错误代码32)。据我所知,这意味着服务器关闭了连接。服务器确实关闭了连接,但只有 后才发送消息HTTP/1.1 401 Unauthorized

  • 使用curl和适当的请求,我们确实在服务器关闭连接之前得到了实际的消息/错误
  • 使用我们用C ++ / boost asio编写的客户端,我们只得到破坏的管道,没有数据
  • 只有当服务器离开连接时,我们才能到达读取错误的点(401),但是这样做会失败,因为现在连接处于打开状态。

我真的很感激任何提示或提示。我知道如果没有代码很难提供帮助,那么我可以随时添加更多源代码。

编辑:

如果我检查写入请求和阅读服务器回复之间的错误,那么我确实得到了实际的HTTP 401错误。然而,这似乎是违反直觉的,我不确定为什么会发生这种情况或是否应该发生。

1 个答案:

答案 0 :(得分:3)

根据HTTP规范允许观察到的行为。

客户端或服务器可以随时关闭套接字。在客户端完成传输请求之前,服务器可以提供响应并关闭连接。在编写正文时,建议客户端监视套接字是否有错误或关闭通知。来自RFC 7230, HTTP/1.1: Message Syntax and Routing Section 6.5. Failures and Timeouts

  

<强> 6.5。失败和超时

     
    

客户端,服务器或代理可以随时关闭传输连接。 [...]

         

发送邮件正文的客户端应该在传输请求时监视网络连接以获取错误响应。如果客户端看到一个响应,指示服务器不希望接收消息正文并且正在关闭连接,则客户端应该立即停止传输正文并关闭其连接端。

  

在正常连接关闭时,服务器将在关闭底层套接字之前向客户端发送响应:

  

<强> 6.6。拆除

     
    

发送&#34;关闭&#34;的服务器连接选项必须在发送包含&#34;关闭&#34;的响应后启动连接关闭[...]。 [...]

  

鉴于上述行为,有三种可能的情况。 async_write()操作以:

完成
  • 成功,表明请求已全部写完。客户端可能已收到或尚未收到HTTP响应
  • 错误,表示请求未完整写入。如果有可在套接字上读取的数据,则它可能包含服务器在连接终止之前发送的HTTP响应。 HTTP连接可能已正常终止
  • 错误,表示请求未完整写入。如果套接字上没有可读取的数据,则HTTP连接未正常终止

考虑:

  • 如果async_read()成功或有可供阅读的数据,则启动async_write()操作

    void write_request(
      const boost::system::error_code & error,
      const std::size_t bytes_transferred)
    {
      // The server may close the connection before the HTTP Request finished
      // writing.  In that case, the HTTP Response will be available on the
      // socket.  Only stop the call chain if an error occurred and no data is
      // available.
      if (error && !socket_->available()) 
      {
        return;
      }
    
      boost::asio::async_read_until(*socket_, buffer_, "\r\n", ...);
    

    }

  • 根据RFC 建议,在async_read()的同时启动async_write()操作。如果服务器指示HTTP连接正在关闭,则客户端将关闭其套接字的发送端。额外的状态处理可能无法保证额外的复杂性