2016-12-15 78 views
0

提供MCVE将是很难,方案如下:升压ASIO async_read管连接关闭太早

  • 用C++编写与升压ASIO服务器提供了一些服务
  • 客户端写在C++ with boost asio请求服务

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

AFAIK当服务器连接关闭得太早时会发生这种情况。 所以,运行到GDB,我可以看到,这个问题是确实是从发送所述请求,向其中读取HTTP报头的第一线的async_read_until的ASYNC_WRITE过渡:

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++编写/升压ASIO我们只能拿到破管的连接,并没有数据之前获得实际的消息/错误
  • 只有当服务器离开连接打开时,我们是否会读到错误(401),但是这会破坏目的,因为现在连接仍处于打开状态。

我真的很感激任何提示或提示。我明白,没有代码很难提供帮助,所以我可以随时添加更多源代码。

编辑:

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

+2

服务器是否需要读取整个HTTP请求以确定并响应未授权?例如,也许可以仅从头部字段中确定授权,而不需要读取任何多部分/表单主体。在这种情况下,服务器可以在客户端完成写请求之前发送响应并关闭连接。 –

+0

@TannerSansbury不,它只需要标题值来回复。是的,这就是发生了什么,服务器在收到整个请求之前正在回复。我认为(是的,我知道......)服务器完成接收请求后,应答将发生,显然不是这种情况。非常感谢您的帮助! –

回答

3

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

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

6.5。故障和超时

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

发送消息主体的客户端应该在传输请求时监视网络连接是否有错误响应。如果客户端看到一个响应,表明服务器不希望收到消息正文并关闭连接,则客户端应该立即停止发送主体并关闭其连接侧。

在一个优美的连接关闭,服务器将关闭底层套接字之前发送到客户端的响应:

6.6。卸下

发送“close”连接选项的服务器必须在发送包含“close”的响应后启动关闭连接。 [...]

鉴于上述行为,有三种可能的情况。 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_write()同时发起async_read()操作。如果服务器指示HTTP连接正在关闭,则客户端将关闭其套接字的发送端。额外的状态处理可能不保证额外的复杂性

+0

非常感谢您的深入回复!我最终采取了第二种方法,但我想我会结合第一个以防万一。 –