tdf#107038 Poco::Timestamp replacement with std::chrono
Added functions to get file timestamp and to convert
chrono timestamp in ISO8601 fraction format and some
test cases.
Change-Id: I58961a31f7262b367cff9f33cffdec7571a2f8f7
diff --git a/common/Log.hpp b/common/Log.hpp
index 5b7e77f..fc04080 100644
--- a/common/Log.hpp
+++ b/common/Log.hpp
@@ -38,6 +38,12 @@
return os;
}
inline std::ostream& operator<< (std::ostream& os, const std::chrono::system_clock::time_point& ts)
{
os << Util::getIso8601FracformatTime(ts);
return os;
}
namespace Log
{
/// Initialize the logging system.
@@ -201,6 +207,16 @@
return lhs;
}
inline StreamLogger& operator<<(StreamLogger& lhs, const std::chrono::system_clock::time_point& rhs)
{
if (lhs.enabled())
{
lhs.getStream() << Util::getIso8601FracformatTime(rhs);
}
return lhs;
}
inline void operator<<(StreamLogger& lhs, const _end_marker&)
{
(void)end;
diff --git a/common/Util.cpp b/common/Util.cpp
index 9cba4a2..00454f0 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -762,11 +762,11 @@
std::string getHttpTimeNow()
{
char time_now[50];
char time_now[64];
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
std::tm now_tm = *std::gmtime(&now_c);
strftime(time_now, 50, "%a, %d %b %Y %T", &now_tm);
strftime(time_now, sizeof(time_now), "%a, %d %b %Y %T", &now_tm);
return time_now;
}
@@ -784,6 +784,62 @@
}
return std::string::npos;
}
std::chrono::system_clock::time_point getFileTimestamp(std::string str_path)
{
struct stat file;
stat(str_path.c_str(), &file);
std::chrono::seconds ns{file.st_mtime};
std::chrono::system_clock::time_point mod_time_point{ns};
return mod_time_point;
}
std::string getIso8601FracformatTime(std::chrono::system_clock::time_point time){
char time_modified[64];
std::time_t lastModified_us_t = std::chrono::high_resolution_clock::to_time_t(time);
std::tm lastModified_tm = *std::gmtime(&lastModified_us_t);
strftime(time_modified, sizeof(time_modified), "%FT%T.", &lastModified_tm);
auto lastModified_s = std::chrono::time_point_cast<std::chrono::seconds>(time);
std::ostringstream oss;
oss << std::setfill('0')
<< time_modified
<< std::setw(6)
<< (time - lastModified_s).count() / 1000
<< "Z";
return oss.str();
}
std::chrono::system_clock::time_point iso8601ToTimestamp(const std::string& iso8601Time, const std::string& logName)
{
std::chrono::system_clock::time_point timestamp;
std::tm tm{};
std::istringstream iss(iso8601Time);
if (!(iss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S")))
{
LOG_WRN(logName << " [" << iso8601Time << "] is in invalid format."
<< "Returning " << timestamp.time_since_epoch().count());
return timestamp;
}
timestamp += std::chrono::seconds(timegm(&tm));
if (iss.eof())
return timestamp;
double us;
if (iss.peek() != '.' || !(iss >> us))
{
LOG_WRN(logName << " [" << iso8601Time << "] is in invalid format."
<< ". Returning " << timestamp.time_since_epoch().count());
return timestamp;
}
std::size_t seconds_us = us * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num;
timestamp += std::chrono::system_clock::duration(seconds_us);
return timestamp;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util.hpp b/common/Util.hpp
index e16479f..52b960c 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -924,6 +924,16 @@
//// Return current time in HTTP format.
std::string getHttpTimeNow();
//// Return timestamp of file
std::chrono::system_clock::time_point getFileTimestamp(std::string str_path);
//// Return time in ISO8061 fraction format
std::string getIso8601FracformatTime(std::chrono::system_clock::time_point time);
//// Convert time from ISO8061 fraction format
std::chrono::system_clock::time_point iso8601ToTimestamp(const std::string& iso8601Time, const std::string& logName);
} // end namespace Util
#endif
diff --git a/test/TileCacheTests.cpp b/test/TileCacheTests.cpp
index ef8c1e4..48f6641 100644
--- a/test/TileCacheTests.cpp
+++ b/test/TileCacheTests.cpp
@@ -194,7 +194,7 @@
// Create TileCache and pretend the file was modified as recently as
// now, so it discards the cached data.
TileCache tc("doc.ods", Poco::Timestamp());
TileCache tc("doc.ods", std::chrono::system_clock::time_point());
int part = 0;
int width = 256;
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index 3f5f028..35c6faf 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -55,6 +55,7 @@
void testAuthorization();
void testJson();
void testAnonymization();
void testTime();
};
void WhiteBoxTests::testLOOLProtocolFunctions()
@@ -731,6 +732,34 @@
CPPUNIT_ASSERT_EQUAL(urlAnonymized3, Util::anonymizeUrl(fileUrl, nAnonymizationSalt));
}
void WhiteBoxTests::testTime()
{
std::ostringstream oss;
std::chrono::system_clock::time_point t(std::chrono::nanoseconds(1567444337874777375));
CPPUNIT_ASSERT_EQUAL(std::string("2019-09-02T17:12:17.874777Z"), Util::getIso8601FracformatTime(t));
t = std::chrono::system_clock::time_point(std::chrono::nanoseconds(0));
CPPUNIT_ASSERT_EQUAL(std::string("1970-01-01T00:00:00.000000Z"), Util::getIso8601FracformatTime(t));
t = Util::iso8601ToTimestamp("2019-09-02T17:12:17.874777Z", "LastModifiedTime");
oss << t.time_since_epoch().count();
CPPUNIT_ASSERT_EQUAL(std::string("1567444337874777000"), oss.str());
t = Util::iso8601ToTimestamp("1970-01-01T00:00:00.000000Z", "LastModifiedTime");
oss << t.time_since_epoch().count();
CPPUNIT_ASSERT_EQUAL(std::string("0"), oss.str());
t = std::chrono::system_clock::now();
uint64_t t_in_micros = (t.time_since_epoch().count() / 1000) * 1000;
oss << t_in_micros;
std::string first = oss.str();
std::string s = Util::getIso8601FracformatTime(t);
t = Util::iso8601ToTimestamp(s, "LastModifiedTime");
oss << t.time_since_epoch().count();
CPPUNIT_ASSERT_EQUAL(first, oss.str());
}
CPPUNIT_TEST_SUITE_REGISTRATION(WhiteBoxTests);
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 026bdda..046f916 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -69,7 +69,7 @@
void sendLastModificationTime(const std::shared_ptr<Session>& session,
DocumentBroker* documentBroker,
const Poco::Timestamp& documentLastModifiedTime)
const std::chrono::system_clock::time_point& documentLastModifiedTime)
{
if (!session)
return;
@@ -679,7 +679,7 @@
{
// Check if document has been modified by some external action
LOG_TRC("Document modified time: " << fileInfo.getModifiedTime());
static const Poco::Timestamp Zero(Poco::Timestamp::fromEpochTime(0));
static const std::chrono::system_clock::time_point Zero;
if (_documentLastModifiedTime != Zero &&
fileInfo.getModifiedTime() != Zero &&
_documentLastModifiedTime != fileInfo.getModifiedTime())
@@ -776,8 +776,8 @@
_filename = fileInfo.getFilename();
// Use the local temp file's timestamp.
_lastFileModifiedTime = templateSource.empty() ? Poco::File(_storage->getRootFilePath()).getLastModified() :
Poco::Timestamp::fromEpochTime(0);
_lastFileModifiedTime = templateSource.empty() ? Util::getFileTimestamp(_storage->getRootFilePath()) :
std::chrono::system_clock::time_point();
bool dontUseCache = false;
#if MOBILEAPP
@@ -894,12 +894,14 @@
const std::string uriAnonym = LOOLWSD::anonymizeUrl(uri);
// If the file timestamp hasn't changed, skip saving.
const Poco::Timestamp newFileModifiedTime = Poco::File(_storage->getRootFilePath()).getLastModified();
const std::chrono::system_clock::time_point newFileModifiedTime = Util::getFileTimestamp(_storage->getRootFilePath());
if (!isSaveAs && newFileModifiedTime == _lastFileModifiedTime && !isRename)
{
// Nothing to do.
auto timeInSec = std::chrono::duration_cast<std::chrono::seconds>
(std::chrono::system_clock::now() - _lastFileModifiedTime);
LOG_DBG("Skipping unnecessary saving to URI [" << uriAnonym << "] with docKey [" << _docKey <<
"]. File last modified " << _lastFileModifiedTime.elapsed() / 1000000 << " seconds ago.");
"]. File last modified " << timeInSec.count() << " seconds ago.");
_poll->wakeup();
return true;
}
@@ -1108,7 +1110,7 @@
if (_sessions.find(sessionId) != _sessions.end())
{
// Invalidate the timestamp to force persisting.
_lastFileModifiedTime = Poco::Timestamp::fromEpochTime(0);
_lastFileModifiedTime = std::chrono::system_clock::time_point();
// We do not want save to terminate editing mode if we are in edit mode now
@@ -1578,7 +1580,7 @@
{
std::ostringstream oss;
oss << "HTTP/1.1 200 OK\r\n"
<< "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
<< "Last-Modified: " << Util::getHttpTimeNow() << "\r\n"
<< "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
<< "Content-Length: " << saved->length() << "\r\n"
<< "Content-Type: application/octet-stream\r\n"
@@ -1601,7 +1603,7 @@
// Bad request.
std::ostringstream oss;
oss << "HTTP/1.1 400\r\n"
<< "Date: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
<< "Date: " << Util::getHttpTimeNow() << "\r\n"
<< "User-Agent: LOOLWSD WOPI Agent\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n"
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index fe370cd..d3bdaa6 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -449,10 +449,10 @@
std::chrono::steady_clock::time_point _lastSaveResponseTime;
/// The document's last-modified time on storage.
Poco::Timestamp _documentLastModifiedTime;
std::chrono::system_clock::time_point _documentLastModifiedTime;
/// The jailed file last-modified time.
Poco::Timestamp _lastFileModifiedTime;
std::chrono::system_clock::time_point _lastFileModifiedTime;
/// All session of this DocBroker by ID.
std::map<std::string, std::shared_ptr<ClientSession> > _sessions;
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 0057f8c..b7fef17 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -18,8 +18,6 @@
#include <iconv.h>
#include <string>
#include <Poco/DateTime.h>
#include <Poco/DateTimeParser.h>
#include <Poco/Exception.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
@@ -41,7 +39,6 @@
#endif
#include <Poco/StreamCopier.h>
#include <Poco/Timestamp.h>
#include <Poco/URI.h>
#include "Auth.hpp"
@@ -261,9 +258,10 @@
const Poco::Path path = Poco::Path(getUri().getPath());
LOG_DBG("Getting info for local uri [" << LOOLWSD::anonymizeUrl(getUri().toString()) << "], path [" << LOOLWSD::anonymizeUrl(path.toString()) << "].");
std::string str_path = path.toString();
const auto& filename = path.getFileName();
const Poco::File file = Poco::File(path);
const Poco::Timestamp lastModified = file.getLastModified();
std::chrono::high_resolution_clock::time_point lastModified = Util::getFileTimestamp(str_path);
const size_t size = file.getSize();
setFileInfo(FileInfo({filename, "localhost", lastModified, size}));
@@ -350,7 +348,9 @@
// update its fileinfo object. This is used later to check if someone else changed the
// document while we are/were editing it
getFileInfo().setModifiedTime(Poco::File(getUri().getPath()).getLastModified());
const Poco::Path path = Poco::Path(getUri().getPath());
std::string str_path = path.toString();
getFileInfo().setModifiedTime(Util::getFileTimestamp(str_path));
LOG_TRC("New FileInfo modified time in storage " << getFileInfo().getModifiedTime());
}
catch (const Poco::Exception& exc)
@@ -397,28 +397,6 @@
#endif
}
Poco::Timestamp iso8601ToTimestamp(const std::string& iso8601Time, const std::string& name)
{
Poco::Timestamp timestamp = Poco::Timestamp::fromEpochTime(0);
try
{
if (!iso8601Time.empty())
{
int timeZoneDifferential;
Poco::DateTime dateTime;
Poco::DateTimeParser::parse(Poco::DateTimeFormat::ISO8601_FRAC_FORMAT, iso8601Time, dateTime, timeZoneDifferential);
timestamp = dateTime.timestamp();
}
}
catch (const Poco::SyntaxException& exc)
{
LOG_WRN(name << " [" << iso8601Time << "] is in invalid format: " << exc.displayText() <<
(exc.nested() ? " (" + exc.nested()->displayText() + ")" : "") << ". Returning " << timestamp);
}
return timestamp;
}
} // anonymous namespace
std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Authorization& auth)
@@ -610,7 +588,7 @@
throw UnauthorizedRequestException("Access denied. WOPI::CheckFileInfo failed on: " + uriAnonym);
}
const Poco::Timestamp modifiedTime = iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime");
const std::chrono::system_clock::time_point modifiedTime = Util::iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime");
setFileInfo(FileInfo({filename, ownerId, modifiedTime, size}));
return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo(
@@ -745,9 +723,7 @@
if (!getForceSave())
{
// Request WOPI host to not overwrite if timestamps mismatch
request.set("X-LOOL-WOPI-Timestamp",
Poco::DateTimeFormatter::format(Poco::DateTime(getFileInfo().getModifiedTime()),
Poco::DateTimeFormat::ISO8601_FRAC_FORMAT));
request.set("X-LOOL-WOPI-Timestamp", Util::getIso8601FracformatTime(getFileInfo().getModifiedTime()));
}
}
else
@@ -855,7 +831,7 @@
{
const std::string lastModifiedTime = JsonUtil::getJSONValue<std::string>(object, "LastModifiedTime");
LOG_TRC(wopiLog << " returns LastModifiedTime [" << lastModifiedTime << "].");
getFileInfo().setModifiedTime(iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime"));
getFileInfo().setModifiedTime(Util::iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime"));
if (isSaveAs || isRename)
{
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index db37087..06887c6 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -34,7 +34,7 @@
public:
FileInfo(const std::string& filename,
const std::string& ownerId,
const Poco::Timestamp& modifiedTime,
const std::chrono::system_clock::time_point& modifiedTime,
size_t /*size*/)
: _filename(filename),
_ownerId(ownerId),
@@ -52,14 +52,14 @@
const std::string& getOwnerId() const { return _ownerId; }
void setModifiedTime(const Poco::Timestamp& modifiedTime) { _modifiedTime = modifiedTime; }
void setModifiedTime(const std::chrono::system_clock::time_point& modifiedTime) { _modifiedTime = modifiedTime; }
const Poco::Timestamp& getModifiedTime() const { return _modifiedTime; }
const std::chrono::system_clock::time_point& getModifiedTime() const { return _modifiedTime; }
private:
std::string _filename;
std::string _ownerId;
Poco::Timestamp _modifiedTime;
std::chrono::system_clock::time_point _modifiedTime;
};
class SaveResult
@@ -124,7 +124,7 @@
_uri(uri),
_localStorePath(localStorePath),
_jailPath(jailPath),
_fileInfo("", "lool", Poco::Timestamp::fromEpochTime(0), 0),
_fileInfo("", "lool", std::chrono::system_clock::time_point(), 0),
_isLoaded(false),
_forceSave(false),
_isUserModified(false),
diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp
index a4f16a5..c0d2705 100644
--- a/wsd/TileCache.cpp
+++ b/wsd/TileCache.cpp
@@ -27,7 +27,6 @@
#include <Poco/File.h>
#include <Poco/Path.h>
#include <Poco/StringTokenizer.h>
#include <Poco/Timestamp.h>
#include <Poco/URI.h>
#include "ClientSession.hpp"
@@ -40,18 +39,17 @@
using namespace LOOLProtocol;
using Poco::StringTokenizer;
using Poco::Timestamp;
TileCache::TileCache(const std::string& docURL,
const Timestamp& modifiedTime,
const std::chrono::system_clock::time_point& modifiedTime,
bool dontCache) :
_docURL(docURL),
_dontCache(dontCache)
{
#ifndef BUILDING_TESTS
LOG_INF("TileCache ctor for uri [" << LOOLWSD::anonymizeUrl(_docURL) <<
"], modifiedTime=" << (modifiedTime.raw()/1000000) <<
"], dontCache=" << _dontCache);
"], modifiedTime=" << std::chrono::duration_cast<std::chrono::seconds>
(modifiedTime.time_since_epoch()).count() << "], dontCache=" << _dontCache);
#endif
(void)modifiedTime;
}
diff --git a/wsd/TileCache.hpp b/wsd/TileCache.hpp
index d922b94..88d837e 100644
--- a/wsd/TileCache.hpp
+++ b/wsd/TileCache.hpp
@@ -16,7 +16,6 @@
#include <string>
#include <unordered_map>
#include <Poco/Timestamp.h>
#include <Rectangle.hpp>
#include "TileDesc.hpp"
@@ -83,7 +82,7 @@
/// When the docURL is a non-file:// url, the timestamp has to be provided by the caller.
/// For file:// url's, it's ignored.
/// When it is missing for non-file:// url, it is assumed the document must be read, and no cached value used.
TileCache(const std::string& docURL, const Poco::Timestamp& modifiedTime, bool dontCache = false);
TileCache(const std::string& docURL, const std::chrono::system_clock::time_point& modifiedTime, bool dontCache = false);
~TileCache();
/// Completely clear the cache contents.