2017-09-26 09:12:58 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
2023-04-21 06:50:56 -05:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2017-09-26 09:12:58 -05:00
|
|
|
#include "config.h"
|
|
|
|
|
2022-11-07 19:26:09 -06:00
|
|
|
#include "Protocol.hpp"
|
2022-02-26 15:54:47 -06:00
|
|
|
#include "HttpRequest.hpp"
|
2017-09-27 12:57:22 -05:00
|
|
|
#include "helpers.hpp"
|
2017-09-26 09:12:58 -05:00
|
|
|
#include "Log.hpp"
|
|
|
|
#include "Unit.hpp"
|
2022-02-26 15:54:47 -06:00
|
|
|
#include "Util.hpp"
|
2022-11-07 19:27:12 -06:00
|
|
|
#include "UnitWSDClient.hpp"
|
2022-12-10 11:00:59 -06:00
|
|
|
#include "StringVector.hpp"
|
|
|
|
#include "lokassert.hpp"
|
2018-02-08 08:24:37 -06:00
|
|
|
|
2017-09-26 09:12:58 -05:00
|
|
|
#include <Poco/JSON/Object.h>
|
|
|
|
#include <Poco/URI.h>
|
2020-04-29 14:24:33 -05:00
|
|
|
#include <Poco/Util/LayeredConfiguration.h>
|
2022-11-07 19:26:09 -06:00
|
|
|
|
2022-11-16 06:47:29 -06:00
|
|
|
#include <cctype>
|
2020-12-21 18:07:26 -06:00
|
|
|
#include <sstream>
|
2021-11-11 03:17:43 -06:00
|
|
|
#include <vector>
|
2017-09-26 09:12:58 -05:00
|
|
|
|
2022-11-07 19:26:09 -06:00
|
|
|
namespace Poco
|
|
|
|
{
|
|
|
|
namespace Net
|
|
|
|
{
|
|
|
|
class HTTPRequest;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Poco
|
|
|
|
|
2022-04-30 18:15:57 -05:00
|
|
|
/// Simulates a WOPI server for testing purposes.
|
|
|
|
/// Currently only serves one file contents.
|
2022-05-01 11:31:28 -05:00
|
|
|
/// Furthermore, the file URI doesn't contain the
|
|
|
|
/// real filename (in most tests), instead filenames
|
|
|
|
/// 1 to 9 are considered special.
|
2022-11-07 19:27:12 -06:00
|
|
|
class WopiTestServer : public UnitWSDClient
|
2017-09-26 09:12:58 -05:00
|
|
|
{
|
2022-02-10 07:35:46 -06:00
|
|
|
private:
|
|
|
|
|
2021-11-18 06:08:14 -06:00
|
|
|
enum class COOLStatusCode
|
2018-02-08 12:51:54 -06:00
|
|
|
{
|
|
|
|
DocChanged = 1010
|
|
|
|
};
|
|
|
|
|
2017-10-20 11:12:05 -05:00
|
|
|
/// Content of the file.
|
|
|
|
std::string _fileContent;
|
|
|
|
|
2022-05-01 11:31:28 -05:00
|
|
|
/// The filename. TODO: Support multiple ones.
|
|
|
|
std::string _filename;
|
|
|
|
|
2018-02-08 08:45:47 -06:00
|
|
|
/// Last modified time of the file
|
2019-09-27 11:56:16 -05:00
|
|
|
std::chrono::system_clock::time_point _fileLastModifiedTime;
|
2018-02-08 08:45:47 -06:00
|
|
|
|
2022-02-10 08:58:52 -06:00
|
|
|
/// The number of CheckFileInfo invocations.
|
|
|
|
std::size_t _countCheckFileInfo;
|
|
|
|
/// The number of GetFile invocations.
|
|
|
|
std::size_t _countGetFile;
|
|
|
|
/// The number of rename invocations.
|
|
|
|
std::size_t _countPutRelative;
|
|
|
|
/// The number of upload invocations.
|
|
|
|
std::size_t _countPutFile;
|
|
|
|
|
2022-05-01 11:31:28 -05:00
|
|
|
/// The default filename when only content is given.
|
|
|
|
static constexpr auto DefaultFilename = "hello.txt";
|
|
|
|
|
2022-02-10 07:35:46 -06:00
|
|
|
protected:
|
|
|
|
|
2018-12-13 01:48:50 -06:00
|
|
|
const std::string& getFileContent() const { return _fileContent; }
|
|
|
|
|
2018-02-08 12:51:54 -06:00
|
|
|
/// Sets the file content to a given value and update the last file modified time
|
|
|
|
void setFileContent(const std::string& fileContent)
|
|
|
|
{
|
2022-05-01 10:00:50 -05:00
|
|
|
LOG_TST("setFileContent: [" << COOLProtocol::getAbbreviatedMessage(fileContent ) << ']');
|
2018-02-08 12:51:54 -06:00
|
|
|
_fileContent = fileContent;
|
2019-09-27 11:56:16 -05:00
|
|
|
_fileLastModifiedTime = std::chrono::system_clock::now();
|
2018-02-08 12:51:54 -06:00
|
|
|
}
|
|
|
|
|
2020-12-22 13:00:05 -06:00
|
|
|
const std::chrono::system_clock::time_point& getFileLastModifiedTime() const
|
|
|
|
{
|
|
|
|
return _fileLastModifiedTime;
|
|
|
|
}
|
2019-07-25 00:58:19 -05:00
|
|
|
|
2022-05-01 10:00:50 -05:00
|
|
|
WopiTestServer(const std::string& name, const std::string& filenameOrContents = "Hello, world")
|
2022-11-07 19:27:12 -06:00
|
|
|
: UnitWSDClient(name)
|
2022-05-01 11:31:28 -05:00
|
|
|
, _filename(DefaultFilename)
|
2022-02-10 08:58:52 -06:00
|
|
|
, _countCheckFileInfo(0)
|
|
|
|
, _countGetFile(0)
|
|
|
|
, _countPutRelative(0)
|
|
|
|
, _countPutFile(0)
|
2017-09-26 09:12:58 -05:00
|
|
|
{
|
2022-02-16 16:22:18 -06:00
|
|
|
LOG_TST("WopiTestServer created for [" << getTestname() << ']');
|
2022-05-01 10:00:50 -05:00
|
|
|
|
|
|
|
// Read the document data and store as string in memory.
|
|
|
|
const auto data = helpers::readDataFromFile(filenameOrContents);
|
|
|
|
if (!data.empty())
|
|
|
|
{
|
|
|
|
// That was a filename, set its contents.
|
|
|
|
LOG_TST("WopiTestServer created with " << data.size() << " bytes from file ["
|
|
|
|
<< filenameOrContents << "]");
|
2022-05-01 11:31:28 -05:00
|
|
|
_filename = filenameOrContents; // Capture the real filename.
|
2022-05-01 10:00:50 -05:00
|
|
|
setFileContent(Util::toString(data));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Not a valid filename, assume it's some data.
|
|
|
|
LOG_TST("WopiTestServer created with " << filenameOrContents.size()
|
|
|
|
<< " bytes from data.");
|
|
|
|
setFileContent(filenameOrContents);
|
|
|
|
}
|
2017-09-26 09:12:58 -05:00
|
|
|
}
|
|
|
|
|
2022-02-10 08:58:52 -06:00
|
|
|
std::size_t getCountCheckFileInfo() const { return _countCheckFileInfo; }
|
2023-05-21 10:45:14 -05:00
|
|
|
void resetCountCheckFileInfo()
|
|
|
|
{
|
|
|
|
LOG_TST("Resetting countCheckFileInfo [" << _countCheckFileInfo << "] to zero");
|
|
|
|
_countCheckFileInfo = 0;
|
|
|
|
}
|
|
|
|
|
2022-02-10 08:58:52 -06:00
|
|
|
std::size_t getCountGetFile() const { return _countGetFile; }
|
2023-05-21 10:45:14 -05:00
|
|
|
void resetCountGetFile()
|
|
|
|
{
|
|
|
|
LOG_TST("Resetting countGetFile [" << _countGetFile << "] to zero");
|
|
|
|
_countGetFile = 0;
|
|
|
|
}
|
|
|
|
|
2022-02-10 08:58:52 -06:00
|
|
|
std::size_t getCountPutRelative() const { return _countPutRelative; }
|
2023-05-21 10:45:14 -05:00
|
|
|
void resetCountPutRelative()
|
|
|
|
{
|
|
|
|
LOG_TST("Resetting countPutRelative [" << _countPutRelative << "] to zero");
|
|
|
|
_countPutRelative = 0;
|
|
|
|
}
|
|
|
|
|
2022-02-10 08:58:52 -06:00
|
|
|
std::size_t getCountPutFile() const { return _countPutFile; }
|
2023-05-21 10:45:14 -05:00
|
|
|
void resetCountPutFile()
|
|
|
|
{
|
|
|
|
LOG_TST("Resetting countPutFile [" << _countPutFile << "] to zero");
|
|
|
|
_countPutFile = 0;
|
|
|
|
}
|
2022-02-10 08:58:52 -06:00
|
|
|
|
2017-10-20 11:12:05 -05:00
|
|
|
virtual void assertCheckFileInfoRequest(const Poco::Net::HTTPRequest& /*request*/)
|
|
|
|
{
|
|
|
|
}
|
2017-09-26 09:12:58 -05:00
|
|
|
|
2017-10-20 11:12:05 -05:00
|
|
|
virtual void assertGetFileRequest(const Poco::Net::HTTPRequest& /*request*/)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-08-22 15:00:05 -05:00
|
|
|
/// Assert the PutFile request is valid and optionally return a response.
|
2021-04-30 18:45:34 -05:00
|
|
|
virtual std::unique_ptr<http::Response>
|
|
|
|
assertPutFileRequest(const Poco::Net::HTTPRequest& /*request*/)
|
2017-10-20 11:12:05 -05:00
|
|
|
{
|
2023-02-19 09:42:34 -06:00
|
|
|
return nullptr; // Success.
|
2017-10-20 11:12:05 -05:00
|
|
|
}
|
2017-09-26 09:12:58 -05:00
|
|
|
|
2017-10-25 07:09:27 -05:00
|
|
|
virtual void assertPutRelativeFileRequest(const Poco::Net::HTTPRequest& /*request*/)
|
2017-10-20 11:12:05 -05:00
|
|
|
{
|
|
|
|
}
|
2017-09-26 09:12:58 -05:00
|
|
|
|
2019-04-30 09:21:44 -05:00
|
|
|
virtual void assertRenameFileRequest(const Poco::Net::HTTPRequest& /*request*/)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-11-02 06:11:03 -05:00
|
|
|
/// Called when the server receives a Lock or Unlock request.
|
2023-02-19 09:42:34 -06:00
|
|
|
virtual std::unique_ptr<http::Response>
|
|
|
|
assertLockRequest(const Poco::Net::HTTPRequest& /*request*/)
|
|
|
|
{
|
|
|
|
return nullptr; // Success.
|
|
|
|
}
|
2022-11-02 06:11:03 -05:00
|
|
|
|
2022-04-30 11:29:23 -05:00
|
|
|
/// Given a URI, returns the filename.
|
2022-05-01 11:31:28 -05:00
|
|
|
///FIXME: this should be remove when we support multiple files properly.
|
2022-04-30 11:29:23 -05:00
|
|
|
virtual std::string getFilename(const Poco::URI& uri) const
|
|
|
|
{
|
2022-05-01 11:31:28 -05:00
|
|
|
std::string filename = extractFilenameFromWopiUri(uri.getPath());
|
|
|
|
|
2022-04-30 17:50:14 -05:00
|
|
|
// Note: This is a fake implementation.
|
2022-05-01 11:31:28 -05:00
|
|
|
if (filename == "3")
|
|
|
|
{
|
|
|
|
// Test '%' in the filename.
|
|
|
|
//FIXME: pass this in the URI.
|
|
|
|
return "he%llo.txt";
|
|
|
|
}
|
|
|
|
|
2022-11-16 06:47:29 -06:00
|
|
|
if (filename.size() == 1 && std::isdigit(filename[0]))
|
2022-05-01 11:31:28 -05:00
|
|
|
{
|
2022-11-16 06:47:29 -06:00
|
|
|
const auto number = std::stoi(filename);
|
|
|
|
if (number >= 1 && number <= 9)
|
|
|
|
{
|
|
|
|
// Fake filename, depends on implicit filename.
|
|
|
|
return DefaultFilename;
|
|
|
|
}
|
2022-05-01 11:31:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return the filename given in the URI.
|
|
|
|
return filename;
|
2022-04-30 11:29:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the virtual root-path that we serve.
|
2022-04-30 17:50:14 -05:00
|
|
|
static const std::string& getURIRootPath()
|
2022-04-30 11:29:23 -05:00
|
|
|
{
|
|
|
|
static const std::string RootPath = "/wopi/files/";
|
|
|
|
return RootPath;
|
|
|
|
}
|
|
|
|
|
2022-04-30 17:50:14 -05:00
|
|
|
/// Given a wopi URI, extracts the filename.
|
|
|
|
static std::string extractFilenameFromWopiUri(const std::string& uriPath)
|
|
|
|
{
|
|
|
|
if (Util::startsWith(uriPath, getURIRootPath()))
|
|
|
|
{
|
|
|
|
const auto first = getURIRootPath().size();
|
|
|
|
const auto it = uriPath.find_first_of('/', first);
|
|
|
|
return uriPath.substr(first, it);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true iff @uriPath is a Wopi path but not to the contents.
|
|
|
|
static bool isWopiInfoRequest(const std::string& uriPath)
|
|
|
|
{
|
|
|
|
return Util::startsWith(uriPath, getURIRootPath()) && !Util::endsWith(uriPath, "/contents");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true iff @uriPath is a Wopi path to the contents of a file.
|
|
|
|
static bool isWopiContentRequest(const std::string& uriPath)
|
|
|
|
{
|
|
|
|
return Util::startsWith(uriPath, getURIRootPath()) && Util::endsWith(uriPath, "/contents");
|
|
|
|
}
|
|
|
|
|
2020-04-29 14:24:33 -05:00
|
|
|
void configure(Poco::Util::LayeredConfiguration& config) override
|
|
|
|
{
|
|
|
|
UnitWSD::configure(config);
|
|
|
|
// we're still internally confused as to https vs. http in places.
|
|
|
|
config.setBool("storage.ssl.as_scheme", false);
|
|
|
|
}
|
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
/// Handles WOPI CheckFileInfo requests.
|
|
|
|
virtual bool handleCheckFileInfoRequest(const Poco::Net::HTTPRequest& request,
|
|
|
|
std::shared_ptr<StreamSocket>& socket)
|
2017-09-26 09:12:58 -05:00
|
|
|
{
|
2022-04-30 15:31:25 -05:00
|
|
|
const Poco::URI uriReq(request.getURI());
|
|
|
|
|
|
|
|
Poco::JSON::Object::Ptr fileInfo = new Poco::JSON::Object();
|
|
|
|
fileInfo->set("BaseFileName", getFilename(uriReq));
|
|
|
|
fileInfo->set("Size", getFileContent().size());
|
|
|
|
fileInfo->set("Version", "1.0");
|
|
|
|
fileInfo->set("OwnerId", "test");
|
|
|
|
fileInfo->set("UserId", "test");
|
|
|
|
fileInfo->set("UserFriendlyName", "test");
|
|
|
|
fileInfo->set("UserCanWrite", "true");
|
|
|
|
fileInfo->set("PostMessageOrigin", "localhost");
|
|
|
|
fileInfo->set("LastModifiedTime",
|
|
|
|
Util::getIso8601FracformatTime(getFileLastModifiedTime()));
|
|
|
|
fileInfo->set("EnableOwnerTermination", "true");
|
2022-11-02 06:11:03 -05:00
|
|
|
fileInfo->set("SupportsLocks", "false");
|
|
|
|
configCheckFileInfo(fileInfo);
|
2022-04-30 15:31:25 -05:00
|
|
|
|
|
|
|
std::ostringstream jsonStream;
|
|
|
|
fileInfo->stringify(jsonStream);
|
|
|
|
|
2023-01-16 17:38:59 -06:00
|
|
|
http::Response httpResponse(http::StatusCode::OK);
|
2022-04-30 15:31:25 -05:00
|
|
|
httpResponse.set("Last-Modified", Util::getHttpTime(getFileLastModifiedTime()));
|
|
|
|
httpResponse.setBody(jsonStream.str(), "application/json; charset=utf-8");
|
|
|
|
socket->sendAndShutdown(httpResponse);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2020-06-22 07:24:11 -05:00
|
|
|
|
2022-11-02 06:11:03 -05:00
|
|
|
/// Override to set the CheckFileInfo attributes.
|
|
|
|
virtual void configCheckFileInfo(Poco::JSON::Object::Ptr /*fileInfo*/) {}
|
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
virtual bool handleGetFileRequest(const Poco::Net::HTTPRequest&,
|
|
|
|
std::shared_ptr<StreamSocket>& socket)
|
|
|
|
{
|
2023-01-16 17:38:59 -06:00
|
|
|
http::Response httpResponse(http::StatusCode::OK);
|
2022-04-30 15:31:25 -05:00
|
|
|
httpResponse.set("Last-Modified", Util::getHttpTime(getFileLastModifiedTime()));
|
|
|
|
httpResponse.setBody(getFileContent(), "application/octet-stream");
|
|
|
|
socket->sendAndShutdown(httpResponse);
|
2020-06-22 07:24:11 -05:00
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool handleHttpGetRequest(const Poco::Net::HTTPRequest& request,
|
|
|
|
std::shared_ptr<StreamSocket>& socket)
|
|
|
|
{
|
|
|
|
LOG_ASSERT_MSG(request.getMethod() == "GET", "Expect an HTTP GET request");
|
2017-09-26 09:12:58 -05:00
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
const Poco::URI uriReq(request.getURI());
|
|
|
|
|
2022-04-30 17:50:14 -05:00
|
|
|
if (isWopiInfoRequest(uriReq.getPath())) // CheckFileInfo
|
2017-09-26 09:12:58 -05:00
|
|
|
{
|
2022-02-10 08:58:52 -06:00
|
|
|
++_countCheckFileInfo;
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Handling CheckFileInfo (#" << _countCheckFileInfo
|
|
|
|
<< "): " << uriReq.getPath());
|
2017-09-26 09:12:58 -05:00
|
|
|
|
|
|
|
assertCheckFileInfoRequest(request);
|
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
return handleCheckFileInfoRequest(request, socket);
|
2017-09-26 09:12:58 -05:00
|
|
|
}
|
2022-04-30 17:50:14 -05:00
|
|
|
else if (isWopiContentRequest(uriReq.getPath())) // GetFile
|
2017-09-26 09:12:58 -05:00
|
|
|
{
|
2022-02-10 08:58:52 -06:00
|
|
|
++_countGetFile;
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Handling GetFile (#" << _countGetFile
|
|
|
|
<< "): " << uriReq.getPath());
|
2017-09-26 09:12:58 -05:00
|
|
|
|
|
|
|
assertGetFileRequest(request);
|
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
return handleGetFileRequest(request, socket);
|
2017-10-20 11:12:05 -05:00
|
|
|
}
|
2022-04-30 15:31:25 -05:00
|
|
|
|
2023-02-25 09:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: not a wopi request, skipping: " << uriReq.getPath());
|
2022-04-30 15:31:25 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool handleHttpPostRequest(const Poco::Net::HTTPRequest& request,
|
|
|
|
Poco::MemoryInputStream& message,
|
|
|
|
std::shared_ptr<StreamSocket>& socket)
|
|
|
|
{
|
|
|
|
LOG_ASSERT_MSG(request.getMethod() == "POST", "Expect an HTTP POST request");
|
|
|
|
|
|
|
|
const Poco::URI uriReq(request.getURI());
|
2022-04-30 17:50:14 -05:00
|
|
|
if (isWopiInfoRequest(uriReq.getPath()))
|
2017-10-20 11:12:05 -05:00
|
|
|
{
|
2022-11-02 06:11:03 -05:00
|
|
|
if (!request.get("X-WOPI-Lock", std::string()).empty())
|
2019-04-30 09:21:44 -05:00
|
|
|
{
|
2022-11-02 06:11:03 -05:00
|
|
|
const std::string op = request.get("X-WOPI-Override", std::string());
|
|
|
|
if (op == "LOCK" || op == "UNLOCK")
|
|
|
|
{
|
2023-02-19 09:42:34 -06:00
|
|
|
std::unique_ptr<http::Response> response = assertLockRequest(request);
|
|
|
|
if (!response)
|
|
|
|
{
|
|
|
|
LOG_TST("FakeWOPIHost: " << op << " operation on [" << uriReq.getPath()
|
|
|
|
<< "] succeeded 200 OK");
|
|
|
|
http::Response httpResponse(http::StatusCode::OK);
|
|
|
|
socket->sendAndShutdown(httpResponse);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LOG_TST("FakeWOPIHost: " << op << " operation on [" << uriReq.getPath()
|
|
|
|
<< "]: " << response->statusLine().statusCode()
|
|
|
|
<< ' ' << response->statusLine().reasonPhrase());
|
|
|
|
socket->sendAndShutdown(*response);
|
|
|
|
}
|
2022-11-02 06:11:03 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-16 17:38:59 -06:00
|
|
|
http::Response httpResponse(http::StatusCode::Conflict);
|
2022-11-02 06:11:03 -05:00
|
|
|
httpResponse.set("X-WOPI-LockFailureReason", "Invalid lock operation");
|
|
|
|
socket->sendAndShutdown(httpResponse);
|
|
|
|
}
|
2019-04-30 09:21:44 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-11-02 06:11:03 -05:00
|
|
|
const std::string wopiURL =
|
|
|
|
helpers::getTestServerURI() +
|
|
|
|
"/something wopi/files/1?access_token=anything&reuse_cookies=cook=well";
|
|
|
|
|
|
|
|
std::string content;
|
|
|
|
if (request.get("X-WOPI-Override") == std::string("PUT_RELATIVE"))
|
|
|
|
{
|
|
|
|
++_countPutRelative;
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Handling PutRelativeFile (#"
|
2022-11-02 06:11:03 -05:00
|
|
|
<< _countPutRelative << "): " << uriReq.getPath());
|
|
|
|
|
|
|
|
LOK_ASSERT_EQUAL(std::string("PUT_RELATIVE"), request.get("X-WOPI-Override"));
|
|
|
|
assertPutRelativeFileRequest(request);
|
|
|
|
content = "{ \"Name\":\"hello world%1.pdf\", \"Url\":\"" + wopiURL + "\" }";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// rename file; response should be the file name without the url and the extension
|
|
|
|
LOK_ASSERT_EQUAL(std::string("RENAME_FILE"), request.get("X-WOPI-Override"));
|
|
|
|
assertRenameFileRequest(request);
|
|
|
|
content = "{ \"Name\":\"hello\", \"Url\":\"" + wopiURL + "\" }";
|
|
|
|
}
|
|
|
|
|
2023-01-16 17:38:59 -06:00
|
|
|
http::Response httpResponse(http::StatusCode::OK);
|
2022-11-02 06:11:03 -05:00
|
|
|
httpResponse.set("Last-Modified", Util::getHttpTime(getFileLastModifiedTime()));
|
|
|
|
httpResponse.setBody(content, "application/json; charset=utf-8");
|
|
|
|
socket->sendAndShutdown(httpResponse);
|
2019-04-30 09:21:44 -05:00
|
|
|
}
|
2017-10-20 11:12:05 -05:00
|
|
|
|
2017-09-27 07:13:43 -05:00
|
|
|
return true;
|
|
|
|
}
|
2022-04-30 17:50:14 -05:00
|
|
|
else if (isWopiContentRequest(uriReq.getPath()))
|
2017-09-27 07:13:43 -05:00
|
|
|
{
|
2022-02-10 08:58:52 -06:00
|
|
|
++_countPutFile;
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Handling PutFile (#" << _countPutFile
|
|
|
|
<< "): " << uriReq.getPath());
|
2017-09-27 07:13:43 -05:00
|
|
|
|
2022-02-10 08:58:52 -06:00
|
|
|
const std::string wopiTimestamp = request.get("X-COOL-WOPI-Timestamp", std::string());
|
2018-02-08 12:51:54 -06:00
|
|
|
if (!wopiTimestamp.empty())
|
|
|
|
{
|
2022-04-27 19:35:28 -05:00
|
|
|
const std::string fileModifiedTime =
|
|
|
|
Util::getIso8601FracformatTime(getFileLastModifiedTime());
|
2018-02-08 12:51:54 -06:00
|
|
|
if (wopiTimestamp != fileModifiedTime)
|
|
|
|
{
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Document conflict detected, Stored ModifiedTime: ["
|
|
|
|
<< fileModifiedTime << "], Upload ModifiedTime: [" << wopiTimestamp
|
|
|
|
<< ']');
|
2023-01-16 17:38:59 -06:00
|
|
|
http::Response httpResponse(http::StatusCode::Conflict);
|
2021-04-29 09:13:55 -05:00
|
|
|
httpResponse.setBody(
|
2021-11-18 06:08:14 -06:00
|
|
|
"{\"COOLStatusCode\":" +
|
|
|
|
std::to_string(static_cast<int>(COOLStatusCode::DocChanged)) + '}');
|
2021-04-29 09:13:55 -05:00
|
|
|
socket->sendAndShutdown(httpResponse);
|
2018-02-08 12:51:54 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2023-01-23 15:53:27 -06:00
|
|
|
else
|
|
|
|
{
|
|
|
|
LOG_TST("FakeWOPIHost: Forced document upload");
|
|
|
|
}
|
2018-02-08 12:51:54 -06:00
|
|
|
|
2021-04-30 18:45:34 -05:00
|
|
|
std::unique_ptr<http::Response> response = assertPutFileRequest(request);
|
2022-02-26 15:54:47 -06:00
|
|
|
if (!response || response->statusLine().statusCategory() ==
|
|
|
|
http::StatusLine::StatusCodeClass::Successful)
|
|
|
|
{
|
|
|
|
const std::streamsize size = request.getContentLength();
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Writing document contents in storage (" << size << "bytes)");
|
2022-02-26 15:54:47 -06:00
|
|
|
std::vector<char> buffer(size);
|
|
|
|
message.read(buffer.data(), size);
|
|
|
|
setFileContent(Util::toString(buffer));
|
|
|
|
}
|
|
|
|
|
2021-04-30 18:45:34 -05:00
|
|
|
if (response)
|
|
|
|
{
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Response to POST "
|
2021-05-02 11:23:52 -05:00
|
|
|
<< uriReq.getPath() << ": " << response->statusLine().statusCode() << ' '
|
2021-05-02 11:18:40 -05:00
|
|
|
<< response->statusLine().reasonPhrase());
|
2021-04-30 18:45:34 -05:00
|
|
|
socket->sendAndShutdown(*response);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// By default we return success.
|
2021-05-02 11:18:40 -05:00
|
|
|
const std::string body = "{\"LastModifiedTime\": \"" +
|
2022-04-27 19:35:28 -05:00
|
|
|
Util::getIso8601FracformatTime(getFileLastModifiedTime()) +
|
2021-05-02 11:18:40 -05:00
|
|
|
"\" }";
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("FakeWOPIHost: Response (default) to POST " << uriReq.getPath()
|
|
|
|
<< ": 200 OK " << body);
|
2023-01-16 17:38:59 -06:00
|
|
|
http::Response httpResponse(http::StatusCode::OK);
|
2022-02-01 07:33:25 -06:00
|
|
|
httpResponse.setBody(body, "application/json; charset=utf-8");
|
2021-04-30 18:45:34 -05:00
|
|
|
socket->sendAndShutdown(httpResponse);
|
|
|
|
}
|
2017-09-27 07:13:43 -05:00
|
|
|
|
2017-09-26 09:12:58 -05:00
|
|
|
return true;
|
|
|
|
}
|
2022-04-30 15:31:25 -05:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-12-10 11:00:59 -06:00
|
|
|
/// In some very rare cases we may get requests from other tests.
|
|
|
|
/// This asserts that the URI in question is for our test.
|
|
|
|
void assertTargetTest(const Poco::URI& uri)
|
|
|
|
{
|
|
|
|
const auto params = uri.getQueryParameters();
|
|
|
|
const auto testnameIt = std::find_if(params.begin(), params.end(),
|
|
|
|
[](const std::pair<std::string, std::string>& pair)
|
|
|
|
{ return pair.first == "testname"; });
|
|
|
|
|
|
|
|
LOK_ASSERT_MESSAGE_SILENT("Request belongs to an unknown test", testnameIt != params.end());
|
|
|
|
|
|
|
|
const std::string target = StringVector::tokenize(testnameIt->second, '/')[0];
|
|
|
|
LOK_ASSERT_EQUAL_MESSAGE("Request belongs to a different test", getTestname(), target);
|
|
|
|
}
|
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
/// Here we act as a WOPI server, so that we have a server that responds to
|
|
|
|
/// the wopi requests without additional expensive setup.
|
|
|
|
bool handleHttpRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message,
|
|
|
|
std::shared_ptr<StreamSocket>& socket) override
|
|
|
|
{
|
2022-12-10 10:37:44 -06:00
|
|
|
Poco::URI uriReq(Util::decodeURIComponent(request.getURI()));
|
2022-04-30 15:31:25 -05:00
|
|
|
|
|
|
|
{
|
|
|
|
std::ostringstream oss;
|
2023-01-23 15:53:27 -06:00
|
|
|
oss << "FakeWOPIHost: " << request.getMethod() << " request URI [" << uriReq.toString()
|
2022-04-30 15:31:25 -05:00
|
|
|
<< "]:\n";
|
|
|
|
for (const auto& pair : request)
|
|
|
|
{
|
|
|
|
oss << '\t' << pair.first << ": " << pair.second << " / ";
|
|
|
|
}
|
|
|
|
|
2023-04-13 07:45:25 -05:00
|
|
|
if (UnitBase::get().isFinished())
|
|
|
|
oss << "\nIgnoring as test has finished";
|
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
LOG_TST(oss.str());
|
2023-04-13 07:45:25 -05:00
|
|
|
|
|
|
|
if (UnitBase::get().isFinished())
|
|
|
|
return false;
|
2022-04-30 15:31:25 -05:00
|
|
|
}
|
|
|
|
|
2022-12-10 11:00:59 -06:00
|
|
|
assertTargetTest(uriReq);
|
wsd: test: track tests in URLs
This helps detect when tests cross-connect.
A rare, and very hard to find, issue went
like this:
A test (a random one) would recieve a request
that it didn't expect. This would cause it
to fail. It was clear that this was coming
from a different test--but which?
Meanwhile, another test (UnitWOPIHttpRedirect)
was in connectLOKit retry loop due to repeated
failure to connect.
With this patch, it was easy to see what was
happening, once the source was found. The
idea is to stamp the test sending the
request so the test-server can validate.
Upon failure, we can see the source test.
The source test (UnitWOPIHttpRedirect),
in the case in question, is a
server-side test. It flagged to exit once
it verified that the redirection worked
(in the GetFile serving the document).
Unfortunately, exiting didn't stop the
connection attempts in the same test. In
most cases the document would load before
DocBroker is stopped. But every so often
everything would shutdown and the test
would keep trying to connectLOKit.
Since we shutdown everything, we also
closed the listening port, which is now
available for other tests to re-use.
This is how UnitWOPIHttpRedirect requests
ended up at different tests, causing them
to fail sporadically.
The subsequent test fixes the retry logic.
The fix is left separate since this patch
is useful on its own, and independent of
the issue and the fix. This is just a tool.
Change-Id: I2848cc578a102fc0bd981e1ac71aaabc25f1a403
Signed-off-by: Ashod Nakashian <ashod.nakashian@collabora.co.uk>
2022-07-29 20:07:30 -05:00
|
|
|
|
2022-04-30 15:31:25 -05:00
|
|
|
if (request.getMethod() == "GET")
|
|
|
|
{
|
|
|
|
return handleHttpGetRequest(request, socket);
|
|
|
|
}
|
|
|
|
else if (request.getMethod() == "POST")
|
|
|
|
{
|
|
|
|
return handleHttpPostRequest(request, message, socket);
|
|
|
|
}
|
2021-11-15 09:41:58 -06:00
|
|
|
else if (!Util::startsWith(uriReq.getPath(), "/cool/")) // Skip requests to the websrv.
|
2020-12-22 13:00:05 -06:00
|
|
|
{
|
|
|
|
// Complain if we are expected to handle something that we don't.
|
2023-01-23 15:53:27 -06:00
|
|
|
LOG_TST("ERROR: FakeWOPIHost: Request, cannot handle request: " << uriReq.getPath());
|
2020-12-22 13:00:05 -06:00
|
|
|
}
|
2017-09-26 09:12:58 -05:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-29 23:26:56 -06:00
|
|
|
|
2017-09-26 09:12:58 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|