diff --git a/map/traffic_manager.cpp b/map/traffic_manager.cpp index 4249316603..4e004245e3 100644 --- a/map/traffic_manager.cpp +++ b/map/traffic_manager.cpp @@ -227,9 +227,13 @@ void TrafficManager::ThreadRoutine() { for (auto const & mwm : mwms) { + auto const & mwmInfo = mwm.GetInfo(); + if (!mwmInfo) + continue; + traffic::TrafficInfo info(mwm, m_currentDataVersion); - if (info.ReceiveTrafficData()) + if (info.ReceiveTrafficData(m_trafficETags[mwm])) { OnTrafficDataResponse(move(info)); } diff --git a/map/traffic_manager.hpp b/map/traffic_manager.hpp index cf53d3091f..82a913535f 100644 --- a/map/traffic_manager.hpp +++ b/map/traffic_manager.hpp @@ -20,6 +20,7 @@ #include "std/map.hpp" #include "std/mutex.hpp" #include "std/set.hpp" +#include "std/string.hpp" #include "std/vector.hpp" namespace df @@ -141,6 +142,10 @@ private: vector m_lastMwmsByRect; set m_activeMwms; + // The ETag or entity tag is part of HTTP, the protocol for the World Wide Web. + // It is one of several mechanisms that HTTP provides for web cache validation, + // which allows a client to make conditional requests. + map m_trafficETags; vector m_requestedMwms; mutex m_mutex; diff --git a/traffic/traffic_info.cpp b/traffic/traffic_info.cpp index d3346adb99..23138ba085 100644 --- a/traffic/traffic_info.cpp +++ b/traffic/traffic_info.cpp @@ -75,6 +75,8 @@ string MakeRemoteURL(string const & name, uint64_t version) ss << UrlEncode(name) << TRAFFIC_FILE_EXTENSION; return ss.str(); } + +char const kETag[] = "Etag"; } // namespace // TrafficInfo::RoadSegmentId ----------------------------------------------------------------- @@ -133,10 +135,10 @@ void TrafficInfo::SetTrafficKeysForTesting(vector const & keys) m_availability = Availability::IsAvailable; } -bool TrafficInfo::ReceiveTrafficData() +bool TrafficInfo::ReceiveTrafficData(string & etag) { vector values; - if (!ReceiveTrafficValues(values)) + if (!ReceiveTrafficValues(etag, values)) return false; return UpdateTrafficData(values); @@ -396,48 +398,28 @@ bool TrafficInfo::ReceiveTrafficKeys() return true; } -bool TrafficInfo::ReceiveTrafficValues(vector & values) +bool TrafficInfo::ReceiveTrafficValues(string & etag, vector & values) { auto const & info = m_mwmId.GetInfo(); if (!info) return false; - string const url = MakeRemoteURL(info->GetCountryName(), info->GetVersion()); + uint64_t const version = info->GetVersion(); + string const url = MakeRemoteURL(info->GetCountryName(), version); if (url.empty()) return false; - vector contents; - int errorCode; - if (!ReadRemoteFile(url, contents, errorCode)) - { - if (errorCode == 404) - { - string const result(reinterpret_cast(contents.data()), contents.size()); - - int64_t version = 0; - strings::to_int64(result.c_str(), version); - - if (version > info->GetVersion() && version <= m_currentDataVersion) - m_availability = Availability::ExpiredData; - else if (version > m_currentDataVersion) - m_availability = Availability::ExpiredApp; - else - m_availability = Availability::NoData; - } - else - { - m_availability = Availability::Unknown; - - alohalytics::LogEvent( - "$TrafficNetworkError", - alohalytics::TStringMap({{"code", strings::to_string(errorCode)}})); - } - return false; - } + platform::HttpClient request(url); + request.LoadHeaders(true); + request.SetRawHeader("If-None-Match", etag); + if (!request.RunHttpRequest() || request.ErrorCode() != 200) + return ProcessFailure(request, version); try { + string const & response = request.ServerResponse(); + vector contents(response.cbegin(), response.cend()); DeserializeTrafficValues(contents, values); } catch (Reader::Exception const & e) @@ -453,6 +435,12 @@ bool TrafficInfo::ReceiveTrafficValues(vector & values) return false; } + // Update ETag for this MWM. + auto const & headers = request.GetHeaders(); + auto const it = headers.find(kETag); + if (it != headers.end()) + etag = it->second; + m_availability = Availability::IsAvailable; return true; } @@ -480,13 +468,45 @@ bool TrafficInfo::UpdateTrafficData(vector const & values) return true; } +bool TrafficInfo::ProcessFailure(platform::HttpClient const & request, uint64_t const mwmVersion) +{ + switch (request.ErrorCode()) + { + case 404: /* Not Found */ + { + int64_t version = 0; + strings::to_int64(request.ServerResponse().c_str(), version); + + if (version > mwmVersion && version <= m_currentDataVersion) + m_availability = Availability::ExpiredData; + else if (version > m_currentDataVersion) + m_availability = Availability::ExpiredApp; + else + m_availability = Availability::NoData; + return false; + } + case 304: /* Not Modified */ + { + m_availability = Availability::IsAvailable; + return true; + } + } + + m_availability = Availability::Unknown; + alohalytics::LogEvent( + "$TrafficNetworkError", + alohalytics::TStringMap({{"code", strings::to_string(request.ErrorCode())}})); + + return false; +} + string DebugPrint(TrafficInfo::RoadSegmentId const & id) { string const dir = - id.m_dir == TrafficInfo::RoadSegmentId::kForwardDirection ? "Forward" : "Backward"; + id.m_dir == TrafficInfo::RoadSegmentId::kForwardDirection ? "Forward" : "Backward"; ostringstream oss; oss << "RoadSegmentId [" - << " fid = " << id.m_fid << " idx = " << id.m_idx << " dir = " << dir << " ]"; + << " fid = " << id.m_fid << " idx = " << id.m_idx << " dir = " << dir << " ]"; return oss.str(); } } // namespace traffic diff --git a/traffic/traffic_info.hpp b/traffic/traffic_info.hpp index 4c0ace5d21..23cbb268f0 100644 --- a/traffic/traffic_info.hpp +++ b/traffic/traffic_info.hpp @@ -9,6 +9,11 @@ #include "std/shared_ptr.hpp" #include "std/vector.hpp" +namespace platform +{ +class HttpClient; +} + namespace traffic { // This class is responsible for providing the real-time @@ -74,10 +79,13 @@ public: static TrafficInfo BuildForTesting(Coloring && coloring); void SetTrafficKeysForTesting(vector const & keys); - // Fetches the latest traffic data from the server and updates the coloring. + // Fetches the latest traffic data from the server and updates the coloring and ETag. // Construct the url by passing an MwmId. + // The ETag or entity tag is part of HTTP, the protocol for the World Wide Web. + // It is one of several mechanisms that HTTP provides for web cache validation, + // which allows a client to make conditional requests. // *NOTE* This method must not be called on the UI thread. - bool ReceiveTrafficData(); + bool ReceiveTrafficData(string & etag); // Returns the latest known speed group by a feature segment's id // or SpeedGroup::Unknown if there is no information about the segment. @@ -119,11 +127,13 @@ private: // Returns true and updates m_coloring if the values are read successfully and // their number is equal to the number of keys. // Otherwise, returns false and does not change m_coloring. - bool ReceiveTrafficValues(vector & values); + bool ReceiveTrafficValues(string & etag, vector & values); // Updates the coloring and changes the availability status if needed. bool UpdateTrafficData(vector const & values); + bool ProcessFailure(platform::HttpClient const & request, uint64_t const mwmVersion); + // The mapping from feature segments to speed groups (see speed_groups.hpp). Coloring m_coloring; diff --git a/traffic/traffic_tests/traffic_info_test.cpp b/traffic/traffic_tests/traffic_info_test.cpp index 45f962c65a..d804824580 100644 --- a/traffic/traffic_tests/traffic_info_test.cpp +++ b/traffic/traffic_tests/traffic_info_test.cpp @@ -44,21 +44,24 @@ UNIT_TEST(TrafficInfo_RemoteFile) auto const & r = mwmSet.Register(platform::LocalCountryFile::MakeForTesting("traffic_data_test")); TrafficInfo trafficInfo(r.first, r.first.GetInfo()->GetVersion()); - TEST(trafficInfo.ReceiveTrafficData(), ()); + string etag; + TEST(trafficInfo.ReceiveTrafficData(etag), ()); } { TestMwmSet mwmSet; auto const & r = mwmSet.Register(platform::LocalCountryFile::MakeForTesting("traffic_data_test2")); TrafficInfo trafficInfo(r.first, r.first.GetInfo()->GetVersion()); - TEST(!trafficInfo.ReceiveTrafficData(), ()); + string etag; + TEST(!trafficInfo.ReceiveTrafficData(etag), ()); } { TestMwmSet mwmSet; auto const & r = mwmSet.Register(platform::LocalCountryFile::MakeForTesting("traffic_data_test", 101010)); TrafficInfo trafficInfo(r.first, r.first.GetInfo()->GetVersion()); - TEST(trafficInfo.ReceiveTrafficData(), ()); + string etag; + TEST(trafficInfo.ReceiveTrafficData(etag), ()); } } diff --git a/xcode/traffic/traffic.xcodeproj/project.pbxproj b/xcode/traffic/traffic.xcodeproj/project.pbxproj index b7a473d591..88b14cb0bb 100644 --- a/xcode/traffic/traffic.xcodeproj/project.pbxproj +++ b/xcode/traffic/traffic.xcodeproj/project.pbxproj @@ -12,6 +12,14 @@ 672292AB1DE2F673005BA3A7 /* libalohalitics.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 672292AA1DE2F673005BA3A7 /* libalohalitics.a */; }; 672292AD1DE2F683005BA3A7 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 672292AC1DE2F683005BA3A7 /* libz.tbd */; }; 672292AF1DE2FF0F005BA3A7 /* libindexer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 672292AE1DE2FF0F005BA3A7 /* libindexer.a */; }; + 674231CD1DFAB03800913FEB /* libgeometry.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231CC1DFAB03800913FEB /* libgeometry.a */; }; + 674231D31DFAB04C00913FEB /* libeditor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231CE1DFAB04C00913FEB /* libeditor.a */; }; + 674231D41DFAB04C00913FEB /* liboauthcpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231CF1DFAB04C00913FEB /* liboauthcpp.a */; }; + 674231D51DFAB04C00913FEB /* libopening_hours.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231D01DFAB04C00913FEB /* libopening_hours.a */; }; + 674231D61DFAB04C00913FEB /* libprotobuf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231D11DFAB04C00913FEB /* libprotobuf.a */; }; + 674231D71DFAB04C00913FEB /* libpugixml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231D21DFAB04C00913FEB /* libpugixml.a */; }; + 674231D91DFAB06100913FEB /* libjansson.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231D81DFAB06100913FEB /* libjansson.a */; }; + 674231DB1DFAB08C00913FEB /* librouting.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674231DA1DFAB08C00913FEB /* librouting.a */; }; 67BECB5F1DDA44FD00FC4E99 /* speed_groups.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67BECB5B1DDA44FD00FC4E99 /* speed_groups.cpp */; }; 67BECB601DDA44FD00FC4E99 /* speed_groups.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67BECB5C1DDA44FD00FC4E99 /* speed_groups.hpp */; }; 67BECB611DDA44FD00FC4E99 /* traffic_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67BECB5D1DDA44FD00FC4E99 /* traffic_info.cpp */; }; @@ -30,6 +38,14 @@ 672292AA1DE2F673005BA3A7 /* libalohalitics.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libalohalitics.a; path = "../../../omim-build/xcode/Debug/libalohalitics.a"; sourceTree = ""; }; 672292AC1DE2F683005BA3A7 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 672292AE1DE2FF0F005BA3A7 /* libindexer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libindexer.a; path = "../../../omim-build/xcode/Debug/libindexer.a"; sourceTree = ""; }; + 674231CC1DFAB03800913FEB /* libgeometry.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgeometry.a; path = "../../../omim-build/xcode/Debug/libgeometry.a"; sourceTree = ""; }; + 674231CE1DFAB04C00913FEB /* libeditor.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libeditor.a; path = "../../../omim-build/xcode/Debug/libeditor.a"; sourceTree = ""; }; + 674231CF1DFAB04C00913FEB /* liboauthcpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liboauthcpp.a; path = "../../../omim-build/xcode/Debug/liboauthcpp.a"; sourceTree = ""; }; + 674231D01DFAB04C00913FEB /* libopening_hours.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopening_hours.a; path = "../../../omim-build/xcode/Debug/libopening_hours.a"; sourceTree = ""; }; + 674231D11DFAB04C00913FEB /* libprotobuf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libprotobuf.a; path = "../../../omim-build/xcode/Debug/libprotobuf.a"; sourceTree = ""; }; + 674231D21DFAB04C00913FEB /* libpugixml.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpugixml.a; path = "../../../omim-build/xcode/Debug/libpugixml.a"; sourceTree = ""; }; + 674231D81DFAB06100913FEB /* libjansson.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libjansson.a; path = "../../../omim-build/xcode/Debug/libjansson.a"; sourceTree = ""; }; + 674231DA1DFAB08C00913FEB /* librouting.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = librouting.a; path = "../../../omim-build/xcode/Debug/librouting.a"; sourceTree = ""; }; 67BECB4A1DDA43AF00FC4E99 /* libtraffic.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libtraffic.a; sourceTree = BUILT_PRODUCTS_DIR; }; 67BECB581DDA43FB00FC4E99 /* common-debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "common-debug.xcconfig"; path = "../common-debug.xcconfig"; sourceTree = ""; }; 67BECB591DDA440100FC4E99 /* common-release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "common-release.xcconfig"; path = "../common-release.xcconfig"; sourceTree = ""; }; @@ -58,6 +74,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 674231DB1DFAB08C00913FEB /* librouting.a in Frameworks */, + 674231D91DFAB06100913FEB /* libjansson.a in Frameworks */, + 674231D31DFAB04C00913FEB /* libeditor.a in Frameworks */, + 674231D41DFAB04C00913FEB /* liboauthcpp.a in Frameworks */, + 674231D51DFAB04C00913FEB /* libopening_hours.a in Frameworks */, + 674231D61DFAB04C00913FEB /* libprotobuf.a in Frameworks */, + 674231D71DFAB04C00913FEB /* libpugixml.a in Frameworks */, + 674231CD1DFAB03800913FEB /* libgeometry.a in Frameworks */, 672292AF1DE2FF0F005BA3A7 /* libindexer.a in Frameworks */, 672292AD1DE2F683005BA3A7 /* libz.tbd in Frameworks */, 672292AB1DE2F673005BA3A7 /* libalohalitics.a in Frameworks */, @@ -120,6 +144,14 @@ 67BECB831DDA474400FC4E99 /* Frameworks */ = { isa = PBXGroup; children = ( + 674231DA1DFAB08C00913FEB /* librouting.a */, + 674231D81DFAB06100913FEB /* libjansson.a */, + 674231CE1DFAB04C00913FEB /* libeditor.a */, + 674231CF1DFAB04C00913FEB /* liboauthcpp.a */, + 674231D01DFAB04C00913FEB /* libopening_hours.a */, + 674231D11DFAB04C00913FEB /* libprotobuf.a */, + 674231D21DFAB04C00913FEB /* libpugixml.a */, + 674231CC1DFAB03800913FEB /* libgeometry.a */, 672292AE1DE2FF0F005BA3A7 /* libindexer.a */, 672292AC1DE2F683005BA3A7 /* libz.tbd */, 672292AA1DE2F673005BA3A7 /* libalohalitics.a */,