diff --git a/android/jni/CMakeLists.txt b/android/jni/CMakeLists.txt index a56410eb16..a5e7c74da5 100644 --- a/android/jni/CMakeLists.txt +++ b/android/jni/CMakeLists.txt @@ -73,6 +73,7 @@ set( com/mapswithme/opengl/gl3stub.c com/mapswithme/platform/HttpThread.cpp com/mapswithme/platform/HttpUploader.cpp + com/mapswithme/platform/HttpUserAgent.cpp com/mapswithme/platform/GuiThread.cpp com/mapswithme/platform/Language.cpp com/mapswithme/platform/MarketingService.cpp diff --git a/android/jni/com/mapswithme/platform/HttpUserAgent.cpp b/android/jni/com/mapswithme/platform/HttpUserAgent.cpp new file mode 100644 index 0000000000..efe28bf40f --- /dev/null +++ b/android/jni/com/mapswithme/platform/HttpUserAgent.cpp @@ -0,0 +1,10 @@ +#include "platform/http_user_agent.hpp" + +namespace platform +{ +std::string HttpUserAgent::ExtractAppVersion() const +{ + //TODO(@alexzatsepin): implement app version extraction. + return {}; +} +} // platform \ No newline at end of file diff --git a/editor/user_stats.cpp b/editor/user_stats.cpp index 35d7d6261a..66e3b3c6fa 100644 --- a/editor/user_stats.cpp +++ b/editor/user_stats.cpp @@ -90,6 +90,7 @@ bool UserStatsLoader::Update(string const & userName) auto const url = kUserStatsUrl + "&name=" + UrlEncode(userName); platform::HttpClient request(url); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); if (!request.RunHttpRequest()) { diff --git a/local_ads/statistics.cpp b/local_ads/statistics.cpp index d99a367c76..95039eba7b 100644 --- a/local_ads/statistics.cpp +++ b/local_ads/statistics.cpp @@ -423,6 +423,7 @@ void Statistics::SendFileWithMetadata(MetadataKey && metadataKey, Metadata && me #endif request.SetBodyData(std::string(bytes.begin(), bytes.end()), contentType, "POST", contentEncoding); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); if (request.RunHttpRequest() && request.ErrorCode() == 200) { GetPlatform().RunTask(Platform::Thread::File, [this, metadataKey = std::move(metadataKey), diff --git a/map/cloud.cpp b/map/cloud.cpp index c72f62a6a0..45a13d3991 100644 --- a/map/cloud.cpp +++ b/map/cloud.cpp @@ -231,6 +231,7 @@ Cloud::RequestResult CloudRequestWithResult(std::string const & url, request.SetTimeout(kRequestTimeoutInSec); request.SetRawHeader("Accept", kApplicationJson); request.SetRawHeader("Authorization", BuildAuthenticationToken(accessToken)); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); request.SetBodyData(SerializeToJson(RequestDataType(args...)), kApplicationJson); if (request.RunHttpRequest() && !request.WasRedirected()) diff --git a/map/local_ads_manager.cpp b/map/local_ads_manager.cpp index b3a7074a39..65e9cca8e1 100644 --- a/map/local_ads_manager.cpp +++ b/map/local_ads_manager.cpp @@ -414,6 +414,7 @@ bool LocalAdsManager::DownloadCampaign(MwmSet::MwmId const & mwmId, std::vector< platform::HttpClient request(url); request.SetTimeout(5); // timeout in seconds + request.SetUserAgent(GetPlatform().GetAppUserAgent()); bool const success = request.RunHttpRequest() && request.ErrorCode() == 200; std::lock_guard lock(m_campaignsMutex); diff --git a/map/user.cpp b/map/user.cpp index 175f2d367a..fad56d1167 100644 --- a/map/user.cpp +++ b/map/user.cpp @@ -565,6 +565,7 @@ void User::RequestImpl(std::string const & url, BuildRequestHandler const & onBu platform::HttpClient request(url); request.SetRawHeader("Accept", kApplicationJson); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); if (onBuildRequest) onBuildRequest(request); diff --git a/partners_api/locals_api.cpp b/partners_api/locals_api.cpp index 4fd2c04ce4..c9bb3b32b2 100644 --- a/partners_api/locals_api.cpp +++ b/partners_api/locals_api.cpp @@ -84,6 +84,7 @@ bool RawApi::Get(double lat, double lon, std::string const & lang, size_t result platform::HttpClient request(ostream.str()); request.SetHttpMethod("GET"); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); if (request.RunHttpRequest()) { result = request.ServerResponse(); diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index 99b366a813..e01092cd40 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -24,6 +24,8 @@ set( http_request.hpp http_thread_callback.hpp http_uploader.hpp + http_user_agent.cpp + http_user_agent.hpp local_country_file.cpp local_country_file.hpp local_country_file_utils.cpp @@ -61,6 +63,7 @@ if(${PLATFORM_IPHONE}) http_thread_apple.mm http_client_apple.mm http_uploader_apple.mm + http_user_agent_ios.mm marketing_service_ios.mm network_policy_ios.h network_policy_ios.mm @@ -80,6 +83,7 @@ elseif(${PLATFORM_ANDROID}) else() # neither iPhone nor Android append( SRC + http_user_agent_dummy.cpp location_service.cpp location_service.hpp marketing_service_dummy.cpp @@ -93,6 +97,7 @@ else() # neither iPhone nor Android append( SRC http_client_curl.cpp + http_user_agent_dummy.cpp http_thread_qt.cpp http_thread_qt.hpp http_uploader_dummy.cpp @@ -110,6 +115,7 @@ else() # neither iPhone nor Android http_thread_apple.h http_thread_apple.mm http_uploader_apple.mm + http_user_agent_dummy.cpp marketing_service_dummy.cpp platform_mac.mm platform_unix_impl.cpp @@ -125,6 +131,7 @@ else() # neither iPhone nor Android http_thread_qt.cpp http_thread_qt.hpp http_uploader_dummy.cpp + http_user_agent_dummy.cpp marketing_service_dummy.cpp platform_linux.cpp platform_unix_impl.cpp diff --git a/platform/http_client.cpp b/platform/http_client.cpp index a7b3a03641..4561eeb5f9 100644 --- a/platform/http_client.cpp +++ b/platform/http_client.cpp @@ -95,6 +95,11 @@ void HttpClient::SetTimeout(double timeoutSec) m_timeoutSec = timeoutSec; } +void HttpClient::SetUserAgent(HttpUserAgent const & userAgent) +{ + m_headers.emplace(userAgent.Key(), userAgent.Value()); +} + string const & HttpClient::UrlRequested() const { return m_urlRequested; diff --git a/platform/http_client.hpp b/platform/http_client.hpp index 37f6096d90..7273f37753 100644 --- a/platform/http_client.hpp +++ b/platform/http_client.hpp @@ -23,6 +23,8 @@ SOFTWARE. *******************************************************************************/ #pragma once +#include "platform/http_user_agent.hpp" + #include "base/macros.hpp" #include "std/string.hpp" @@ -83,6 +85,7 @@ public: HttpClient & SetHandleRedirects(bool handle_redirects); HttpClient & SetRawHeader(string const & key, string const & value); void SetTimeout(double timeoutSec); + void SetUserAgent(HttpUserAgent const & userAgent); string const & UrlRequested() const; // @returns empty string in the case of error diff --git a/platform/http_client_apple.mm b/platform/http_client_apple.mm index 05d80b835b..4367ded321 100644 --- a/platform/http_client_apple.mm +++ b/platform/http_client_apple.mm @@ -105,16 +105,21 @@ bool HttpClient::RunHttpRequest() request.HTTPShouldHandleCookies = NO; request.HTTPMethod = @(m_httpMethod.c_str()); + NSString * userAgentStr = @"User-Agent"; + BOOL hasUserAgentHeader = NO; for (auto const & header : m_headers) { - [request setValue:@(header.second.c_str()) forHTTPHeaderField:@(header.first.c_str())]; + NSString * field = @(header.first.c_str()); + if ([field compare:userAgentStr] == NSOrderedSame) + hasUserAgentHeader = YES; + [request setValue:@(header.second.c_str()) forHTTPHeaderField:field]; } if (!m_cookies.empty()) [request setValue:[NSString stringWithUTF8String:m_cookies.c_str()] forHTTPHeaderField:@"Cookie"]; #if (TARGET_OS_IPHONE > 0) - else if (gBrowserUserAgent) - [request setValue:gBrowserUserAgent forHTTPHeaderField:@"User-Agent"]; + else if (!hasUserAgentHeader && gBrowserUserAgent) + [request setValue:gBrowserUserAgent forHTTPHeaderField:userAgentStr]; #endif // TARGET_OS_IPHONE if (!m_bodyData.empty()) diff --git a/platform/http_user_agent.cpp b/platform/http_user_agent.cpp new file mode 100644 index 0000000000..c3651a6f5c --- /dev/null +++ b/platform/http_user_agent.cpp @@ -0,0 +1,29 @@ +#include "platform/http_user_agent.hpp" + +#include + +namespace platform +{ +HttpUserAgent::HttpUserAgent() +{ + m_appVersion = ExtractAppVersion(); +} + +std::string HttpUserAgent::Key() const +{ + return "User-Agent"; +} + +std::string HttpUserAgent::Value() const +{ + std::stringstream ss; + ss << "MAPS.ME/"; +#ifdef OMIM_OS_IPHONE + ss << "iOS/"; +#elif OMIM_OS_ANDROID + ss << "Android/"; +#endif + ss << m_appVersion; + return ss.str(); +} +} // platform diff --git a/platform/http_user_agent.hpp b/platform/http_user_agent.hpp new file mode 100644 index 0000000000..842bd98b1b --- /dev/null +++ b/platform/http_user_agent.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace platform +{ +class HttpUserAgent +{ +public: + HttpUserAgent(); + std::string Key() const; + std::string Value() const; + +private: + std::string ExtractAppVersion() const; + + std::string m_appVersion; +}; +} // platform diff --git a/platform/http_user_agent_dummy.cpp b/platform/http_user_agent_dummy.cpp new file mode 100644 index 0000000000..4448ae29e5 --- /dev/null +++ b/platform/http_user_agent_dummy.cpp @@ -0,0 +1,9 @@ +#include "platform/http_user_agent.hpp" + +namespace platform +{ +std::string HttpUserAgent::ExtractAppVersion() const +{ + return {}; +} +} // platform diff --git a/platform/http_user_agent_ios.mm b/platform/http_user_agent_ios.mm new file mode 100644 index 0000000000..022b604b6c --- /dev/null +++ b/platform/http_user_agent_ios.mm @@ -0,0 +1,12 @@ +#include "platform/http_user_agent.hpp" + +#import + +namespace platform +{ +std::string HttpUserAgent::ExtractAppVersion() const +{ + NSString * str = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"]; + return std::string(str.UTF8String); +} +} // platform diff --git a/platform/platform.hpp b/platform/platform.hpp index be72573630..47aa3b9906 100644 --- a/platform/platform.hpp +++ b/platform/platform.hpp @@ -2,6 +2,7 @@ #include "platform/country_defines.hpp" #include "platform/gui_thread.hpp" +#include "platform/http_user_agent.hpp" #include "platform/marketing_service.hpp" #include "platform/secure_storage.hpp" @@ -122,6 +123,8 @@ protected: /// Platform-dependent secure storage. platform::SecureStorage m_secureStorage; + platform::HttpUserAgent m_appUserAgent; + std::unique_ptr m_guiThread; std::unique_ptr m_networkThread; @@ -280,6 +283,7 @@ public: MarketingService & GetMarketingService() { return m_marketingService; } platform::SecureStorage & GetSecureStorage() { return m_secureStorage; } + platform::HttpUserAgent & GetAppUserAgent() { return m_appUserAgent; } /// \brief Placing an executable object |task| on a queue of |thread|. Then the object will be /// executed on |thread|. diff --git a/platform/remote_file.cpp b/platform/remote_file.cpp index e8f2b0cf7c..961917af25 100644 --- a/platform/remote_file.cpp +++ b/platform/remote_file.cpp @@ -26,6 +26,7 @@ RemoteFile::Result RemoteFile::Download(std::string const & filePath) const platform::HttpClient request(m_url); request.SetTimeout(kRequestTimeoutInSec); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); if (request.RunHttpRequest()) { if (!m_allowRedirection && request.WasRedirected()) diff --git a/routing/online_cross_fetcher.cpp b/routing/online_cross_fetcher.cpp index d7854dfc22..3ad70a74ca 100644 --- a/routing/online_cross_fetcher.cpp +++ b/routing/online_cross_fetcher.cpp @@ -1,6 +1,7 @@ #include "routing/online_cross_fetcher.hpp" #include "platform/http_request.hpp" +#include "platform/platform.hpp" #include "base/logging.hpp" #include "base/string_utils.hpp" @@ -76,6 +77,7 @@ void OnlineCrossFetcher::Do() string const url = GenerateOnlineRequest(m_serverURL, MercatorBounds::ToLatLon(pointFrom), MercatorBounds::ToLatLon(pointTo)); platform::HttpClient request(url); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); LOG(LINFO, ("Check mwms by URL: ", url)); if (request.RunHttpRequest() && request.ErrorCode() == 200 && !request.WasRedirected()) diff --git a/storage/diff_scheme/diff_scheme_checker.cpp b/storage/diff_scheme/diff_scheme_checker.cpp index e1ac43044a..420f72be74 100644 --- a/storage/diff_scheme/diff_scheme_checker.cpp +++ b/storage/diff_scheme/diff_scheme_checker.cpp @@ -119,6 +119,7 @@ NameDiffInfoMap Checker::Check(LocalMapsInfo const & info) ASSERT(!body.empty(), ()); request.SetBodyData(body, "application/json"); request.SetTimeout(kTimeoutInSeconds); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); NameDiffInfoMap diffs; if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200) { diff --git a/traffic/traffic_info.cpp b/traffic/traffic_info.cpp index 121921bb7e..95a9f73368 100644 --- a/traffic/traffic_info.cpp +++ b/traffic/traffic_info.cpp @@ -1,6 +1,7 @@ #include "traffic/traffic_info.hpp" #include "platform/http_client.hpp" +#include "platform/platform.hpp" #include "routing_common/car_model.hpp" @@ -40,6 +41,7 @@ namespace bool ReadRemoteFile(string const & url, vector & contents, int & errorCode) { platform::HttpClient request(url); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); if (!request.RunHttpRequest()) { errorCode = request.ErrorCode(); @@ -451,6 +453,7 @@ TrafficInfo::ServerDataStatus TrafficInfo::ReceiveTrafficValues(string & etag, v platform::HttpClient request(url); request.LoadHeaders(true); + request.SetUserAgent(GetPlatform().GetAppUserAgent()); request.SetRawHeader("If-None-Match", etag); if (!request.RunHttpRequest() || request.ErrorCode() != 200) diff --git a/xcode/platform/platform.xcodeproj/project.pbxproj b/xcode/platform/platform.xcodeproj/project.pbxproj index b0087c0e83..33ac2261cc 100644 --- a/xcode/platform/platform.xcodeproj/project.pbxproj +++ b/xcode/platform/platform.xcodeproj/project.pbxproj @@ -28,6 +28,9 @@ 4564FA7E2094978D0043CCFB /* remote_file.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4564FA7C2094978C0043CCFB /* remote_file.hpp */; }; 4564FA7F2094978D0043CCFB /* remote_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4564FA7D2094978D0043CCFB /* remote_file.cpp */; }; 5661A5CC20DD57DA00C6B1D1 /* async_gui_thread.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 5661A5CB20DD57DA00C6B1D1 /* async_gui_thread.hpp */; }; + 45D7ADBA210F48E500160DE3 /* http_user_agent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45D7ADB7210F48E400160DE3 /* http_user_agent.cpp */; }; + 45D7ADBB210F48E500160DE3 /* http_user_agent_ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 45D7ADB8210F48E400160DE3 /* http_user_agent_ios.mm */; }; + 45D7ADBC210F48E500160DE3 /* http_user_agent.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 45D7ADB9210F48E400160DE3 /* http_user_agent.hpp */; }; 56EB1EDC1C6B6E6C0022D831 /* file_logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56EB1ED81C6B6E6C0022D831 /* file_logging.cpp */; }; 56EB1EDD1C6B6E6C0022D831 /* file_logging.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56EB1ED91C6B6E6C0022D831 /* file_logging.hpp */; }; 56EB1EDE1C6B6E6C0022D831 /* mwm_traits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56EB1EDA1C6B6E6C0022D831 /* mwm_traits.cpp */; }; @@ -143,6 +146,9 @@ 4564FA7C2094978C0043CCFB /* remote_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = remote_file.hpp; sourceTree = ""; }; 4564FA7D2094978D0043CCFB /* remote_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = remote_file.cpp; sourceTree = ""; }; 5661A5CB20DD57DA00C6B1D1 /* async_gui_thread.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = async_gui_thread.hpp; sourceTree = ""; }; + 45D7ADB7210F48E400160DE3 /* http_user_agent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = http_user_agent.cpp; sourceTree = ""; }; + 45D7ADB8210F48E400160DE3 /* http_user_agent_ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = http_user_agent_ios.mm; sourceTree = ""; }; + 45D7ADB9210F48E400160DE3 /* http_user_agent.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = http_user_agent.hpp; sourceTree = ""; }; 56EB1ED81C6B6E6C0022D831 /* file_logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_logging.cpp; sourceTree = ""; }; 56EB1ED91C6B6E6C0022D831 /* file_logging.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = file_logging.hpp; sourceTree = ""; }; 56EB1EDA1C6B6E6C0022D831 /* mwm_traits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mwm_traits.cpp; sourceTree = ""; }; @@ -380,6 +386,9 @@ 6753438F1A3F5D5A00A0A8C3 /* http_thread_callback.hpp */, 3D318A042021DD8A007B2607 /* http_uploader_apple.mm */, 3D318A052021DD8B007B2607 /* http_uploader.hpp */, + 45D7ADB8210F48E400160DE3 /* http_user_agent_ios.mm */, + 45D7ADB7210F48E400160DE3 /* http_user_agent.cpp */, + 45D7ADB9210F48E400160DE3 /* http_user_agent.hpp */, 674125041B4C00CC00A3E828 /* local_country_file_utils.cpp */, 674125051B4C00CC00A3E828 /* local_country_file_utils.hpp */, 674125061B4C00CC00A3E828 /* local_country_file.cpp */, @@ -496,6 +505,7 @@ 4564FA7E2094978D0043CCFB /* remote_file.hpp in Headers */, 3D78156F1F3A14090068B6AC /* gui_thread.hpp in Headers */, 451E32A21F73A8B000964C9F /* secure_storage.hpp in Headers */, + 45D7ADBC210F48E500160DE3 /* http_user_agent.hpp in Headers */, 674125091B4C00CC00A3E828 /* country_defines.hpp in Headers */, 675343CD1A3F5D5A00A0A8C3 /* platform.hpp in Headers */, 6741250F1B4C00CC00A3E828 /* local_country_file.hpp in Headers */, @@ -657,6 +667,7 @@ 3D318A062021DD8B007B2607 /* http_uploader_apple.mm in Sources */, 3D78156E1F3A14090068B6AC /* gui_thread_apple.mm in Sources */, 67247FFD1C60BD6500EDE56A /* writable_dir_changer.cpp in Sources */, + 45D7ADBA210F48E500160DE3 /* http_user_agent.cpp in Sources */, 34513AFB1DCB37C100471BDA /* marketing_service.cpp in Sources */, 6741250C1B4C00CC00A3E828 /* local_country_file_utils.cpp in Sources */, 3D97F64B1D9C05E800380945 /* http_client.cpp in Sources */, @@ -673,6 +684,7 @@ 675343B31A3F5D5A00A0A8C3 /* chunks_download_strategy.cpp in Sources */, 34513AFA1DCB37C100471BDA /* marketing_service_ios.mm in Sources */, 675343C01A3F5D5A00A0A8C3 /* location_service.cpp in Sources */, + 45D7ADBB210F48E500160DE3 /* http_user_agent_ios.mm in Sources */, 675343D31A3F5D5A00A0A8C3 /* settings.cpp in Sources */, 675343CE1A3F5D5A00A0A8C3 /* preferred_languages.cpp in Sources */, 675343B91A3F5D5A00A0A8C3 /* http_thread_apple.mm in Sources */,