Add /hosting/capabilities endpoint to advertise online features
Add an entry to discovery.xml with the urlsrc where capabilities end point can be found. Use json format to send back the feature list. Change-Id: I390a53d956d53ca79e5a8090aead7f4131ec4ca0private/kendy/mobile
parent
d6d0c3e8a3
commit
0bb8b7c7a8
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"convert-to": {
|
||||
"available": false
|
||||
}
|
||||
}
|
|
@ -36,6 +36,8 @@ constexpr const char CHILD_URI[] = "/loolws/child?";
|
|||
constexpr const char NEW_CHILD_URI[] = "/loolws/newchild";
|
||||
constexpr const char LO_JAIL_SUBPATH[] = "lo";
|
||||
|
||||
constexpr const char CAPABILITIES_END_POINT[] = "/hosting/capabilities";
|
||||
|
||||
/// The HTTP response User-Agent.
|
||||
constexpr const char* HTTP_AGENT_STRING = "LOOLWSD HTTP Agent " LOOLWSD_VERSION;
|
||||
|
||||
|
|
|
@ -339,5 +339,9 @@
|
|||
<app name="application/pdf">
|
||||
<action name="view" ext="pdf"/>
|
||||
</app>
|
||||
|
||||
<app name="Capabilities">
|
||||
<action name="getinfo" ext=""/>
|
||||
</app>
|
||||
</net-zone>
|
||||
</wopi-discovery>
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
|
||||
#include <config.h>
|
||||
|
||||
#include <Poco/DOM/AutoPtr.h>
|
||||
#include <Poco/DOM/DOMParser.h>
|
||||
#include <Poco/DOM/Document.h>
|
||||
#include <Poco/DOM/Element.h>
|
||||
#include <Poco/DOM/NodeList.h>
|
||||
#include <Poco/Net/AcceptCertificateHandler.h>
|
||||
#include <Poco/Net/FilePartSource.h>
|
||||
#include <Poco/Net/HTMLForm.h>
|
||||
|
@ -40,6 +45,7 @@ class HTTPServerTest : public CPPUNIT_NS::TestFixture
|
|||
CPPUNIT_TEST_SUITE(HTTPServerTest);
|
||||
|
||||
CPPUNIT_TEST(testDiscovery);
|
||||
CPPUNIT_TEST(testCapabilities);
|
||||
CPPUNIT_TEST(testLoleafletGet);
|
||||
CPPUNIT_TEST(testLoleafletPost);
|
||||
CPPUNIT_TEST(testScriptsAndLinksGet);
|
||||
|
@ -49,6 +55,7 @@ class HTTPServerTest : public CPPUNIT_NS::TestFixture
|
|||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void testDiscovery();
|
||||
void testCapabilities();
|
||||
void testLoleafletGet();
|
||||
void testLoleafletPost();
|
||||
void testScriptsAndLinksGet();
|
||||
|
@ -104,6 +111,72 @@ void HTTPServerTest::testDiscovery()
|
|||
CPPUNIT_ASSERT_EQUAL(std::string("text/xml"), response.getContentType());
|
||||
}
|
||||
|
||||
|
||||
void HTTPServerTest::testCapabilities()
|
||||
{
|
||||
std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri));
|
||||
|
||||
// Get discovery first and extract the urlsrc of the capabilities end point
|
||||
std::string capabiltiesURI;
|
||||
{
|
||||
|
||||
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, "/hosting/discovery");
|
||||
session->sendRequest(request);
|
||||
|
||||
Poco::Net::HTTPResponse response;
|
||||
std::istream& rs = session->receiveResponse(response);
|
||||
CPPUNIT_ASSERT_EQUAL(Poco::Net::HTTPResponse::HTTP_OK, response.getStatus());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("text/xml"), response.getContentType());
|
||||
|
||||
std::string discoveryXML;
|
||||
Poco::StreamCopier::copyToString(rs, discoveryXML);
|
||||
|
||||
Poco::XML::DOMParser parser;
|
||||
Poco::XML::AutoPtr<Poco::XML::Document> docXML = parser.parseString(discoveryXML);
|
||||
Poco::XML::AutoPtr<Poco::XML::NodeList> listNodes = docXML->getElementsByTagName("action");
|
||||
bool foundCapabilities = false;
|
||||
for (unsigned long index = 0; index < listNodes->length(); ++index)
|
||||
{
|
||||
Poco::XML::Element* elem = static_cast<Poco::XML::Element*>(listNodes->item(index));
|
||||
Poco::XML::Element* parent = elem->parentNode() ? static_cast<Poco::XML::Element*>(elem->parentNode()) : nullptr;
|
||||
if(parent && parent->getAttribute("name") == "Capabilities")
|
||||
{
|
||||
foundCapabilities = true;
|
||||
capabiltiesURI = elem->getAttribute("urlsrc");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(foundCapabilities);
|
||||
CPPUNIT_ASSERT_EQUAL(_uri.toString() + CAPABILITIES_END_POINT, capabiltiesURI);
|
||||
}
|
||||
|
||||
// Then get the capabilities json
|
||||
{
|
||||
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, CAPABILITIES_END_POINT);
|
||||
session->sendRequest(request);
|
||||
|
||||
Poco::Net::HTTPResponse response;
|
||||
std::istream& rs = session->receiveResponse(response);
|
||||
CPPUNIT_ASSERT_EQUAL(Poco::Net::HTTPResponse::HTTP_OK, response.getStatus());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("application/json"), response.getContentType());
|
||||
|
||||
std::ostringstream oss;
|
||||
Poco::StreamCopier::copyStream(rs, oss);
|
||||
std::string responseString = oss.str();
|
||||
|
||||
Poco::JSON::Parser parser;
|
||||
Poco::Dynamic::Var jsonFile = parser.parse(responseString);
|
||||
Poco::JSON::Object::Ptr features = jsonFile.extract<Poco::JSON::Object::Ptr>();
|
||||
CPPUNIT_ASSERT(features);
|
||||
CPPUNIT_ASSERT(features->has("convert-to"));
|
||||
|
||||
Poco::JSON::Object::Ptr convert_to = features->get("convert-to").extract<Poco::JSON::Object::Ptr>();
|
||||
CPPUNIT_ASSERT(convert_to->has("available"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HTTPServerTest::testLoleafletGet()
|
||||
{
|
||||
std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri));
|
||||
|
|
|
@ -2015,6 +2015,10 @@ private:
|
|||
{
|
||||
handleWopiDiscoveryRequest(request);
|
||||
}
|
||||
else if (request.getMethod() == HTTPRequest::HTTP_GET && request.getURI() == CAPABILITIES_END_POINT)
|
||||
{
|
||||
handleCapabilitiesRequest(request);
|
||||
}
|
||||
else if (request.getMethod() == HTTPRequest::HTTP_GET && request.getURI() == "/robots.txt")
|
||||
{
|
||||
handleRobotsTxtRequest(request);
|
||||
|
@ -2162,6 +2166,28 @@ private:
|
|||
LOG_INF("Sent discovery.xml successfully.");
|
||||
}
|
||||
|
||||
void handleCapabilitiesRequest(const Poco::Net::HTTPRequest& request)
|
||||
{
|
||||
LOG_DBG("Wopi capabilities request: " << request.getURI());
|
||||
|
||||
std::string capabilities = getCapabilitiesJson();
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "HTTP/1.1 200 OK\r\n"
|
||||
<< "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
|
||||
<< "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
|
||||
<< "Content-Length: " << capabilities.size() << "\r\n"
|
||||
<< "Content-Type: application/json\r\n"
|
||||
<< "X-Content-Type-Options: nosniff\r\n"
|
||||
<< "\r\n"
|
||||
<< capabilities;
|
||||
|
||||
auto socket = _socket.lock();
|
||||
socket->send(oss.str());
|
||||
socket->shutdown();
|
||||
LOG_INF("Sent cpabilities.json successfully.");
|
||||
}
|
||||
|
||||
void handleRobotsTxtRequest(const Poco::Net::HTTPRequest& request)
|
||||
{
|
||||
LOG_DBG("HTTP request: " << request.getURI());
|
||||
|
@ -2626,15 +2652,16 @@ private:
|
|||
const std::string urlsrc = "urlsrc";
|
||||
const auto& config = Application::instance().config();
|
||||
const std::string loleafletHtml = config.getString("loleaflet_html", "loleaflet.html");
|
||||
const std::string uriValue =
|
||||
const std::string rootUriValue =
|
||||
#if ENABLE_SSL
|
||||
((LOOLWSD::isSSLEnabled() || LOOLWSD::isSSLTermination()) ? "https://" : "http://")
|
||||
#else
|
||||
"http://"
|
||||
#endif
|
||||
+ std::string("%SERVER_HOST%")
|
||||
+ LOOLWSD::ServiceRoot
|
||||
+ "/loleaflet/" LOOLWSD_VERSION_HASH "/" + loleafletHtml + '?';
|
||||
+ LOOLWSD::ServiceRoot;
|
||||
const std::string uriValue = rootUriValue
|
||||
+ "/loleaflet/" LOOLWSD_VERSION_HASH "/" + loleafletHtml + '?';
|
||||
|
||||
InputSource inputSrc(discoveryPath);
|
||||
DOMParser parser;
|
||||
|
@ -2644,7 +2671,15 @@ private:
|
|||
for (unsigned long it = 0; it < listNodes->length(); ++it)
|
||||
{
|
||||
Element* elem = static_cast<Element*>(listNodes->item(it));
|
||||
elem->setAttribute(urlsrc, uriValue);
|
||||
Element* parent = elem->parentNode() ? static_cast<Element*>(elem->parentNode()) : nullptr;
|
||||
if(parent && parent->getAttribute("name") == "Capabilities")
|
||||
{
|
||||
elem->setAttribute(urlsrc, rootUriValue + CAPABILITIES_END_POINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem->setAttribute(urlsrc, uriValue);
|
||||
}
|
||||
|
||||
// Set the View extensions cache as well.
|
||||
if (elem->getAttribute("name") == "edit")
|
||||
|
@ -2657,6 +2692,39 @@ private:
|
|||
return ostrXML.str();
|
||||
}
|
||||
|
||||
/// Process the capabilities.json file and return as string.
|
||||
std::string getCapabilitiesJson()
|
||||
{
|
||||
std::shared_ptr<StreamSocket> socket = _socket.lock();
|
||||
|
||||
// http://server/hosting/capabilities
|
||||
#if defined __linux && defined MOBILEAPP
|
||||
std::string capabilitiesPath = Path(Application::instance().commandPath()).parent().parent().toString() + "capabilities.json";
|
||||
#else
|
||||
std::string capabilitiesPath = Path(Application::instance().commandPath()).parent().toString() + "capabilities.json";
|
||||
#endif
|
||||
if (!File(capabilitiesPath).exists())
|
||||
{
|
||||
capabilitiesPath = LOOLWSD::FileServerRoot + "/capabilities.json";
|
||||
}
|
||||
std::ifstream ifs (capabilitiesPath.c_str(), std::ifstream::in);
|
||||
|
||||
if(!ifs.is_open())
|
||||
return "";
|
||||
|
||||
Poco::JSON::Parser parser;
|
||||
Poco::Dynamic::Var jsonFile = parser.parse(ifs);
|
||||
Poco::JSON::Object::Ptr features = jsonFile.extract<Poco::JSON::Object::Ptr>();
|
||||
Poco::JSON::Object::Ptr convert_to = features->get("convert-to").extract<Poco::JSON::Object::Ptr>();
|
||||
|
||||
Poco::Dynamic::Var available = allowPostFrom(socket->clientAddress());
|
||||
convert_to->set("available", available);
|
||||
|
||||
std::ostringstream ostrJSON;
|
||||
features->stringify(ostrJSON);
|
||||
return ostrJSON.str();
|
||||
}
|
||||
|
||||
private:
|
||||
// The socket that owns us (we can't own it).
|
||||
std::weak_ptr<StreamSocket> _socket;
|
||||
|
|
Loading…
Reference in New Issue