diff options
author | Stefan Saraev <stefansaraev@users.noreply.github.com> | 2016-05-17 21:07:38 +0300 |
---|---|---|
committer | Stefan Saraev <stefansaraev@users.noreply.github.com> | 2016-05-17 21:07:38 +0300 |
commit | 7d1fff681ad345e9930e714dc8b3707ea299adeb (patch) | |
tree | 0b9dbce14929e94b0f50594c39e7a69e37edc556 | |
parent | d1f3e54695f13b0919887b6fc8500c962269a5f6 (diff) | |
parent | e9f18d8240b680d409dc1653db5e3fdc9e3e9eb5 (diff) |
Merge pull request #9576 from Montellese/webserver_improvements
[network] improvements to the webserver implementation
20 files changed, 509 insertions, 374 deletions
diff --git a/Kodi.xcodeproj/project.pbxproj b/Kodi.xcodeproj/project.pbxproj index ce0e9a740d..e00a88798d 100644 --- a/Kodi.xcodeproj/project.pbxproj +++ b/Kodi.xcodeproj/project.pbxproj @@ -139,6 +139,8 @@ 228FA65E1C53F9D50023BBF0 /* InfoScanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 228FA65C1C53F9D50023BBF0 /* InfoScanner.cpp */; }; 2A7B2BDC1BD6F16600044BCD /* PVRSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */; }; 2A7B2BDD1BD6F16600044BCD /* PVRSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */; }; + 2AB491701CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4916E1CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp */; }; + 2AB491711CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2AB4916E1CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp */; }; 2AC7EB5A1C21F6BA00BDAA95 /* GUIWindowPVRTimerRules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2AC7EB561C21F6BA00BDAA95 /* GUIWindowPVRTimerRules.cpp */; }; 2AC7EB5B1C21F6BA00BDAA95 /* GUIWindowPVRTimersBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2AC7EB581C21F6BA00BDAA95 /* GUIWindowPVRTimersBase.cpp */; }; 2AC7EB5C1C2330B300BDAA95 /* GUIWindowPVRTimerRules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2AC7EB561C21F6BA00BDAA95 /* GUIWindowPVRTimerRules.cpp */; }; @@ -2580,6 +2582,8 @@ 228FA65C1C53F9D50023BBF0 /* InfoScanner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InfoScanner.cpp; sourceTree = "<group>"; }; 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRSettings.cpp; sourceTree = "<group>"; }; 2A7B2BDE1BD6F18B00044BCD /* PVRSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRSettings.h; sourceTree = "<group>"; }; + 2AB4916E1CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPRequestHandlerUtils.cpp; sourceTree = "<group>"; }; + 2AB4916F1CDDF1920004C263 /* HTTPRequestHandlerUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPRequestHandlerUtils.h; sourceTree = "<group>"; }; 2AC7EB561C21F6BA00BDAA95 /* GUIWindowPVRTimerRules.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRTimerRules.cpp; sourceTree = "<group>"; }; 2AC7EB571C21F6BA00BDAA95 /* GUIWindowPVRTimerRules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRTimerRules.h; sourceTree = "<group>"; }; 2AC7EB581C21F6BA00BDAA95 /* GUIWindowPVRTimersBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRTimersBase.cpp; sourceTree = "<group>"; }; @@ -7482,6 +7486,8 @@ DFCA6ABC152245CD000BFAAE /* HTTPJsonRpcHandler.h */, 395C29E11A98A15700EBC7AD /* HTTPPythonHandler.cpp */, 395C29E21A98A15700EBC7AD /* HTTPPythonHandler.h */, + 2AB4916E1CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp */, + 2AB4916F1CDDF1920004C263 /* HTTPRequestHandlerUtils.h */, DFCA6ABD152245CD000BFAAE /* HTTPVfsHandler.cpp */, DFCA6ABE152245CD000BFAAE /* HTTPVfsHandler.h */, DFCA6ABF152245CD000BFAAE /* HTTPWebinterfaceAddonsHandler.cpp */, @@ -10160,6 +10166,7 @@ 18B7C7CD1294222E009E7A26 /* GUIListItemLayout.cpp in Sources */, 18B7C7CE1294222E009E7A26 /* GUIListLabel.cpp in Sources */, 18B7C7CF1294222E009E7A26 /* GUIMessage.cpp in Sources */, + 2AB491701CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp in Sources */, 18B7C7D01294222E009E7A26 /* GUIMoverControl.cpp in Sources */, 18B7C7D11294222E009E7A26 /* GUIMultiImage.cpp in Sources */, 18B7C7D21294222E009E7A26 /* GUIMultiSelectText.cpp in Sources */, @@ -11321,6 +11328,7 @@ E49913B2174E5F3700741B6D /* WebSocket.cpp in Sources */, E49913B3174E5F3700741B6D /* WebSocketManager.cpp in Sources */, E49913B4174E5F3700741B6D /* WebSocketV13.cpp in Sources */, + 2AB491711CDDF1920004C263 /* HTTPRequestHandlerUtils.cpp in Sources */, E49913B5174E5F3700741B6D /* WebSocketV8.cpp in Sources */, E49913B6174E5F3C00741B6D /* AirPlayServer.cpp in Sources */, E49913B7174E5F3C00741B6D /* AirTunesServer.cpp in Sources */, diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index cc4305a10d..11a42be1b9 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -3069,7 +3069,12 @@ msgctxt "#680" msgid "Verbose logging for the [B]Video[/B] component" msgstr "" -#empty strings from id 681 to 699 +#: xbmc/settings/AdvancedSettings.cpp +msgctxt "#681" +msgid "Verbose logging for the [B]Webserver[/B] component" +msgstr "" + +#empty strings from id 682 to 699 msgctxt "#700" msgid "Cleaning up library" diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index 0c88bc4743..91e937d437 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -714,6 +714,7 @@ copy "..\Win32BuildSetup\dependencies\python27.dll" "$(TargetDir)"</Command> <ClCompile Include="..\..\xbmc\peripherals\devices\PeripheralJoystick.cpp" /> <ClCompile Include="..\..\xbmc\peripherals\EventScanner.cpp" /> <ClCompile Include="..\..\xbmc\peripherals\EventScanRate.cpp" /> + <ClCompile Include="..\..\xbmc\network\httprequesthandler\HTTPRequestHandlerUtils.cpp" /> <ClCompile Include="..\..\xbmc\platform\posix\main.cpp"> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Testsuite|Win32'">true</ExcludedFromBuild> </ClCompile> @@ -1267,6 +1268,7 @@ copy "..\Win32BuildSetup\dependencies\python27.dll" "$(TargetDir)"</Command> <ClInclude Include="..\..\xbmc\peripherals\devices\PeripheralJoystick.h" /> <ClInclude Include="..\..\xbmc\peripherals\EventScanner.h" /> <ClInclude Include="..\..\xbmc\peripherals\EventScanRate.h" /> + <ClInclude Include="..\..\xbmc\network\httprequesthandler\HTTPRequestHandlerUtils.h" /> <ClInclude Include="..\..\xbmc\platform\MessagePrinter.h" /> <ClInclude Include="..\..\xbmc\media\MediaType.h" /> <ClInclude Include="..\..\xbmc\messaging\ApplicationMessenger.h" /> @@ -3148,4 +3150,4 @@ copy "..\Win32BuildSetup\dependencies\python27.dll" "$(TargetDir)"</Command> </VisualStudio> </ProjectExtensions> <Import Project="$(SolutionDir)\$(ProjectFileName).targets.user" Condition="Exists('$(SolutionDir)\$(ProjectFileName).targets.user')" /> -</Project>
\ No newline at end of file +</Project> diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index d9458f7a49..79f39c2706 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -2842,6 +2842,11 @@ <ClCompile Include="..\..\xbmc\cores\FFmpeg.cpp"> <Filter>cores</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\network\httprequesthandler\HTTPRequestHandlerUtils.cpp"> + <Filter>network\httprequesthandler</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> <ClCompile Include="..\..\xbmc\media\MediaType.cpp"> <Filter>media</Filter> </ClCompile> @@ -6723,6 +6728,9 @@ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogKeyboardTouch.h"> <Filter>dialogs</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\network\httprequesthandler\HTTPRequestHandlerUtils.h"> + <Filter>network\httprequesthandler</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc"> @@ -6809,4 +6817,4 @@ <Filter>shaders</Filter> </FxCompile> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/xbmc/commons/ilog.h b/xbmc/commons/ilog.h index de90359aa6..90cd9a7149 100644 --- a/xbmc/commons/ilog.h +++ b/xbmc/commons/ilog.h @@ -39,20 +39,21 @@ #define LOGNONE 7 // extra masks - from bit 5 -#define LOGMASKBIT 5 -#define LOGMASK ((1 << LOGMASKBIT) - 1) +#define LOGMASKBIT 5 +#define LOGMASK ((1 << LOGMASKBIT) - 1) -#define LOGSAMBA (1 << (LOGMASKBIT + 0)) -#define LOGCURL (1 << (LOGMASKBIT + 1)) -#define LOGFFMPEG (1 << (LOGMASKBIT + 2)) -#define LOGRTMP (1 << (LOGMASKBIT + 3)) -#define LOGDBUS (1 << (LOGMASKBIT + 4)) -#define LOGJSONRPC (1 << (LOGMASKBIT + 5)) -#define LOGAUDIO (1 << (LOGMASKBIT + 6)) -#define LOGAIRTUNES (1 << (LOGMASKBIT + 7)) -#define LOGUPNP (1 << (LOGMASKBIT + 8)) -#define LOGCEC (1 << (LOGMASKBIT + 9)) -#define LOGVIDEO (1 << (LOGMASKBIT + 10)) +#define LOGSAMBA (1 << (LOGMASKBIT + 0)) +#define LOGCURL (1 << (LOGMASKBIT + 1)) +#define LOGFFMPEG (1 << (LOGMASKBIT + 2)) +#define LOGRTMP (1 << (LOGMASKBIT + 3)) +#define LOGDBUS (1 << (LOGMASKBIT + 4)) +#define LOGJSONRPC (1 << (LOGMASKBIT + 5)) +#define LOGAUDIO (1 << (LOGMASKBIT + 6)) +#define LOGAIRTUNES (1 << (LOGMASKBIT + 7)) +#define LOGUPNP (1 << (LOGMASKBIT + 8)) +#define LOGCEC (1 << (LOGMASKBIT + 9)) +#define LOGVIDEO (1 << (LOGMASKBIT + 10)) +#define LOGWEBSERVER (1 << (LOGMASKBIT + 11)) #include "utils/params_check_macros.h" diff --git a/xbmc/network/NetworkServices.cpp b/xbmc/network/NetworkServices.cpp index 1e151960d5..1aa03f8bdf 100644 --- a/xbmc/network/NetworkServices.cpp +++ b/xbmc/network/NetworkServices.cpp @@ -119,18 +119,18 @@ CNetworkServices::CNetworkServices() #endif // HAS_WEB_SERVER { #ifdef HAS_WEB_SERVER - CWebServer::RegisterRequestHandler(&m_httpImageHandler); - CWebServer::RegisterRequestHandler(&m_httpImageTransformationHandler); - CWebServer::RegisterRequestHandler(&m_httpVfsHandler); + m_webserver.RegisterRequestHandler(&m_httpImageHandler); + m_webserver.RegisterRequestHandler(&m_httpImageTransformationHandler); + m_webserver.RegisterRequestHandler(&m_httpVfsHandler); #ifdef HAS_JSONRPC - CWebServer::RegisterRequestHandler(&m_httpJsonRpcHandler); + m_webserver.RegisterRequestHandler(&m_httpJsonRpcHandler); #endif // HAS_JSONRPC #ifdef HAS_WEB_INTERFACE #ifdef HAS_PYTHON - CWebServer::RegisterRequestHandler(&m_httpPythonHandler); + m_webserver.RegisterRequestHandler(&m_httpPythonHandler); #endif - CWebServer::RegisterRequestHandler(&m_httpWebinterfaceAddonsHandler); - CWebServer::RegisterRequestHandler(&m_httpWebinterfaceHandler); + m_webserver.RegisterRequestHandler(&m_httpWebinterfaceAddonsHandler); + m_webserver.RegisterRequestHandler(&m_httpWebinterfaceHandler); #endif // HAS_WEB_INTERFACE #endif // HAS_WEB_SERVER } @@ -138,25 +138,25 @@ CNetworkServices::CNetworkServices() CNetworkServices::~CNetworkServices() { #ifdef HAS_WEB_SERVER - CWebServer::UnregisterRequestHandler(&m_httpImageHandler); + m_webserver.UnregisterRequestHandler(&m_httpImageHandler); delete &m_httpImageHandler; - CWebServer::UnregisterRequestHandler(&m_httpImageTransformationHandler); + m_webserver.UnregisterRequestHandler(&m_httpImageTransformationHandler); delete &m_httpImageTransformationHandler; - CWebServer::UnregisterRequestHandler(&m_httpVfsHandler); + m_webserver.UnregisterRequestHandler(&m_httpVfsHandler); delete &m_httpVfsHandler; #ifdef HAS_JSONRPC - CWebServer::UnregisterRequestHandler(&m_httpJsonRpcHandler); + m_webserver.UnregisterRequestHandler(&m_httpJsonRpcHandler); delete &m_httpJsonRpcHandler; CJSONRPC::Cleanup(); #endif // HAS_JSONRPC #ifdef HAS_WEB_INTERFACE #ifdef HAS_PYTHON - CWebServer::UnregisterRequestHandler(&m_httpPythonHandler); + m_webserver.UnregisterRequestHandler(&m_httpPythonHandler); delete &m_httpPythonHandler; #endif - CWebServer::UnregisterRequestHandler(&m_httpWebinterfaceAddonsHandler); + m_webserver.UnregisterRequestHandler(&m_httpWebinterfaceAddonsHandler); delete &m_httpWebinterfaceAddonsHandler; - CWebServer::UnregisterRequestHandler(&m_httpWebinterfaceHandler); + m_webserver.UnregisterRequestHandler(&m_httpWebinterfaceHandler); delete &m_httpWebinterfaceHandler; #endif // HAS_WEB_INTERFACE delete &m_webserver; diff --git a/xbmc/network/WebServer.cpp b/xbmc/network/WebServer.cpp index 9237621ef2..2f9d1f3420 100644 --- a/xbmc/network/WebServer.cpp +++ b/xbmc/network/WebServer.cpp @@ -35,7 +35,9 @@ #endif #include "filesystem/File.h" +#include "network/httprequesthandler/HTTPRequestHandlerUtils.h" #include "network/httprequesthandler/IHTTPRequestHandler.h" +#include "settings/AdvancedSettings.h" #include "settings/Settings.h" #include "threads/SingleLock.h" #include "URL.h" @@ -48,8 +50,6 @@ #include "utils/Variant.h" #include "XBDateTime.h" -//#define WEBSERVER_DEBUG - #ifdef TARGET_WINDOWS #ifndef _DEBUG #pragma comment(lib, "libmicrohttpd.lib") @@ -67,23 +67,6 @@ #define HEADER_NEWLINE "\r\n" -typedef struct ConnectionHandler -{ - std::string fullUri; - bool isNew; - std::shared_ptr<IHTTPRequestHandler> requestHandler; - struct MHD_PostProcessor *postprocessor; - int errorStatus; - - ConnectionHandler(const std::string& uri) - : fullUri(uri) - , isNew(true) - , requestHandler(nullptr) - , postprocessor(nullptr) - , errorStatus(MHD_HTTP_OK) - { } -} ConnectionHandler; - typedef struct { std::shared_ptr<XFILE::CFile> file; CHttpRanges ranges; @@ -96,10 +79,9 @@ typedef struct { uint64_t writePosition; } HttpFileDownloadContext; -std::vector<IHTTPRequestHandler *> CWebServer::m_requestHandlers; - CWebServer::CWebServer() - : m_daemon_ip6(nullptr), + : m_port(0), + m_daemon_ip6(nullptr), m_daemon_ip4(nullptr), m_running(false), m_needcredentials(false), @@ -120,44 +102,12 @@ CWebServer::CWebServer() #endif } -HTTPMethod CWebServer::GetMethod(const char *method) -{ - if (strcmp(method, "GET") == 0) - return GET; - if (strcmp(method, "POST") == 0) - return POST; - if (strcmp(method, "HEAD") == 0) - return HEAD; - - return UNKNOWN; -} - -int CWebServer::FillArgumentMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) -{ - if (cls == nullptr || key == nullptr) - return MHD_NO; - - std::map<std::string, std::string> *arguments = (std::map<std::string, std::string> *)cls; - arguments->insert(std::make_pair(key, value != nullptr ? value : "")); - return MHD_YES; -} - -int CWebServer::FillArgumentMultiMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) -{ - if (cls == nullptr || key == nullptr) - return MHD_NO; - - std::multimap<std::string, std::string> *arguments = (std::multimap<std::string, std::string> *)cls; - arguments->insert(std::make_pair(key, value != nullptr ? value : "")); - return MHD_YES; -} - -int CWebServer::AskForAuthentication(struct MHD_Connection *connection) +int CWebServer::AskForAuthentication(struct MHD_Connection *connection) const { struct MHD_Response *response = MHD_create_response_from_data(0, nullptr, MHD_NO, MHD_NO); if (!response) { - CLog::Log(LOGERROR, "CWebServer: unable to create HTTP Unauthorized response"); + CLog::Log(LOGERROR, "CWebServer[%hu]: unable to create HTTP Unauthorized response", m_port); return MHD_NO; } @@ -165,20 +115,21 @@ int CWebServer::AskForAuthentication(struct MHD_Connection *connection) ret |= AddHeader(response, MHD_HTTP_HEADER_CONNECTION, "close"); if (!ret) { - CLog::Log(LOGERROR, "CWebServer: unable to prepare HTTP Unauthorized response"); + CLog::Log(LOGERROR, "CWebServer[%hu]: unable to prepare HTTP Unauthorized response", m_port); MHD_destroy_response(response); return MHD_NO; } -#ifdef WEBSERVER_DEBUG - std::multimap<std::string, std::string> headerValues; - GetRequestHeaderValues(connection, MHD_RESPONSE_HEADER_KIND, headerValues); + if (g_advancedSettings.CanLogComponent(LOGWEBSERVER)) + { + std::multimap<std::string, std::string> headerValues; + HTTPRequestHandlerUtils::GetRequestHeaderValues(connection, MHD_RESPONSE_HEADER_KIND, headerValues); - CLog::Log(LOGDEBUG, "webserver [OUT] HTTP %d", MHD_HTTP_UNAUTHORIZED); + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] HTTP %d", m_port, MHD_HTTP_UNAUTHORIZED); - for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) - CLog::Log(LOGDEBUG, "webserver [OUT] %s: %s", header->first.c_str(), header->second.c_str()); -#endif + for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] %s: %s", m_port, header->first.c_str(), header->second.c_str()); + } ret = MHD_queue_response(connection, MHD_HTTP_UNAUTHORIZED, response); MHD_destroy_response(response); @@ -186,19 +137,19 @@ int CWebServer::AskForAuthentication(struct MHD_Connection *connection) return ret; } -bool CWebServer::IsAuthenticated(CWebServer *server, struct MHD_Connection *connection) +bool CWebServer::IsAuthenticated(struct MHD_Connection *connection) const { - CSingleLock lock(server->m_critSection); + CSingleLock lock(m_critSection); - if (!server->m_needcredentials) + if (!m_needcredentials) return true; const char *base = "Basic "; - std::string authorization = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); + std::string authorization = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); if (authorization.empty() || !StringUtils::StartsWith(authorization, base)) return false; - return server->m_Credentials64Encoded.compare(StringUtils::Mid(authorization.c_str(), strlen(base))) == 0; + return m_Credentials64Encoded.compare(StringUtils::Mid(authorization.c_str(), strlen(base))) == 0; } #if (MHD_VERSION >= 0x00040001) @@ -215,32 +166,29 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, { if (cls == nullptr || con_cls == nullptr || *con_cls == nullptr) { - CLog::Log(LOGERROR, "CWebServer: invalid request received"); + CLog::Log(LOGERROR, "CWebServer[unknown]: invalid request received"); return MHD_NO; } - CWebServer *server = reinterpret_cast<CWebServer*>(cls); - std::unique_ptr<ConnectionHandler> conHandler(reinterpret_cast<ConnectionHandler*>(*con_cls)); - HTTPMethod methodType = GetMethod(method); - HTTPRequest request = { server, connection, conHandler->fullUri, url, methodType, version }; - - // remember if the request was new - bool isNewRequest = conHandler->isNew; - // because now it isn't anymore - conHandler->isNew = false; + CWebServer *webServer = reinterpret_cast<CWebServer*>(cls); + if (webServer == nullptr) + { + CLog::Log(LOGERROR, "CWebServer[unknown]: invalid request received"); + return MHD_NO; + } - // reset con_cls and set it if still necessary - *con_cls = nullptr; + ConnectionHandler* connectionHandler = reinterpret_cast<ConnectionHandler*>(*con_cls); + HTTPMethod methodType = GetHTTPMethod(method); + HTTPRequest request = { webServer, connection, connectionHandler->fullUri, url, methodType, version }; -#ifdef WEBSERVER_DEBUG - if (isNewRequest) + if (connectionHandler->isNew && g_advancedSettings.CanLogComponent(LOGWEBSERVER)) { std::multimap<std::string, std::string> headerValues; - GetRequestHeaderValues(connection, MHD_HEADER_KIND, headerValues); + HTTPRequestHandlerUtils::GetRequestHeaderValues(connection, MHD_HEADER_KIND, headerValues); std::multimap<std::string, std::string> getValues; - GetRequestHeaderValues(connection, MHD_GET_ARGUMENT_KIND, getValues); + HTTPRequestHandlerUtils::GetRequestHeaderValues(connection, MHD_GET_ARGUMENT_KIND, getValues); - CLog::Log(LOGDEBUG, "webserver [IN] %s %s %s", version, method, request.pathUrlFull.c_str()); + CLog::Log(LOGDEBUG, "CWebServer[%hu] [IN] %s %s %s", webServer->m_port, version, GetHTTPMethod(request.method).c_str(), request.pathUrlFull.c_str()); if (!getValues.empty()) { std::string tmp; @@ -250,15 +198,29 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, tmp += "; "; tmp += get->first + " = " + get->second; } - CLog::Log(LOGDEBUG, "webserver [IN] Query arguments: %s", tmp.c_str()); + CLog::Log(LOGDEBUG, "CWebServer[%hu] [IN] Query arguments: %s", webServer->m_port, tmp.c_str()); } for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) - CLog::Log(LOGDEBUG, "webserver [IN] %s: %s", header->first.c_str(), header->second.c_str()); + CLog::Log(LOGDEBUG, "CWebServer[%hu] [IN] %s: %s", webServer->m_port, header->first.c_str(), header->second.c_str()); } -#endif - if (!IsAuthenticated(server, connection)) + return webServer->HandlePartialRequest(connection, connectionHandler, request, upload_data, upload_data_size, con_cls); +} + +int CWebServer::HandlePartialRequest(struct MHD_Connection *connection, ConnectionHandler* connectionHandler, HTTPRequest request, const char *upload_data, size_t *upload_data_size, void **con_cls) +{ + std::unique_ptr<ConnectionHandler> conHandler(connectionHandler); + + // remember if the request was new + bool isNewRequest = conHandler->isNew; + // because now it isn't anymore + conHandler->isNew = false; + + // reset con_cls and set it if still necessary + *con_cls = nullptr; + + if (!IsAuthenticated(connection)) return AskForAuthentication(connection); // check if this is the first call to AnswerToConnection for this request @@ -266,7 +228,7 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, { // parse the Range header and store it in the request object CHttpRanges ranges; - bool ranged = ranges.Parse(GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE)); + bool ranged = ranges.Parse(HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE)); // look for a IHTTPRequestHandler which can take care of the current request for (std::vector<IHTTPRequestHandler *>::const_iterator it = m_requestHandlers.begin(); it != m_requestHandlers.end(); ++it) @@ -278,14 +240,14 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, std::shared_ptr<IHTTPRequestHandler> handler(requestHandler->Create(request)); // if we got a GET request we need to check if it should be cached - if (methodType == GET) + if (request.method == GET) { if (handler->CanBeCached()) { bool cacheable = true; // handle Cache-Control - std::string cacheControl = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CACHE_CONTROL); + std::string cacheControl = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CACHE_CONTROL); if (!cacheControl.empty()) { std::vector<std::string> cacheControls = StringUtils::Split(cacheControl, ","); @@ -303,7 +265,7 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, if (cacheable) { // handle Pragma (but only if "Cache-Control: no-cache" hasn't been set) - std::string pragma = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_PRAGMA); + std::string pragma = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_PRAGMA); if (pragma.compare(HEADER_VALUE_NO_CACHE) == 0) cacheable = false; } @@ -312,8 +274,8 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, if (handler->GetLastModifiedDate(lastModified) && lastModified.IsValid()) { // handle If-Modified-Since or If-Unmodified-Since - std::string ifModifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_MODIFIED_SINCE); - std::string ifUnmodifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE); + std::string ifModifiedSince = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_MODIFIED_SINCE); + std::string ifUnmodifiedSince = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE); CDateTime ifModifiedSinceDate; CDateTime ifUnmodifiedSinceDate; @@ -325,7 +287,7 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, struct MHD_Response *response = MHD_create_response_from_data(0, nullptr, MHD_NO, MHD_NO); if (response == nullptr) { - CLog::Log(LOGERROR, "CWebServer: failed to create a HTTP 304 response"); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create a HTTP 304 response", m_port); return MHD_NO; } @@ -334,13 +296,13 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, // handle If-Unmodified-Since else if (ifUnmodifiedSinceDate.SetFromRFC1123DateTime(ifUnmodifiedSince) && lastModified.GetAsUTCDateTime() > ifUnmodifiedSinceDate) - return SendErrorResponse(connection, MHD_HTTP_PRECONDITION_FAILED, methodType); + return SendErrorResponse(connection, MHD_HTTP_PRECONDITION_FAILED, request.method); } // handle If-Range header but only if the Range header is present if (ranged && lastModified.IsValid()) { - std::string ifRange = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_RANGE); + std::string ifRange = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_RANGE); if (!ifRange.empty() && lastModified.IsValid()) { CDateTime ifRangeDate; @@ -358,12 +320,12 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, } } // if we got a POST request we need to take care of the POST data - else if (methodType == POST) + else if (request.method == POST) { conHandler->requestHandler = handler; // get the content-type of the POST data - std::string contentType = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); + std::string contentType = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); if (!contentType.empty()) { // if the content-type is application/x-ww-form-urlencoded or multipart/form-data we can use MHD's POST processor @@ -376,7 +338,7 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, // MHD doesn't seem to be able to handle this post request if (conHandler->postprocessor == nullptr) { - CLog::Log(LOGERROR, "CWebServer: unable to create HTTP POST processor for %s", url); + CLog::Log(LOGERROR, "CWebServer[%hu]: unable to create HTTP POST processor for %s", m_port, request.pathUrl.c_str()); conHandler->errorStatus = MHD_HTTP_INTERNAL_SERVER_ERROR; } } @@ -397,11 +359,11 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, else { // again we need to take special care of the POST data - if (methodType == POST) + if (request.method == POST) { if (conHandler->requestHandler == nullptr) { - CLog::Log(LOGERROR, "CWebServer: cannot handle partial HTTP POST for %s request because there is no valid request handler available", url); + CLog::Log(LOGERROR, "CWebServer[%hu]: cannot handle partial HTTP POST for %s request because there is no valid request handler available", m_port, request.pathUrl.c_str()); conHandler->errorStatus = MHD_HTTP_INTERNAL_SERVER_ERROR; } @@ -422,7 +384,7 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, // abort if the received POST data couldn't be handled if (!postDataHandled) { - CLog::Log(LOGERROR, "CWebServer: failed to handle HTTP POST data for %s", url); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to handle HTTP POST data for %s", m_port, request.pathUrl.c_str()); conHandler->errorStatus = MHD_HTTP_REQUEST_ENTITY_TOO_LARGE; } } @@ -444,7 +406,7 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, // check if something went wrong while handling the POST data if (conHandler->errorStatus != MHD_HTTP_OK) - return SendErrorResponse(connection, conHandler->errorStatus, methodType); + return SendErrorResponse(connection, conHandler->errorStatus, request.method); return HandleRequest(conHandler->requestHandler); } @@ -461,8 +423,8 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, } } - CLog::Log(LOGERROR, "CWebServer: couldn't find any request handler for %s", url); - return SendErrorResponse(connection, MHD_HTTP_NOT_FOUND, methodType); + CLog::Log(LOGERROR, "CWebServer[%hu]: couldn't find any request handler for %s", m_port, request.pathUrl.c_str()); + return SendErrorResponse(connection, MHD_HTTP_NOT_FOUND, request.method); } #if (MHD_VERSION >= 0x00040001) @@ -482,7 +444,7 @@ int CWebServer::HandlePostField(void *cls, enum MHD_ValueKind kind, const char * if (conHandler == nullptr || conHandler->requestHandler == nullptr || key == nullptr || data == nullptr || size == 0) { - CLog::Log(LOGERROR, "CWebServer: unable to handle HTTP POST field"); + CLog::Log(LOGERROR, "CWebServer[%hu]: unable to handle HTTP POST field", reinterpret_cast<CWebServer*>(cls)->m_port); return MHD_NO; } @@ -499,7 +461,7 @@ int CWebServer::HandleRequest(const std::shared_ptr<IHTTPRequestHandler>& handle int ret = handler->HandleRequest(); if (ret == MHD_NO) { - CLog::Log(LOGERROR, "CWebServer: failed to handle HTTP request for %s", request.pathUrl.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to handle HTTP request for %s", m_port, request.pathUrl.c_str()); return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } @@ -508,7 +470,7 @@ int CWebServer::HandleRequest(const std::shared_ptr<IHTTPRequestHandler>& handle switch (responseDetails.type) { case HTTPNone: - CLog::Log(LOGERROR, "CWebServer: HTTP request handler didn't process %s", request.pathUrl.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: HTTP request handler didn't process %s", m_port, request.pathUrl.c_str()); return MHD_NO; case HTTPRedirect: @@ -531,13 +493,13 @@ int CWebServer::HandleRequest(const std::shared_ptr<IHTTPRequestHandler>& handle break; default: - CLog::Log(LOGERROR, "CWebServer: internal error while HTTP request handler processed %s", request.pathUrl.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: internal error while HTTP request handler processed %s", m_port, request.pathUrl.c_str()); return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } if (ret == MHD_NO) { - CLog::Log(LOGERROR, "CWebServer: failed to create HTTP response for %s", request.pathUrl.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create HTTP response for %s", m_port, request.pathUrl.c_str()); return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } @@ -609,15 +571,16 @@ int CWebServer::FinalizeRequest(const std::shared_ptr<IHTTPRequestHandler>& hand for (std::multimap<std::string, std::string>::const_iterator it = responseDetails.headers.begin(); it != responseDetails.headers.end(); ++it) AddHeader(response, it->first, it->second); -#ifdef WEBSERVER_DEBUG - std::multimap<std::string, std::string> headerValues; - GetRequestHeaderValues(request.connection, MHD_RESPONSE_HEADER_KIND, headerValues); + if (g_advancedSettings.CanLogComponent(LOGWEBSERVER)) + { + std::multimap<std::string, std::string> headerValues; + HTTPRequestHandlerUtils::GetRequestHeaderValues(request.connection, MHD_RESPONSE_HEADER_KIND, headerValues); - CLog::Log(LOGDEBUG, "webserver [OUT] %s %d %s", request.version.c_str(), responseStatus, request.pathUrlFull.c_str()); + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] %s %d %s", m_port, request.version.c_str(), responseStatus, request.pathUrlFull.c_str()); - for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) - CLog::Log(LOGDEBUG, "webserver [OUT] %s: %s", header->first.c_str(), header->second.c_str()); -#endif + for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] %s: %s", m_port, header->first.c_str(), header->second.c_str()); + } int ret = MHD_queue_response(request.connection, responseStatus, response); MHD_destroy_response(response); @@ -625,7 +588,7 @@ int CWebServer::FinalizeRequest(const std::shared_ptr<IHTTPRequestHandler>& hand return ret; } -int CWebServer::CreateMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) +int CWebServer::CreateMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) const { if (handler == nullptr) return MHD_NO; @@ -642,7 +605,7 @@ int CWebServer::CreateMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestH if ((request.ranges.IsEmpty() && responseRanges.size() > 1) || (!request.ranges.IsEmpty() && responseRanges.size() > request.ranges.Size())) { - CLog::Log(LOGWARNING, "CWebServer: response contains more ranges (%d) than the request asked for (%d)", (int)responseRanges.size(), (int)request.ranges.Size()); + CLog::Log(LOGWARNING, "CWebServer[%hu]: response contains more ranges (%d) than the request asked for (%d)", m_port, (int)responseRanges.size(), (int)request.ranges.Size()); return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } @@ -654,7 +617,7 @@ int CWebServer::CreateMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestH // check if the range is valid if (!responseRange.IsValid()) { - CLog::Log(LOGWARNING, "CWebServer: invalid response data with range start at %" PRId64 " and end at %" PRId64, responseRange.GetFirstPosition(), responseRange.GetLastPosition()); + CLog::Log(LOGWARNING, "CWebServer[%hu]: invalid response data with range start at %" PRId64 " and end at %" PRId64, m_port, responseRange.GetFirstPosition(), responseRange.GetLastPosition()); return SendErrorResponse(request.connection, MHD_HTTP_INTERNAL_SERVER_ERROR, request.method); } @@ -683,7 +646,7 @@ int CWebServer::CreateMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestH return CreateRangedMemoryDownloadResponse(handler, response); } -int CWebServer::CreateRangedMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) +int CWebServer::CreateRangedMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) const { if (handler == nullptr) return MHD_NO; @@ -763,12 +726,12 @@ int CWebServer::CreateRangedMemoryDownloadResponse(const std::shared_ptr<IHTTPRe return CreateMemoryDownloadResponse(request.connection, result.c_str(), result.size(), false, true, response); } -int CWebServer::CreateRedirect(struct MHD_Connection *connection, const std::string &strURL, struct MHD_Response *&response) +int CWebServer::CreateRedirect(struct MHD_Connection *connection, const std::string &strURL, struct MHD_Response *&response) const { response = MHD_create_response_from_data(0, nullptr, MHD_NO, MHD_NO); if (response == nullptr) { - CLog::Log(LOGERROR, "CWebServer: failed to create HTTP redirect response to %s", strURL.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create HTTP redirect response to %s", m_port, strURL.c_str()); return MHD_NO; } @@ -776,7 +739,7 @@ int CWebServer::CreateRedirect(struct MHD_Connection *connection, const std::str return MHD_YES; } -int CWebServer::CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) +int CWebServer::CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) const { if (handler == nullptr) return MHD_NO; @@ -790,7 +753,7 @@ int CWebServer::CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHan if (!file->Open(filePath, XFILE::READ_NO_CACHE)) { - CLog::Log(LOGERROR, "WebServer: Failed to open %s", filePath.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: Failed to open %s", m_port, filePath.c_str()); return SendErrorResponse(request.connection, MHD_HTTP_NOT_FOUND, request.method); } @@ -820,7 +783,7 @@ int CWebServer::CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHan if (!request.ranges.IsEmpty()) context->ranges = request.ranges; else - GetRequestedRanges(request.connection, fileLength, context->ranges); + HTTPRequestHandlerUtils::GetRequestedRanges(request.connection, fileLength, context->ranges); } uint64_t firstPosition = 0; @@ -880,7 +843,7 @@ int CWebServer::CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHan &CWebServer::ContentReaderFreeCallback); if (response == nullptr) { - CLog::Log(LOGERROR, "CWebServer: failed to create a HTTP response for %s to be filled from %s", request.pathUrl.c_str(), filePath.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create a HTTP response for %s to be filled from %s", m_port, request.pathUrl.c_str(), filePath.c_str()); return MHD_NO; } @@ -895,7 +858,7 @@ int CWebServer::CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHan response = MHD_create_response_from_data(0, nullptr, MHD_NO, MHD_NO); if (response == nullptr) { - CLog::Log(LOGERROR, "CWebServer: failed to create a HTTP HEAD response for %s", request.pathUrl.c_str()); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create a HTTP HEAD response for %s", m_port, request.pathUrl.c_str()); return MHD_NO; } @@ -909,7 +872,7 @@ int CWebServer::CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHan return MHD_YES; } -int CWebServer::CreateErrorResponse(struct MHD_Connection *connection, int responseType, HTTPMethod method, struct MHD_Response *&response) +int CWebServer::CreateErrorResponse(struct MHD_Connection *connection, int responseType, HTTPMethod method, struct MHD_Response *&response) const { size_t payloadSize = 0; void *payload = nullptr; @@ -933,40 +896,41 @@ int CWebServer::CreateErrorResponse(struct MHD_Connection *connection, int respo response = MHD_create_response_from_data(payloadSize, payload, MHD_NO, MHD_NO); if (response == nullptr) { - CLog::Log(LOGERROR, "CWebServer: failed to create a HTTP %d error response", responseType); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create a HTTP %d error response", m_port, responseType); return MHD_NO; } return MHD_YES; } -int CWebServer::CreateMemoryDownloadResponse(struct MHD_Connection *connection, const void *data, size_t size, bool free, bool copy, struct MHD_Response *&response) +int CWebServer::CreateMemoryDownloadResponse(struct MHD_Connection *connection, const void *data, size_t size, bool free, bool copy, struct MHD_Response *&response) const { response = MHD_create_response_from_data(size, const_cast<void*>(data), free ? MHD_YES : MHD_NO, copy ? MHD_YES : MHD_NO); if (response == nullptr) { - CLog::Log(LOGERROR, "CWebServer: failed to create a HTTP download response"); + CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create a HTTP download response", m_port); return MHD_NO; } return MHD_YES; } -int CWebServer::SendErrorResponse(struct MHD_Connection *connection, int errorType, HTTPMethod method) +int CWebServer::SendErrorResponse(struct MHD_Connection *connection, int errorType, HTTPMethod method) const { struct MHD_Response *response = nullptr; int ret = CreateErrorResponse(connection, errorType, method, response); if (ret == MHD_YES) { -#ifdef WEBSERVER_DEBUG - std::multimap<std::string, std::string> headerValues; - GetRequestHeaderValues(connection, MHD_RESPONSE_HEADER_KIND, headerValues); + if (g_advancedSettings.CanLogComponent(LOGWEBSERVER)) + { + std::multimap<std::string, std::string> headerValues; + HTTPRequestHandlerUtils::GetRequestHeaderValues(connection, MHD_RESPONSE_HEADER_KIND, headerValues); - CLog::Log(LOGDEBUG, "webserver [OUT] HTTP %d", errorType); + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] HTTP %d", m_port, errorType); - for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) - CLog::Log(LOGDEBUG, "webserver [OUT] %s: %s", header->first.c_str(), header->second.c_str()); -#endif + for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] %s: %s", m_port, header->first.c_str(), header->second.c_str()); + } ret = MHD_queue_response(connection, errorType, response); MHD_destroy_response(response); @@ -977,13 +941,26 @@ int CWebServer::SendErrorResponse(struct MHD_Connection *connection, int errorTy void* CWebServer::UriRequestLogger(void *cls, const char *uri) { + CWebServer *webServer = reinterpret_cast<CWebServer*>(cls); + // log the full URI - CLog::Log(LOGDEBUG, "webserver: request received for %s", uri); + if (webServer == nullptr) + CLog::Log(LOGDEBUG, "CWebServer[unknown]: request received for %s", uri); + else + webServer->LogRequest(uri); // create and return a new connection handler return new ConnectionHandler(uri); } +void CWebServer::LogRequest(const char* uri) const +{ + if (uri == nullptr) + return; + + CLog::Log(LOGDEBUG, "CWebServer[%hu]: request received for %s", m_port, uri); +} + #if (MHD_VERSION >= 0x00090200) ssize_t CWebServer::ContentReaderCallback(void *cls, uint64_t pos, char *buf, size_t max) #elif (MHD_VERSION >= 0x00040001) @@ -996,9 +973,8 @@ int CWebServer::ContentReaderCallback(void *cls, size_t pos, char *buf, int max) if (context == nullptr || context->file == nullptr) return -1; -#ifdef WEBSERVER_DEBUG - CLog::Log(LOGDEBUG, "webserver [OUT] write maximum %d bytes from %" PRIu64 " (%" PRIu64 ")", max, context->writePosition, pos); -#endif + if (g_advancedSettings.CanLogComponent(LOGWEBSERVER)) + CLog::Log(LOGDEBUG, "CWebServer [OUT] write maximum %d bytes from %" PRIu64 " (%" PRIu64 ")", max, context->writePosition, pos); // check if we need to add the end-boundary if (context->rangeCountTotal > 1 && context->ranges.IsEmpty()) @@ -1066,9 +1042,10 @@ int CWebServer::ContentReaderCallback(void *cls, size_t pos, char *buf, int max) // add the number of read bytes to the number of written bytes written += res; -#ifdef WEBSERVER_DEBUG - CLog::Log(LOGDEBUG, "webserver [OUT] wrote %d bytes from %" PRIu64 " in range (%" PRIu64 " - %" PRIu64 ")", written, context->writePosition, start, end); -#endif + + if (g_advancedSettings.CanLogComponent(LOGWEBSERVER)) + CLog::Log(LOGDEBUG, "CWebServer [OUT] wrote %d bytes from %" PRIu64 " in range (%" PRIu64 " - %" PRIu64 ")", written, context->writePosition, start, end); + // update the current write position context->writePosition += res; @@ -1088,9 +1065,8 @@ void CWebServer::ContentReaderFreeCallback(void *cls) HttpFileDownloadContext *context = (HttpFileDownloadContext *)cls; delete context; -#ifdef WEBSERVER_DEBUG - CLog::Log(LOGDEBUG, "webserver [OUT] done"); -#endif + if (g_advancedSettings.CanLogComponent(LOGWEBSERVER)) + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] done", reinterpret_cast<CWebServer*>(cls)->m_port); } // local helper @@ -1164,7 +1140,7 @@ struct MHD_Daemon* CWebServer::StartMHD(unsigned int flags, int port) MHD_OPTION_END); } -bool CWebServer::Start(int port, const std::string &username, const std::string &password) +bool CWebServer::Start(uint16_t port, const std::string &username, const std::string &password) { SetCredentials(username, password); if (!m_running) @@ -1180,9 +1156,12 @@ bool CWebServer::Start(int port, const std::string &username, const std::string m_running = (m_daemon_ip6 != nullptr) || (m_daemon_ip4 != nullptr); if (m_running) - CLog::Log(LOGNOTICE, "WebServer: Started the webserver"); + { + m_port = port; + CLog::Log(LOGNOTICE, "CWebServer[%hu]: Started", m_port); + } else - CLog::Log(LOGERROR, "WebServer: Failed to start the webserver"); + CLog::Log(LOGERROR, "CWebServer[%hu]: Failed to start", port); } return m_running; @@ -1190,21 +1169,20 @@ bool CWebServer::Start(int port, const std::string &username, const std::string bool CWebServer::Stop() { - if (m_running) - { - if (m_daemon_ip6 != nullptr) - MHD_stop_daemon(m_daemon_ip6); + if (!m_running) + return true; + + if (m_daemon_ip6 != nullptr) + MHD_stop_daemon(m_daemon_ip6); - if (m_daemon_ip4 != nullptr) - MHD_stop_daemon(m_daemon_ip4); + if (m_daemon_ip4 != nullptr) + MHD_stop_daemon(m_daemon_ip4); - m_running = false; - CLog::Log(LOGNOTICE, "WebServer: Stopped the webserver"); - } - else - CLog::Log(LOGNOTICE, "WebServer: Stopped failed because its not running"); + m_running = false; + CLog::Log(LOGNOTICE, "CWebServer[%hu]: Stopped", m_port); + m_port = 0; - return !m_running; + return true; } bool CWebServer::IsStarted() @@ -1220,53 +1198,18 @@ void CWebServer::SetCredentials(const std::string &username, const std::string & m_needcredentials = !password.empty(); } -bool CWebServer::PrepareDownload(const char *path, CVariant &details, std::string &protocol) -{ - if (!XFILE::CFile::Exists(path)) - return false; - - protocol = "http"; - std::string url; - std::string strPath = path; - if (StringUtils::StartsWith(strPath, "image://") || - (StringUtils::StartsWith(strPath, "special://") && StringUtils::EndsWith(strPath, ".tbn"))) - url = "image/"; - else - url = "vfs/"; - url += CURL::Encode(strPath); - details["path"] = url; - - return true; -} - -bool CWebServer::Download(const char *path, CVariant &result) -{ - return false; -} - -int CWebServer::GetCapabilities() -{ - return JSONRPC::Response | JSONRPC::FileDownloadRedirect; -} - void CWebServer::RegisterRequestHandler(IHTTPRequestHandler *handler) { if (handler == nullptr) return; - for (std::vector<IHTTPRequestHandler *>::iterator it = m_requestHandlers.begin(); it != m_requestHandlers.end(); ++it) - { - if (*it == handler) - return; - - if ((*it)->GetPriority() < handler->GetPriority()) - { - m_requestHandlers.insert(it, handler); - return; - } - } + const auto& it = std::find(m_requestHandlers.cbegin(), m_requestHandlers.cend(), handler); + if (it != m_requestHandlers.cend()) + return; m_requestHandlers.push_back(handler); + std::sort(m_requestHandlers.begin(), m_requestHandlers.end(), + [](IHTTPRequestHandler* lhs, IHTTPRequestHandler* rhs) { return rhs->GetPriority() < lhs->GetPriority(); }); } void CWebServer::UnregisterRequestHandler(IHTTPRequestHandler *handler) @@ -1274,64 +1217,7 @@ void CWebServer::UnregisterRequestHandler(IHTTPRequestHandler *handler) if (handler == nullptr) return; - for (std::vector<IHTTPRequestHandler *>::iterator it = m_requestHandlers.begin(); it != m_requestHandlers.end(); ++it) - { - if (*it == handler) - { - m_requestHandlers.erase(it); - return; - } - } -} - -std::string CWebServer::GetRequestHeaderValue(struct MHD_Connection *connection, enum MHD_ValueKind kind, const std::string &key) -{ - if (connection == nullptr) - return ""; - - const char* value = MHD_lookup_connection_value(connection, kind, key.c_str()); - if (value == nullptr) - return ""; - - if (StringUtils::EqualsNoCase(key, MHD_HTTP_HEADER_CONTENT_TYPE)) - { - // Work around a bug in firefox (see https://bugzilla.mozilla.org/show_bug.cgi?id=416178) - // by cutting of anything that follows a ";" in a "Content-Type" header field - std::string strValue(value); - size_t pos = strValue.find(';'); - if (pos != std::string::npos) - strValue = strValue.substr(0, pos); - - return strValue; - } - - return value; -} - -int CWebServer::GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::map<std::string, std::string> &headerValues) -{ - if (connection == nullptr) - return -1; - - return MHD_get_connection_values(connection, kind, FillArgumentMap, &headerValues); -} - -int CWebServer::GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::multimap<std::string, std::string> &headerValues) -{ - if (connection == nullptr) - return -1; - - return MHD_get_connection_values(connection, kind, FillArgumentMultiMap, &headerValues); -} - -bool CWebServer::GetRequestedRanges(struct MHD_Connection *connection, uint64_t totalLength, CHttpRanges &ranges) -{ - ranges.Clear(); - - if (connection == nullptr) - return false; - - return ranges.Parse(GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE), totalLength); + m_requestHandlers.erase(std::remove(m_requestHandlers.begin(), m_requestHandlers.end(), handler), m_requestHandlers.end()); } std::string CWebServer::CreateMimeTypeFromExtension(const char *ext) @@ -1344,37 +1230,14 @@ std::string CWebServer::CreateMimeTypeFromExtension(const char *ext) return CMime::GetMimeType(ext); } -int CWebServer::AddHeader(struct MHD_Response *response, const std::string &name, const std::string &value) +int CWebServer::AddHeader(struct MHD_Response *response, const std::string &name, const std::string &value) const { if (response == nullptr || name.empty()) return 0; -#ifdef WEBSERVER_DEBUG - CLog::Log(LOGDEBUG, "webserver [OUT] %s: %s", name.c_str(), value.c_str()); -#endif - return MHD_add_response_header(response, name.c_str(), value.c_str()); -} - -bool CWebServer::GetLastModifiedDateTime(XFILE::CFile *file, CDateTime &lastModified) -{ - if (file == nullptr) - return false; + if (g_advancedSettings.CanLogComponent(LOGWEBSERVER)) + CLog::Log(LOGDEBUG, "CWebServer[%hu] [OUT] %s: %s", m_port, name.c_str(), value.c_str()); - struct __stat64 statBuffer; - if (file->Stat(&statBuffer) != 0) - return false; - - struct tm *time; -#ifdef HAVE_LOCALTIME_R - struct tm result = {}; - time = localtime_r((time_t*)&statBuffer.st_mtime, &result); -#else - time = localtime((time_t *)&statBuffer.st_mtime); -#endif - if (time == nullptr) - return false; - - lastModified = *time; - return true; + return MHD_add_response_header(response, name.c_str(), value.c_str()); } #endif diff --git a/xbmc/network/WebServer.h b/xbmc/network/WebServer.h index 19a953edaf..2f5faae10d 100644 --- a/xbmc/network/WebServer.h +++ b/xbmc/network/WebServer.h @@ -25,7 +25,6 @@ #include <memory> #include <vector> -#include "interfaces/json-rpc/ITransportLayer.h" #include "network/httprequesthandler/IHTTPRequestHandler.h" #include "threads/CriticalSection.h" @@ -36,36 +35,66 @@ namespace XFILE class CDateTime; class CVariant; -class CWebServer : public JSONRPC::ITransportLayer +class CWebServer { public: CWebServer(); virtual ~CWebServer() { } - // implementation of JSONRPC::ITransportLayer - virtual bool PrepareDownload(const char *path, CVariant &details, std::string &protocol); - virtual bool Download(const char *path, CVariant &result); - virtual int GetCapabilities(); - - bool Start(int port, const std::string &username, const std::string &password); + bool Start(uint16_t port, const std::string &username, const std::string &password); bool Stop(); bool IsStarted(); void SetCredentials(const std::string &username, const std::string &password); - static void RegisterRequestHandler(IHTTPRequestHandler *handler); - static void UnregisterRequestHandler(IHTTPRequestHandler *handler); - - static std::string GetRequestHeaderValue(struct MHD_Connection *connection, enum MHD_ValueKind kind, const std::string &key); - static int GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::map<std::string, std::string> &headerValues); - static int GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::multimap<std::string, std::string> &headerValues); - - static bool GetRequestedRanges(struct MHD_Connection *connection, uint64_t totalLength, CHttpRanges &ranges); + void RegisterRequestHandler(IHTTPRequestHandler *handler); + void UnregisterRequestHandler(IHTTPRequestHandler *handler); + +protected: + typedef struct ConnectionHandler + { + std::string fullUri; + bool isNew; + std::shared_ptr<IHTTPRequestHandler> requestHandler; + struct MHD_PostProcessor *postprocessor; + int errorStatus; + + ConnectionHandler(const std::string& uri) + : fullUri(uri) + , isNew(true) + , requestHandler(nullptr) + , postprocessor(nullptr) + , errorStatus(MHD_HTTP_OK) + { } + } ConnectionHandler; + + virtual void LogRequest(const char* uri) const; + + virtual int HandlePartialRequest(struct MHD_Connection *connection, ConnectionHandler* connectionHandler, HTTPRequest request, + const char *upload_data, size_t *upload_data_size, void **con_cls); + virtual int HandleRequest(const std::shared_ptr<IHTTPRequestHandler>& handler); + virtual int FinalizeRequest(const std::shared_ptr<IHTTPRequestHandler>& handler, int responseStatus, struct MHD_Response *response); private: struct MHD_Daemon* StartMHD(unsigned int flags, int port); - static int AskForAuthentication (struct MHD_Connection *connection); - static bool IsAuthenticated (CWebServer *server, struct MHD_Connection *connection); + int AskForAuthentication(struct MHD_Connection *connection) const; + bool IsAuthenticated(struct MHD_Connection *connection) const; + + int CreateMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) const; + int CreateRangedMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) const; + + int CreateRedirect(struct MHD_Connection *connection, const std::string &strURL, struct MHD_Response *&response) const; + int CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response) const; + int CreateErrorResponse(struct MHD_Connection *connection, int responseType, HTTPMethod method, struct MHD_Response *&response) const; + int CreateMemoryDownloadResponse(struct MHD_Connection *connection, const void *data, size_t size, bool free, bool copy, struct MHD_Response *&response) const; + + int SendErrorResponse(struct MHD_Connection *connection, int errorType, HTTPMethod method) const; + + int AddHeader(struct MHD_Response *response, const std::string &name, const std::string &value) const; + + static std::string CreateMimeTypeFromExtension(const char *ext); + + // MHD callback implementations static void* UriRequestLogger(void *cls, const char *uri); #if (MHD_VERSION >= 0x00090200) @@ -96,28 +125,8 @@ private: const char *transfer_encoding, const char *data, uint64_t off, unsigned int size); #endif - static int HandleRequest(const std::shared_ptr<IHTTPRequestHandler>& handler); - static int FinalizeRequest(const std::shared_ptr<IHTTPRequestHandler>& handler, int responseStatus, struct MHD_Response *response); - - static int CreateMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response); - static int CreateRangedMemoryDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response); - - static int CreateRedirect(struct MHD_Connection *connection, const std::string &strURL, struct MHD_Response *&response); - static int CreateFileDownloadResponse(const std::shared_ptr<IHTTPRequestHandler>& handler, struct MHD_Response *&response); - static int CreateErrorResponse(struct MHD_Connection *connection, int responseType, HTTPMethod method, struct MHD_Response *&response); - static int CreateMemoryDownloadResponse(struct MHD_Connection *connection, const void *data, size_t size, bool free, bool copy, struct MHD_Response *&response); - - static int SendErrorResponse(struct MHD_Connection *connection, int errorType, HTTPMethod method); - - static HTTPMethod GetMethod(const char *method); - static int FillArgumentMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); - static int FillArgumentMultiMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); - - static std::string CreateMimeTypeFromExtension(const char *ext); - - static int AddHeader(struct MHD_Response *response, const std::string &name, const std::string &value); - static bool GetLastModifiedDateTime(XFILE::CFile *file, CDateTime &lastModified); + uint16_t m_port; struct MHD_Daemon *m_daemon_ip6; struct MHD_Daemon *m_daemon_ip4; bool m_running; @@ -125,6 +134,6 @@ private: size_t m_thread_stacksize; std::string m_Credentials64Encoded; CCriticalSection m_critSection; - static std::vector<IHTTPRequestHandler *> m_requestHandlers; + std::vector<IHTTPRequestHandler *> m_requestHandlers; }; #endif diff --git a/xbmc/network/httprequesthandler/CMakeLists.txt b/xbmc/network/httprequesthandler/CMakeLists.txt index eb5523b529..885bf96683 100644 --- a/xbmc/network/httprequesthandler/CMakeLists.txt +++ b/xbmc/network/httprequesthandler/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES HTTPFileHandler.cpp HTTPImageTransformationHandler.cpp HTTPJsonRpcHandler.cpp HTTPPythonHandler.cpp + HTTPRequestHandlerUtils.cpp HTTPVfsHandler.cpp HTTPWebinterfaceAddonsHandler.cpp HTTPWebinterfaceHandler.cpp @@ -13,6 +14,7 @@ set(HEADERS HTTPFileHandler.h HTTPImageTransformationHandler.h HTTPJsonRpcHandler.h HTTPPythonHandler.h + HTTPRequestHandlerUtils.h HTTPVfsHandler.h HTTPWebinterfaceAddonsHandler.h HTTPWebinterfaceHandler.h diff --git a/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp b/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp index b00c760640..51040daa61 100644 --- a/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp +++ b/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp @@ -25,6 +25,7 @@ #include "URL.h" #include "filesystem/ImageFile.h" #include "network/WebServer.h" +#include "network/httprequesthandler/HTTPRequestHandlerUtils.h" #include "utils/Mime.h" #include "utils/StringUtils.h" #include "utils/URIUtils.h" @@ -109,7 +110,7 @@ bool CHTTPImageTransformationHandler::CanHandleRequest(const HTTPRequest &reques // get the transformation options std::map<std::string, std::string> options; - CWebServer::GetRequestHeaderValues(request.connection, MHD_GET_ARGUMENT_KIND, options); + HTTPRequestHandlerUtils::GetRequestHeaderValues(request.connection, MHD_GET_ARGUMENT_KIND, options); return (options.find(TRANSFORMATION_OPTION_WIDTH) != options.end() || options.find(TRANSFORMATION_OPTION_HEIGHT) != options.end()); @@ -131,7 +132,7 @@ int CHTTPImageTransformationHandler::HandleRequest() // get the transformation options std::map<std::string, std::string> options; - CWebServer::GetRequestHeaderValues(m_request.connection, MHD_GET_ARGUMENT_KIND, options); + HTTPRequestHandlerUtils::GetRequestHeaderValues(m_request.connection, MHD_GET_ARGUMENT_KIND, options); std::vector<std::string> urlOptions; std::map<std::string, std::string>::const_iterator option = options.find(TRANSFORMATION_OPTION_WIDTH); diff --git a/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp b/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp index d922ef4191..3a604bc4b3 100644 --- a/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp +++ b/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp @@ -19,10 +19,13 @@ */ #include "HTTPJsonRpcHandler.h" +#include "URL.h" +#include "filesystem/File.h" #include "interfaces/json-rpc/JSONRPC.h" #include "interfaces/json-rpc/JSONServiceDescription.h" #include "interfaces/json-rpc/JSONUtils.h" #include "network/WebServer.h" +#include "network/httprequesthandler/HTTPRequestHandlerUtils.h" #include "utils/JSONVariantWriter.h" #include "utils/log.h" #include "utils/Variant.h" @@ -42,11 +45,11 @@ int CHTTPJsonRpcHandler::HandleRequest() // get all query arguments std::map<std::string, std::string> arguments; - CWebServer::GetRequestHeaderValues(m_request.connection, MHD_GET_ARGUMENT_KIND, arguments); + HTTPRequestHandlerUtils::GetRequestHeaderValues(m_request.connection, MHD_GET_ARGUMENT_KIND, arguments); if (m_request.method == POST) { - std::string contentType = CWebServer::GetRequestHeaderValue(m_request.connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); + std::string contentType = HTTPRequestHandlerUtils::GetRequestHeaderValue(m_request.connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); // If the content-type of the m_request was specified, it must be application/json-rpc, application/json, or application/jsonrequest // http://www.jsonrpc.org/historical/json-rpc-over-http.html if (!contentType.empty() && contentType.compare("application/json-rpc") != 0 && @@ -81,7 +84,7 @@ int CHTTPJsonRpcHandler::HandleRequest() if (isRequest) { - m_responseData = JSONRPC::CJSONRPC::MethodCall(m_requestData, m_request.webserver, &client); + m_responseData = JSONRPC::CJSONRPC::MethodCall(m_requestData, &m_transportLayer, &client); if (!jsonpCallback.empty()) m_responseData = jsonpCallback + "(" + m_responseData + ");"; @@ -90,7 +93,7 @@ int CHTTPJsonRpcHandler::HandleRequest() { // get the whole output of JSONRPC.Introspect CVariant result; - JSONRPC::CJSONServiceDescription::Print(result, m_request.webserver, &client); + JSONRPC::CJSONServiceDescription::Print(result, &m_transportLayer, &client); m_responseData = CJSONVariantWriter::Write(result, false); } else @@ -138,6 +141,35 @@ bool CHTTPJsonRpcHandler::appendPostData(const char *data, unsigned int size) return true; } +bool CHTTPJsonRpcHandler::CHTTPTransportLayer::PrepareDownload(const char *path, CVariant &details, std::string &protocol) +{ + if (!XFILE::CFile::Exists(path)) + return false; + + protocol = "http"; + std::string url; + std::string strPath = path; + if (StringUtils::StartsWith(strPath, "image://") || + (StringUtils::StartsWith(strPath, "special://") && StringUtils::EndsWith(strPath, ".tbn"))) + url = "image/"; + else + url = "vfs/"; + url += CURL::Encode(strPath); + details["path"] = url; + + return true; +} + +bool CHTTPJsonRpcHandler::CHTTPTransportLayer::Download(const char *path, CVariant &result) +{ + return false; +} + +int CHTTPJsonRpcHandler::CHTTPTransportLayer::GetCapabilities() +{ + return JSONRPC::Response | JSONRPC::FileDownloadRedirect; +} + int CHTTPJsonRpcHandler::CHTTPClient::GetPermissionFlags() { return JSONRPC::OPERATION_PERMISSION_ALL; diff --git a/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.h b/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.h index c4287eaf1b..273f925b2b 100644 --- a/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.h +++ b/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.h @@ -22,6 +22,7 @@ #include <string> #include "interfaces/json-rpc/IClient.h" +#include "interfaces/json-rpc/ITransportLayer.h" #include "network/httprequesthandler/IHTTPRequestHandler.h" class CHTTPJsonRpcHandler : public IHTTPRequestHandler @@ -30,6 +31,7 @@ public: CHTTPJsonRpcHandler() { } virtual ~CHTTPJsonRpcHandler() { } + // implementations of IHTTPRequestHandler virtual IHTTPRequestHandler* Create(const HTTPRequest &request) { return new CHTTPJsonRpcHandler(request); } virtual bool CanHandleRequest(const HTTPRequest &request); @@ -55,6 +57,19 @@ private: std::string m_responseData; CHttpResponseRange m_responseRange; + class CHTTPTransportLayer : public JSONRPC::ITransportLayer + { + public: + CHTTPTransportLayer() = default; + ~CHTTPTransportLayer() = default; + + // implementations of JSONRPC::ITransportLayer + bool PrepareDownload(const char *path, CVariant &details, std::string &protocol) override; + bool Download(const char *path, CVariant &result) override; + int GetCapabilities() override; + }; + CHTTPTransportLayer m_transportLayer; + class CHTTPClient : public JSONRPC::IClient { public: diff --git a/xbmc/network/httprequesthandler/HTTPPythonHandler.cpp b/xbmc/network/httprequesthandler/HTTPPythonHandler.cpp index a37a760d5d..58dc8010bf 100644 --- a/xbmc/network/httprequesthandler/HTTPPythonHandler.cpp +++ b/xbmc/network/httprequesthandler/HTTPPythonHandler.cpp @@ -25,6 +25,7 @@ #include "interfaces/python/XBPython.h" #include "filesystem/File.h" #include "network/WebServer.h" +#include "network/httprequesthandler/HTTPRequestHandlerUtils.h" #include "network/httprequesthandler/HTTPWebinterfaceHandler.h" #include "network/httprequesthandler/python/HTTPPythonInvoker.h" #include "network/httprequesthandler/python/HTTPPythonWsgiInvoker.h" @@ -136,8 +137,8 @@ int CHTTPPythonHandler::HandleRequest() HTTPPythonRequest* pythonRequest = new HTTPPythonRequest(); pythonRequest->connection = m_request.connection; pythonRequest->file = URIUtils::GetFileName(m_request.pathUrl); - CWebServer::GetRequestHeaderValues(m_request.connection, MHD_GET_ARGUMENT_KIND, pythonRequest->getValues); - CWebServer::GetRequestHeaderValues(m_request.connection, MHD_HEADER_KIND, pythonRequest->headerValues); + HTTPRequestHandlerUtils::GetRequestHeaderValues(m_request.connection, MHD_GET_ARGUMENT_KIND, pythonRequest->getValues); + HTTPRequestHandlerUtils::GetRequestHeaderValues(m_request.connection, MHD_HEADER_KIND, pythonRequest->headerValues); pythonRequest->method = m_request.method; pythonRequest->postValues = m_postFields; pythonRequest->requestContent = m_requestData; diff --git a/xbmc/network/httprequesthandler/HTTPRequestHandlerUtils.cpp b/xbmc/network/httprequesthandler/HTTPRequestHandlerUtils.cpp new file mode 100644 index 0000000000..35a031aa10 --- /dev/null +++ b/xbmc/network/httprequesthandler/HTTPRequestHandlerUtils.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 Team XBMC + * http://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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <map> + +#include "HTTPRequestHandlerUtils.h" +#include "utils/StringUtils.h" + +std::string HTTPRequestHandlerUtils::GetRequestHeaderValue(struct MHD_Connection *connection, enum MHD_ValueKind kind, const std::string &key) +{ + if (connection == nullptr) + return ""; + + const char* value = MHD_lookup_connection_value(connection, kind, key.c_str()); + if (value == nullptr) + return ""; + + if (StringUtils::EqualsNoCase(key, MHD_HTTP_HEADER_CONTENT_TYPE)) + { + // Work around a bug in firefox (see https://bugzilla.mozilla.org/show_bug.cgi?id=416178) + // by cutting of anything that follows a ";" in a "Content-Type" header field + std::string strValue(value); + size_t pos = strValue.find(';'); + if (pos != std::string::npos) + strValue = strValue.substr(0, pos); + + return strValue; + } + + return value; +} + +int HTTPRequestHandlerUtils::GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::map<std::string, std::string> &headerValues) +{ + if (connection == nullptr) + return -1; + + return MHD_get_connection_values(connection, kind, FillArgumentMap, &headerValues); +} + +int HTTPRequestHandlerUtils::GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::multimap<std::string, std::string> &headerValues) +{ + if (connection == nullptr) + return -1; + + return MHD_get_connection_values(connection, kind, FillArgumentMultiMap, &headerValues); +} + +bool HTTPRequestHandlerUtils::GetRequestedRanges(struct MHD_Connection *connection, uint64_t totalLength, CHttpRanges &ranges) +{ + ranges.Clear(); + + if (connection == nullptr) + return false; + + return ranges.Parse(GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE), totalLength); +} + +int HTTPRequestHandlerUtils::FillArgumentMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +{ + if (cls == nullptr || key == nullptr) + return MHD_NO; + + std::map<std::string, std::string> *arguments = reinterpret_cast<std::map<std::string, std::string>*>(cls); + arguments->insert(std::make_pair(key, value != nullptr ? value : "")); + + return MHD_YES; +} + +int HTTPRequestHandlerUtils::FillArgumentMultiMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +{ + if (cls == nullptr || key == nullptr) + return MHD_NO; + + std::multimap<std::string, std::string> *arguments = reinterpret_cast<std::multimap<std::string, std::string>*>(cls); + arguments->insert(std::make_pair(key, value != nullptr ? value : "")); + + return MHD_YES; +} diff --git a/xbmc/network/httprequesthandler/HTTPRequestHandlerUtils.h b/xbmc/network/httprequesthandler/HTTPRequestHandlerUtils.h new file mode 100644 index 0000000000..2336f4d706 --- /dev/null +++ b/xbmc/network/httprequesthandler/HTTPRequestHandlerUtils.h @@ -0,0 +1,41 @@ +#pragma once +/* + * Copyright (C) 2016 Team XBMC + * http://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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> +#include <string> + +#include "network/httprequesthandler/IHTTPRequestHandler.h" + +class HTTPRequestHandlerUtils +{ +public: + static std::string GetRequestHeaderValue(struct MHD_Connection *connection, enum MHD_ValueKind kind, const std::string &key); + static int GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::map<std::string, std::string> &headerValues); + static int GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::multimap<std::string, std::string> &headerValues); + + static bool GetRequestedRanges(struct MHD_Connection *connection, uint64_t totalLength, CHttpRanges &ranges); + +private: + HTTPRequestHandlerUtils() = delete; + + static int FillArgumentMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); + static int FillArgumentMultiMap(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); +}; diff --git a/xbmc/network/httprequesthandler/IHTTPRequestHandler.cpp b/xbmc/network/httprequesthandler/IHTTPRequestHandler.cpp index 3d3571d429..9b611ad269 100644 --- a/xbmc/network/httprequesthandler/IHTTPRequestHandler.cpp +++ b/xbmc/network/httprequesthandler/IHTTPRequestHandler.cpp @@ -19,12 +19,45 @@ */ #include <limits> +#include <utility> #include "IHTTPRequestHandler.h" #include "network/WebServer.h" +#include "network/httprequesthandler/HTTPRequestHandlerUtils.h" #include "utils/StringUtils.h" -#include <utility> +static const std::string HTTPMethodHead = "HEAD"; +static const std::string HTTPMethodGet = "GET"; +static const std::string HTTPMethodPost = "POST"; + +HTTPMethod GetHTTPMethod(const char *method) +{ + if (HTTPMethodGet.compare(method) == 0) + return GET; + if (HTTPMethodPost.compare(method) == 0) + return POST; + if (HTTPMethodHead.compare(method) == 0) + return HEAD; + + return UNKNOWN; +} + +std::string GetHTTPMethod(HTTPMethod method) +{ + switch (method) + { + case HEAD: + return HTTPMethodHead; + + case GET: + return HTTPMethodGet; + + case POST: + return HTTPMethodPost; + } + + return ""; +} IHTTPRequestHandler::IHTTPRequestHandler() : m_request(), @@ -97,7 +130,7 @@ bool IHTTPRequestHandler::GetRequestedRanges(uint64_t totalLength) if (totalLength == 0) return true; - return m_request.webserver->GetRequestedRanges(m_request.connection, totalLength, m_request.ranges); + return HTTPRequestHandlerUtils::GetRequestedRanges(m_request.connection, totalLength, m_request.ranges); } bool IHTTPRequestHandler::GetHostnameAndPort(std::string& hostname, uint16_t &port) @@ -105,7 +138,7 @@ bool IHTTPRequestHandler::GetHostnameAndPort(std::string& hostname, uint16_t &po if (m_request.webserver == NULL || m_request.connection == NULL) return false; - std::string hostnameAndPort = m_request.webserver->GetRequestHeaderValue(m_request.connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST); + std::string hostnameAndPort = HTTPRequestHandlerUtils::GetRequestHeaderValue(m_request.connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST); if (hostnameAndPort.empty()) return false; diff --git a/xbmc/network/httprequesthandler/IHTTPRequestHandler.h b/xbmc/network/httprequesthandler/IHTTPRequestHandler.h index e25dc441a5..40bd7d8c55 100644 --- a/xbmc/network/httprequesthandler/IHTTPRequestHandler.h +++ b/xbmc/network/httprequesthandler/IHTTPRequestHandler.h @@ -44,6 +44,9 @@ enum HTTPMethod HEAD }; +HTTPMethod GetHTTPMethod(const char *method); +std::string GetHTTPMethod(HTTPMethod method); + typedef enum HTTPResponseType { HTTPNone, diff --git a/xbmc/network/httprequesthandler/Makefile b/xbmc/network/httprequesthandler/Makefile index f150729e16..bcc57be5b4 100644 --- a/xbmc/network/httprequesthandler/Makefile +++ b/xbmc/network/httprequesthandler/Makefile @@ -3,6 +3,7 @@ SRCS=HTTPFileHandler.cpp \ HTTPImageTransformationHandler.cpp \ HTTPJsonRpcHandler.cpp \ HTTPPythonHandler.cpp \ + HTTPRequestHandlerUtils.cpp \ HTTPVfsHandler.cpp \ HTTPWebinterfaceAddonsHandler.cpp \ HTTPWebinterfaceHandler.cpp \ diff --git a/xbmc/network/test/TestWebServer.cpp b/xbmc/network/test/TestWebServer.cpp index f27a4030ef..09ff4136d8 100644 --- a/xbmc/network/test/TestWebServer.cpp +++ b/xbmc/network/test/TestWebServer.cpp @@ -28,6 +28,10 @@ #include "filesystem/File.h" #include "interfaces/json-rpc/JSONRPC.h" #include "network/WebServer.h" +#include "network/httprequesthandler/HTTPVfsHandler.h" +#ifdef HAS_JSONRPC +#include "network/httprequesthandler/HTTPJsonRpcHandler.h" +#endif // HAS_JSONRPC #include "settings/MediaSourceSettings.h" #include "test/TestUtils.h" #include "utils/JSONVariantParser.h" @@ -63,6 +67,8 @@ protected: SetupMediaSources(); webserver.Start(WEBSERVER_PORT, "", ""); + webserver.RegisterRequestHandler(&m_jsonRpcHandler); + webserver.RegisterRequestHandler(&m_vfsHandler); } virtual void TearDown() @@ -70,6 +76,9 @@ protected: if (webserver.IsStarted()) webserver.Stop(); + webserver.UnregisterRequestHandler(&m_vfsHandler); + webserver.UnregisterRequestHandler(&m_jsonRpcHandler); + TearDownMediaSources(); } @@ -331,6 +340,8 @@ protected: } CWebServer webserver; + CHTTPJsonRpcHandler m_jsonRpcHandler; + CHTTPVfsHandler m_vfsHandler; std::string baseUrl; std::string sourcePath; }; diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index 7788f0af03..edd7d3d1e6 100644 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -1335,6 +1335,9 @@ void CAdvancedSettings::SettingOptionsLoggingComponentsFiller(const CSetting *se #ifdef HAS_JSONRPC list.push_back(std::make_pair(g_localizeStrings.Get(675), LOGJSONRPC)); #endif +#ifdef HAS_WEB_SERVER + list.push_back(std::make_pair(g_localizeStrings.Get(681), LOGWEBSERVER)); +#endif #ifdef HAS_AIRTUNES list.push_back(std::make_pair(g_localizeStrings.Get(677), LOGAIRTUNES)); #endif |