fixed GH #1212: Lost WebSocket Frames after Client Websocket Handshake is complete

Conflicts:
	Net/include/Poco/Net/HTTPServerRequestImpl.h
This commit is contained in:
Guenter Obiltschnig 2017-01-12 15:59:15 +01:00
parent c58b5b7e69
commit 5ac7959b11
6 changed files with 66 additions and 7 deletions

View File

@ -83,6 +83,12 @@ public:
StreamSocket detachSocket();
/// Returns the underlying socket after detaching
/// it from the server session.
HTTPServerSession& session();
/// Returns the underlying HTTPServerSession.
protected:
static const std::string EXPECT;
private:
HTTPServerResponseImpl& _response;
@ -129,6 +135,12 @@ inline HTTPServerResponse& HTTPServerRequestImpl::response() const
}
inline HTTPServerSession& HTTPServerRequestImpl::session()
{
return _session;
}
} } // namespace Poco::Net

View File

@ -25,6 +25,7 @@
#include "Poco/Timespan.h"
#include "Poco/Exception.h"
#include "Poco/Any.h"
#include "Poco/Buffer.h"
#include <ios>
@ -103,6 +104,14 @@ public:
StreamSocket& socket();
/// Returns a reference to the underlying socket.
void drainBuffer(Poco::Buffer<char>& buffer);
/// Copies all bytes remaining in the internal buffer to the
/// given Poco::Buffer, resizing it as necessary.
///
/// This is usually used together with detachSocket() to
/// obtain any data already read from the socket, but not
/// yet processed.
protected:
HTTPSession();

View File

@ -23,19 +23,23 @@
#include "Poco/Net/StreamSocketImpl.h"
#include "Poco/Buffer.h"
#include "Poco/Random.h"
#include "Poco/Buffer.h"
namespace Poco {
namespace Net {
class HTTPSession;
class Net_API WebSocketImpl: public StreamSocketImpl
/// This class implements a WebSocket, according
/// to the WebSocket protocol described in RFC 6455.
{
public:
WebSocketImpl(StreamSocketImpl* pStreamSocketImpl, bool mustMaskPayload);
/// Creates a StreamSocketImpl using the given native socket.
WebSocketImpl(StreamSocketImpl* pStreamSocketImpl, HTTPSession& session, bool mustMaskPayload);
/// Creates a WebSocketImpl.
// StreamSocketImpl
virtual int sendBytes(const void* buffer, int length, int flags);
@ -88,12 +92,15 @@ protected:
int receivePayload(char *buffer, int payloadLength, char mask[4], bool useMask);
int receiveNBytes(void* buffer, int bytes);
int receiveSomeBytes(char* buffer, int bytes);
virtual ~WebSocketImpl();
private:
WebSocketImpl();
StreamSocketImpl* _pStreamSocketImpl;
Poco::Buffer<char> _buffer;
int _bufferOffset;
int _frameFlags;
bool _mustMaskPayload;
Poco::Random _rnd;

View File

@ -252,4 +252,11 @@ void HTTPSession::attachSessionData(const Poco::Any& data)
}
void HTTPSession::drainBuffer(Poco::Buffer<char>& buffer)
{
buffer.assign(_pCurrent, static_cast<std::size_t>(_pEnd - _pCurrent));
_pCurrent = _pEnd;
}
} } // namespace Poco::Net

View File

@ -19,6 +19,7 @@
#include "Poco/Net/HTTPServerRequestImpl.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Net/HTTPClientSession.h"
#include "Poco/Net/HTTPServerSession.h"
#include "Poco/Net/NetException.h"
#include "Poco/MemoryStream.h"
#include "Poco/NullStream.h"
@ -144,7 +145,9 @@ WebSocketImpl* WebSocket::accept(HTTPServerRequest& request, HTTPServerResponse&
response.set("Sec-WebSocket-Accept", computeAccept(key));
response.setContentLength(0);
response.send().flush();
return new WebSocketImpl(static_cast<StreamSocketImpl*>(static_cast<HTTPServerRequestImpl&>(request).detachSocket().impl()), false);
HTTPServerRequestImpl& requestImpl = static_cast<HTTPServerRequestImpl&>(request);
return new WebSocketImpl(static_cast<StreamSocketImpl*>(requestImpl.detachSocket().impl()), requestImpl.session(), false);
}
else throw WebSocketException("No WebSocket handshake", WS_ERR_NO_HANDSHAKE);
}
@ -212,7 +215,7 @@ WebSocketImpl* WebSocket::completeHandshake(HTTPClientSession& cs, HTTPResponse&
std::string accept = response.get("Sec-WebSocket-Accept", "");
if (accept != computeAccept(key))
throw WebSocketException("Invalid or missing Sec-WebSocket-Accept header in handshake response", WS_ERR_HANDSHAKE_ACCEPT);
return new WebSocketImpl(static_cast<StreamSocketImpl*>(cs.detachSocket().impl()), true);
return new WebSocketImpl(static_cast<StreamSocketImpl*>(cs.detachSocket().impl()), cs, true);
}

View File

@ -17,6 +17,7 @@
#include "Poco/Net/WebSocketImpl.h"
#include "Poco/Net/NetException.h"
#include "Poco/Net/WebSocket.h"
#include "Poco/Net/HTTPSession.h"
#include "Poco/Buffer.h"
#include "Poco/BinaryWriter.h"
#include "Poco/BinaryReader.h"
@ -29,14 +30,17 @@ namespace Poco {
namespace Net {
WebSocketImpl::WebSocketImpl(StreamSocketImpl* pStreamSocketImpl, bool mustMaskPayload):
WebSocketImpl::WebSocketImpl(StreamSocketImpl* pStreamSocketImpl, HTTPSession& session, bool mustMaskPayload):
StreamSocketImpl(pStreamSocketImpl->sockfd()),
_pStreamSocketImpl(pStreamSocketImpl),
_buffer(0),
_bufferOffset(0),
_frameFlags(0),
_mustMaskPayload(mustMaskPayload)
{
poco_check_ptr(pStreamSocketImpl);
_pStreamSocketImpl->duplicate();
session.drainBuffer(_buffer);
}
@ -211,12 +215,12 @@ int WebSocketImpl::receiveBytes(Poco::Buffer<char>& buffer, int)
int WebSocketImpl::receiveNBytes(void* buffer, int bytes)
{
int received = _pStreamSocketImpl->receiveBytes(reinterpret_cast<char*>(buffer), bytes);
int received = receiveSomeBytes(reinterpret_cast<char*>(buffer), bytes);
if (received > 0)
{
while (received < bytes)
{
int n = _pStreamSocketImpl->receiveBytes(reinterpret_cast<char*>(buffer) + received, bytes - received);
int n = receiveSomeBytes(reinterpret_cast<char*>(buffer) + received, bytes - received);
if (n > 0)
received += n;
else
@ -227,6 +231,23 @@ int WebSocketImpl::receiveNBytes(void* buffer, int bytes)
}
int WebSocketImpl::receiveSomeBytes(char* buffer, int bytes)
{
int n = _buffer.size() - _bufferOffset;
if (n > 0)
{
if (bytes < n) n = bytes;
std::memcpy(buffer, _buffer.begin() + _bufferOffset, n);
_bufferOffset += n;
return n;
}
else
{
return _pStreamSocketImpl->receiveBytes(buffer, bytes);
}
}
SocketImpl* WebSocketImpl::acceptConnection(SocketAddress& clientAddr)
{
throw Poco::InvalidAccessException("Cannot acceptConnection() on a WebSocketImpl");