diff options
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj | 2 | ||||
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj.filters | 6 | ||||
-rw-r--r-- | xbmc/network/websocket/Makefile | 1 | ||||
-rw-r--r-- | xbmc/network/websocket/WebSocketV13.cpp | 166 | ||||
-rw-r--r-- | xbmc/network/websocket/WebSocketV13.h | 32 |
5 files changed, 207 insertions, 0 deletions
diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index ff80a5776c..648e776d6c 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -675,6 +675,7 @@ <ClCompile Include="..\..\xbmc\network\WebServer.cpp" /> <ClCompile Include="..\..\xbmc\network\websocket\WebSocket.cpp" /> <ClCompile Include="..\..\xbmc\network\websocket\WebSocketManager.cpp" /> + <ClCompile Include="..\..\xbmc\network\websocket\WebSocketV13.cpp" /> <ClCompile Include="..\..\xbmc\network\websocket\WebSocketV8.cpp" /> <ClCompile Include="..\..\xbmc\network\windows\NetworkWin32.cpp" /> <ClCompile Include="..\..\xbmc\network\windows\ZeroconfWIN.cpp" /> @@ -764,6 +765,7 @@ <ClInclude Include="..\..\xbmc\interfaces\python\xbmcmodule\pythreadstate.h" /> <ClInclude Include="..\..\xbmc\network\websocket\WebSocket.h" /> <ClInclude Include="..\..\xbmc\network\websocket\WebSocketManager.h" /> + <ClInclude Include="..\..\xbmc\network\websocket\WebSocketV13.h" /> <ClInclude Include="..\..\xbmc\network\websocket\WebSocketV8.h" /> <ClInclude Include="..\..\xbmc\threads\platform\win\Implementation.cpp" /> <ClCompile Include="..\..\xbmc\threads\SystemClock.cpp" /> diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index a0b45b91eb..3cc187c9df 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -2586,6 +2586,9 @@ <ClCompile Include="..\..\xbmc\network\websocket\WebSocketManager.cpp"> <Filter>network\websocket</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\network\websocket\WebSocketV13.cpp"> + <Filter>network\websocket</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\xbmc\win32\pch.h"> @@ -5193,6 +5196,9 @@ <ClInclude Include="..\..\xbmc\network\websocket\WebSocketManager.h"> <Filter>network\websocket</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\network\websocket\WebSocketV13.h"> + <Filter>network\websocket</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc"> diff --git a/xbmc/network/websocket/Makefile b/xbmc/network/websocket/Makefile index f3005eda2d..f3d9d41270 100644 --- a/xbmc/network/websocket/Makefile +++ b/xbmc/network/websocket/Makefile @@ -1,6 +1,7 @@ SRCS=WebSocket.cpp \ WebSocketManager.cpp \ WebSocketV8.cpp \ + WebSocketV13.cpp \ LIB=websocket.a diff --git a/xbmc/network/websocket/WebSocketV13.cpp b/xbmc/network/websocket/WebSocketV13.cpp new file mode 100644 index 0000000000..aa084839db --- /dev/null +++ b/xbmc/network/websocket/WebSocketV13.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <string> +#include <sstream> +#include <boost/uuid/sha1.hpp> + +#include "WebSocketV13.h" +#include "WebSocket.h" +#include "utils/Base64.h" +#include "utils/CharsetConverter.h" +#include "utils/HttpParser.h" +#include "utils/HttpResponse.h" +#include "utils/log.h" +#include "utils/StringUtils.h" + +#define WS_HTTP_METHOD "GET" +#define WS_HTTP_TAG "HTTP/" + +#define WS_HEADER_UPGRADE "Upgrade" +#define WS_HEADER_UPGRADE_LC "upgrade" +#define WS_HEADER_CONNECTION "Connection" +#define WS_HEADER_CONNECTION_LC "connection" + +#define WS_HEADER_KEY_LC "sec-websocket-key" // "Sec-WebSocket-Key" +#define WS_HEADER_ACCEPT "Sec-WebSocket-Accept" +#define WS_HEADER_PROTOCOL "Sec-WebSocket-Protocol" +#define WS_HEADER_PROTOCOL_LC "sec-websocket-protocol" // "Sec-WebSocket-Protocol" + +#define WS_PROTOCOL_JSONRPC "jsonrpc.xbmc.org" +#define WS_HEADER_UPGRADE_VALUE "websocket" + +using namespace std; + +bool CWebSocketV13::Handshake(const char* data, size_t length, std::string &response) +{ + string strHeader(data, length); + const char *value; + HttpParser header; + if (header.addBytes(data, length) != HttpParser::Done) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: incomplete handshake received"); + return false; + } + + // The request must be GET + value = header.getMethod(); + if (value == NULL || strnicmp(value, WS_HTTP_METHOD, strlen(WS_HTTP_METHOD)) != 0) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: invalid HTTP method received (GET expected)"); + return false; + } + + // The request must be HTTP/1.1 or higher + int pos; + if ((pos = strHeader.find(WS_HTTP_TAG)) == string::npos) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: invalid handshake received"); + return false; + } + + pos += strlen(WS_HTTP_TAG); + istringstream converter(strHeader.substr(pos, strHeader.find_first_of(" \r\n\t", pos) - pos)); + float fVersion; + converter >> fVersion; + + if (fVersion < 1.1f) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: invalid HTTP version %f (1.1 or higher expected)", fVersion); + return false; + } + + string websocketKey, websocketProtocol; + // There must be a "Host" header + value = header.getValue("host"); + if (value == NULL || strlen(value) == 0) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: \"Host\" header missing"); + return true; + } + + // There must be a "Upgrade" header with the value "websocket" + value = header.getValue(WS_HEADER_UPGRADE_LC); + if (value == NULL || strcmp(value, WS_HEADER_UPGRADE_VALUE) != 0) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: invalid \"%s\" received", WS_HEADER_UPGRADE); + return true; + } + + // There must be a "Connection" header with the value "Upgrade" + value = header.getValue(WS_HEADER_CONNECTION_LC); + if (value == NULL || strcmp(value, WS_HEADER_UPGRADE) != 0) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: invalid \"%s\" received", WS_HEADER_CONNECTION_LC); + return true; + } + + // There must be a base64 encoded 16 byte (=> 24 byte as base62) "Sec-WebSocket-Key" header + value = header.getValue(WS_HEADER_KEY_LC); + if (value == NULL || (websocketKey = value).size() != 24) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: invalid \"Sec-WebSocket-Key\" received"); + return true; + } + + // There might be a "Sec-WebSocket-Protocol" header + value = header.getValue(WS_HEADER_PROTOCOL_LC); + if (value && strlen(value) > 0) + { + CStdStringArray protocols; + StringUtils::SplitString(value, ",", protocols); + for (unsigned int index = 0; index < protocols.size(); index++) + { + if (protocols.at(index).Trim().Equals(WS_PROTOCOL_JSONRPC)) + { + websocketProtocol = WS_PROTOCOL_JSONRPC; + break; + } + } + } + + CHttpResponse httpResponse(HTTP::Get, HTTP::SwitchingProtocols, HTTP::Version1_1); + httpResponse.AddHeader(WS_HEADER_UPGRADE, WS_HEADER_UPGRADE_VALUE); + httpResponse.AddHeader(WS_HEADER_CONNECTION, WS_HEADER_UPGRADE); + std::string responseKey = calculateKey(websocketKey); + httpResponse.AddHeader(WS_HEADER_ACCEPT, responseKey); + if (!websocketProtocol.empty()) + httpResponse.AddHeader(WS_HEADER_PROTOCOL, websocketProtocol); + + char *responseBuffer; + int responseLength = httpResponse.Create(responseBuffer); + response = std::string(responseBuffer, responseLength); + + m_state = WebSocketStateConnected; + + return true; +} + +const CWebSocketFrame* CWebSocketV13::Close(WebSocketCloseReason reason /* = WebSocketCloseNormal */, const std::string &message /* = "" */) +{ + if (m_state == WebSocketStateNotConnected || m_state == WebSocketStateHandshaking || m_state == WebSocketStateClosed) + { + CLog::Log(LOGINFO, "WebSocket [RFC6455]: Cannot send a closing handshake if no connection has been established"); + return NULL; + } + + return close(reason, message); +} diff --git a/xbmc/network/websocket/WebSocketV13.h b/xbmc/network/websocket/WebSocketV13.h new file mode 100644 index 0000000000..254d6486e2 --- /dev/null +++ b/xbmc/network/websocket/WebSocketV13.h @@ -0,0 +1,32 @@ +#pragma once +/* + * Copyright (C) 2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "WebSocketV8.h" + +class CWebSocketV13 : public CWebSocketV8 +{ +public: + CWebSocketV13() { m_version = 13; } + + virtual bool Handshake(const char* data, size_t length, std::string &response); + virtual const CWebSocketFrame* Close(WebSocketCloseReason reason = WebSocketCloseNormal, const std::string &message = ""); +}; |