CServerCollection servers; // Hixie76 server { OutputEx(_T("Hixie76 server on port: ") + ToString(port)); const CFullAddress address( commandLine.Server(), port++, addressPreference); servers.AddServer(new CHixieSocketServer( address, listenBacklog, pool, socketAllocator, bufferAllocator)); } // HyBi server { OutputEx(_T("HyBi server on port: ") + ToString(port)); const CFullAddress address( commandLine.Server(), port++, addressPreference); servers.AddServer(new CHyBiSocketServer( address, listenBacklog, pool, socketAllocator, bufferAllocator, connectionLimiter)); } // Auto detect server { OutputEx(_T("Auto Detect server on port: ") + ToString(port)); const CFullAddress address( commandLine.Server(), port++, addressPreference); servers.AddServer(new CSocketServer( address, listenBacklog, pool, socketAllocator, bufferAllocator, connectionLimiter)); } servers.Start();
/// Implement IAcceptWebSocketConnections virtual JetByteTools::WebSocket::ConnectionEstablishmentResult OnConnectionHandshake( JetByteTools::Win32::IIndexedOpaqueUserData &userData, const std::string &uri, const bool secure, const JetByteTools::WebSocket::CHeaders &requestHeaders, JetByteTools::WebSocket::CHeaders &responseHeaders); /// Implement Hixie76 IWebSocketServer virtual void OnConnectionEstablished( JetByteTools::WebSocket::Hixie76::IWebSocket &socket, const std::string &uri, const bool secure); virtual void OnOutboundConnectionEstablished( IWebSocket &socket, const CHeaders &responseHeaders); virtual void OnData( JetByteTools::WebSocket::Hixie76::IWebSocket &socket, JetByteTools::IO::IBuffer &buffer, const JetByteTools::WebSocket::MessageStatus status); virtual void OnData( JetByteTools::WebSocket::Hixie76::IWebSocket &socket, const JetByteTools::Win32::_tstring &text, const JetByteTools::WebSocket::MessageStatus status); virtual void OnClientClose( JetByteTools::WebSocket::Hixie76::IWebSocket &socket);
void CHixieSocketServer::OnConnectionEstablished( IStreamSocket &socket, const IAddress & /*address*/) { Output(_T("OnConnectionEstablished")); CProtocolHandler *pHandler = m_protocolHandlerAllocator.Allocate(*this, socket, CProtocolHandler::AccumulateCompleteMessage); socket.SetUserPointer(m_protocolHandlerIndex, pHandler); pHandler->AcceptHandshake(); } void CHixieSocketServer::OnReadCompleted( IStreamSocket &socket, IBuffer &buffer) { try { IProtocolHandler *pHandler = reinterpret_cast<IProtocolHandler *>(socket.GetUserPointer(m_protocolHandlerIndex)); pHandler->OnDataReceived(buffer); } catch(const CException &e) { OutputEx(_T("ReadCompleted - Exception - ") + e.GetDetails()); socket.AbortConnection(); } catch(...) { OutputEx(_T("ReadCompleted - Unexpected exception")); socket.AbortConnection(); } }
ConnectionEstablishmentResult CHixieSocketServer::OnConnectionHandshake( IIndexedOpaqueUserData & /*userData*/, const string &uri, const bool secure, const CHeaders &requestHeaders, CHeaders &responseHeaders) { Output(_T("OnConnectionHandshake - : ") + CStringConverter::AtoT(uri)); size_t index = 0; string header; Output(_T("Request headers")); while (requestHeaders.GetRawHeader(index, header)) { Output(CStringConverter::AtoT(header)); } Output(_T("Protocol Version: Hixie76")); const string protocol = secure ? "wss" : "ws"; responseHeaders.Add("Sec-WebSocket-Location: " + protocol + "://" + requestHeaders.GetValue("host") + uri); if (requestHeaders.Contains("sec-websocket-protocol")) { throw CException(_T("CHixieSocketServer::OnConnectionEstablished()"), _T("Unknown protocol")); } static const string webSocketOriginHeader = "sec-websocket-origin"; static const string originHeader = "origin"; if (requestHeaders.Contains(webSocketOriginHeader)) { responseHeaders.Add("Sec-WebSocket-Origin: " + requestHeaders.GetValue(webSocketOriginHeader)); } else if (requestHeaders.Contains(originHeader)) { responseHeaders.Add("Sec-WebSocket-Origin: " + requestHeaders.GetValue(originHeader)); } if (requestHeaders.Contains(originHeader)) { responseHeaders.Add("Origin: " + requestHeaders.GetValue(originHeader)); } return ::ConnectionEstablished; }
void CHixieSocketServer::OnConnectionEstablished( IWebSocket &socket, const string &uri, const bool /*secure*/) { Output(_T("OnConnectionEstablished - Hixie76: ") + CStringConverter::AtoT(uri)); socket.TryRead(); }
void CHixieSocketServer::OnData( IWebSocket &socket, IBuffer &buffer, const MessageStatus status) { // For this to be called you need to specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction if (status != MessageStatusComplete) { throw CException( _T("CHixieSocketServer::OnData()"), _T("Unexpected: message larger than buffer size")); } // For this to be called you need to NOT specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction DEBUG_ONLY(Output(_T("OnMessage - ") + ToString(buffer.GetUsed()) + _T(" bytes\r\n") + DumpData(buffer.GetMemory(), buffer.GetUsed()))); // if we wanted to convert the buffer to text we could do this //const _tstring text = CStringConverter::UTF8toT(buffer.GetMemory(), buffer.GetUsed()); //DEBUG_ONLY(Output(_T("OnMessage - \"") + text+ _T("\""))); socket.TryWriteText(buffer); socket.TryRead(); } void CHixieSocketServer::OnData( IWebSocket &socket, const _tstring &text, const MessageStatus status) { // For this to be called you need to specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction if (status != MessageStatusComplete) { throw CException( _T("CHixieSocketServer::OnData()"), _T("Unexpected: message larger than buffer size")); } DEBUG_ONLY(Output(_T("OnData - \"") + text+ _T("\""))); socket.TryWriteText(text); socket.TryRead(); }
OnData()
interface supplies a JetByteTools::WebSocket::MessageStatus enum which can tell you when you have a complete message. In this simple server we only handle complete messages. See the later server examples for details of how to deal with messages that are larger than your buffer size. void CHixieSocketServer::OnClientClose( IWebSocket &socket) { Output(_T("OnClientClose")); socket.Close(); }
/// Implement IAcceptWebSocketConnections virtual JetByteTools::WebSocket::ConnectionEstablishmentResult OnConnectionHandshake( JetByteTools::Win32::IIndexedOpaqueUserData &userData, const std::string &uri, const bool secure, const JetByteTools::WebSocket::CHeaders &requestHeaders, JetByteTools::WebSocket::CHeaders &responseHeaders); // Implement HyBi IWebHyBiSocketServer virtual void OnOutboundConnectionEstablished( JetByteTools::WebSocket::HyBi::IWebSocket &socket, const JetByteTools::WebSocket::CHeaders &responseHeaders); virtual void OnConnectionEstablished( JetByteTools::WebSocket::HyBi::IWebSocket &socket, const std::string &uri, const bool secure); virtual void OnData( JetByteTools::WebSocket::HyBi::IWebSocket &socket, const JetByteTools::Win32::_tstring &text, const JetByteTools::WebSocket::MessageStatus status, const __int64 messageBytesOutstanding); virtual void OnData( JetByteTools::WebSocket::HyBi::IWebSocket &socket, JetByteTools::IO::IBuffer &buffer, const JetByteTools::WebSocket::MessageType type, const JetByteTools::WebSocket::MessageStatus status, const __int64 messageBytesOutstanding); virtual void OnPingResponse( JetByteTools::WebSocket::HyBi::IWebSocket &socket, const BYTE *pData, const BYTE length); virtual void OnClientClose( JetByteTools::WebSocket::HyBi::IWebSocket &socket, const WORD status, const JetByteTools::Win32::_tstring &text);
void CHyBiSocketServer::OnConnectionEstablished( IStreamSocket &socket, const IAddress & /*address*/) { Output(_T("OnConnectionEstablished")); CProtocolHandler *pHandler = m_protocolHandlerAllocator.Allocate(*this, socket, CProtocolHandler::AccumulateCompleteMessage); socket.SetUserPointer(m_protocolHandlerIndex, pHandler); pHandler->AcceptHandshake(); }
void CHyBiSocketServer::OnData( IWebSocket &socket, const _tstring &text, const MessageStatus status, const __int64 messageBytesOutstanding) { // For this to be called you need to specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction if (status != MessageStatusComplete) { throw CException( _T("CHyBiSocketServer::OnData()"), _T("Unexpected: message larger than buffer size")); } if (messageBytesOutstanding != 0) { throw CException( _T("CHyBiSocketServer::OnData()"), _T("Unexpected: message larger than buffer size: ") + ToString(messageBytesOutstanding) + _T(" outstanding")); } DEBUG_ONLY(Output(_T("OnMessage - \"") + text+ _T("\""))); socket.TryWriteText(text); socket.TryRead(); } void CHyBiSocketServer::OnData( IWebSocket &socket, IBuffer &buffer, const MessageType type, const MessageStatus status, const __int64 messageBytesOutstanding) { // For this to be called you need to NOT specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction if (type == MessageTypeText) { if (status != MessageStatusComplete) { throw CException( _T("CHyBiSocketServer::OnData()"), _T("Unexpected: message larger than buffer size")); } if (messageBytesOutstanding != 0) { throw CException( _T("CHyBiSocketServer::OnData()"), _T("Unexpected: message larger than buffer size: ") + ToString(messageBytesOutstanding) + _T(" outstanding")); } DEBUG_ONLY(Output(_T("OnMessage - ") + ToString(buffer.GetUsed()) + _T(" bytes\r\n") + DumpData(buffer.GetMemory(), buffer.GetUsed()))); // if we wanted to convert the buffer to text we could do this //const _tstring text = CStringConverter::UTF8toT(buffer.GetMemory(), buffer.GetUsed()); //DEBUG_ONLY(Output(_T("OnMessage - \"") + text+ _T("\""))); socket.TryWriteText(buffer); socket.TryRead(); } else { throw CException( _T("CHyBiSocketServer::OnData()"), _T("Unexpected: we don't handle binary frames")); } }
messageBytesOutstanding
will be 0 when the message is actually complete or else contain the number of bytes still pending. Since we only deal in complete messages which fit in a buffer in this example we check for JetByteTools::WebSocket::MessageStatusComplete and messageBytesOutstanding == 0
to determine if we have a complete message. void CHyBiSocketServer::OnPingResponse( IWebSocket & /*socket*/, const BYTE * /*pData*/, const BYTE /*length*/) { throw CException( _T("CHyBiSocketServer::OnPingResponse()"), _T("Unexpected: we don't send pings")); }
void CHyBiSocketServer::OnClientClose( IWebSocket &socket, const WORD status, const _tstring &reason) { Output(_T("OnClientClose:") + ToString(status) + _T(": ") + reason); socket.Close(CloseStatusNormal); }
OnConnectionClosed()
may cause sockets to be leaked and connections to stay in memory. void CSocketServer::OnConnectionReset( IStreamSocket &socket, const DWORD /*lastError*/) { IProtocolHandler *pHandler = static_cast<IProtocolHandler *>(socket.GetUserPointer(m_protocolHandlerIndex)); pHandler->OnConnectionClosed(); } void CSocketServer::OnConnectionClosure( IStreamSocket &socket, const ConnectionClosureReason /*reason*/) { IProtocolHandler *pHandler = reinterpret_cast<IProtocolHandler *>(socket.GetUserPointer(m_protocolHandlerIndex)); pHandler->OnConnectionClosed(); }
CSocketServer::CSocketServer( const IFullAddress &address, const ListenBacklog listenBacklog, IIOPool &pool, IAllocateSequencedStreamSockets &socketAllocator, IAllocateBuffers &bufferAllocator, ILimitConnections &connectionLimiter) : CStreamSocketServerEx(address, listenBacklog, *this, pool, socketAllocator, bufferAllocator, NoZeroByteRead, connectionLimiter), m_protocolHandlerIndex(socketAllocator.RequestUserDataSlot(_T("m_protocolHandlerIndex"))), m_nextBufferIndex(bufferAllocator.RequestUserDataSlot(_T("m_nextBufferIndex"))), m_bufferAllocator(bufferAllocator), m_protocolHandlerAllocator(*static_cast<JetByteTools::WebSocket::HyBi::IWebSocketServer *>(this), bufferAllocator) { m_protocolHandlerAllocator.SupportHixie76( *this, CHixie76ProtocolHandler::AccumulateCompleteMessage); m_protocolHandlerAllocator.SupportHyBiVersions( CAutoDetectProtocolHandlerAllocator::HyBiProtocolAllVersions, *this, CHyBi08ProtocolHandler::AccumulateCompleteMessage); }
void CSocketServer::OnMessage( IWebSocket &socket, const _tstring &text) { // For this to be called you need to specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction DEBUG_ONLY(Output(_T("OnMessage - \"") + text+ _T("\""))); socket.TryWriteText(text); socket.TryRead(); } void CSocketServer::OnMessage( IWebSocket &socket, IBuffer &buffer) { // For this to be called you need to NOT specify CProtocolHandler::DispatchTextAsStrings in the flags that are passed // during construction DEBUG_ONLY(Output(_T("OnMessage - ") + ToString(buffer.GetUsed()) + _T(" bytes\r\n") + DumpData(buffer.GetMemory(), buffer.GetUsed()))); // if we wanted to convert the buffer to text we could do this //const _tstring text = CStringConverter::UTF8toT(buffer.GetMemory(), buffer.GetUsed()); //DEBUG_ONLY(Output(_T("OnMessage - \"") + text+ _T("\""))); socket.TryWriteText(buffer); socket.TryRead(); }