diff options
author | Skyler Grey <skyler.grey@collabora.com> | 2023-08-18 13:30:35 +0000 |
---|---|---|
committer | Caolán McNamara <caolan.mcnamara@collabora.com> | 2023-09-01 16:55:37 +0200 |
commit | 8e246331f6f71320cfcc8defdd04e756a75f71cf (patch) | |
tree | c359388bf1fe8051e0a8d6a542afb86c961ebf45 /desktop | |
parent | cb72f56977faccbda9490bc16ea3cbf7a22c01b0 (diff) |
Add a FunctionBasedURPConnection and a websocket URP connector
- FunctionBasedURPConnection is used to enable a client to open a URP
connection to a fresh Kit instance in COOL.
- This URP connector can be used with that and
https://github.com/CollaboraOnline/online/pull/6992 to use a Java Uno
Remote Protocol client over websockets
- For interoperability with existing Collabora Online websockets a
prefix (urp ) is added to each message sent and a similar prefix
(urp: ) is expected on each message recieved. This allows sending over
the same websocket as other data is being transmitted through. If you
are writing a bridge to work with this, you will need to add/strip the
prefixes accordingly
- This commit uses Java WebSocket
(https://github.com/TooTallNate/Java-WebSocket) to send data over
websockets.
Change-Id: I2bda3d0b988bef7883f9b6829eeb5b7ae8075f27
Signed-off-by: Skyler Grey <skyler.grey@collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151171
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Diffstat (limited to 'desktop')
-rw-r--r-- | desktop/qa/desktop_lib/test_desktop_lib.cxx | 4 | ||||
-rw-r--r-- | desktop/source/lib/init.cxx | 227 |
2 files changed, 230 insertions, 1 deletions
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 35cbbfe60c15..8a55ae18172d 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -3681,10 +3681,12 @@ void DesktopLOKTest::testABI() CPPUNIT_ASSERT_EQUAL(classOffset(15), offsetof(struct _LibreOfficeKitClass, dumpState)); CPPUNIT_ASSERT_EQUAL(classOffset(16), offsetof(struct _LibreOfficeKitClass, extractRequest)); CPPUNIT_ASSERT_EQUAL(classOffset(17), offsetof(struct _LibreOfficeKitClass, trimMemory)); + CPPUNIT_ASSERT_EQUAL(classOffset(18), offsetof(struct _LibreOfficeKitClass, startURP)); + CPPUNIT_ASSERT_EQUAL(classOffset(19), offsetof(struct _LibreOfficeKitClass, stopURP)); // When extending LibreOfficeKit with a new function pointer, add new assert for the offsetof the // new function pointer and bump this assert for the size of the class. - CPPUNIT_ASSERT_EQUAL(classOffset(18), sizeof(struct _LibreOfficeKitClass)); + CPPUNIT_ASSERT_EQUAL(classOffset(20), sizeof(struct _LibreOfficeKitClass)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass, destroy)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass, saveAs)); diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 6db6f722bee6..4b81619cbc1f 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -49,6 +49,7 @@ #include <memory> #include <iostream> #include <string_view> +#include <queue> #include <boost/property_tree/json_parser.hpp> #include <boost/algorithm/string.hpp> @@ -86,6 +87,7 @@ #include <comphelper/servicehelper.hxx> #include <comphelper/sequenceashashmap.hxx> +#include <com/sun/star/connection/XConnection.hpp> #include <com/sun/star/document/MacroExecMode.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/container/XNameAccess.hpp> @@ -108,6 +110,10 @@ #include <com/sun/star/text/TextContentAnchorType.hpp> #include <com/sun/star/document/XRedlinesSupplier.hpp> #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp> +#include <com/sun/star/bridge/BridgeFactory.hpp> +#include <com/sun/star/bridge/XBridgeFactory.hpp> +#include <com/sun/star/bridge/XBridge.hpp> +#include <com/sun/star/uno/XNamingService.hpp> #include <com/sun/star/xml/crypto/SEInitializer.hpp> #include <com/sun/star/xml/crypto/XSEInitializer.hpp> @@ -121,6 +127,7 @@ #include <com/sun/star/i18n/LocaleCalendar2.hpp> #include <com/sun/star/i18n/ScriptType.hpp> #include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <editeng/flstitem.hxx> #ifdef IOS @@ -228,6 +235,9 @@ using namespace css; using namespace vcl; using namespace desktop; using namespace utl; +using namespace bridge; +using namespace uno; +using namespace lang; static LibLibreOffice_Impl *gImpl = nullptr; static bool lok_preinit_2_called = false; @@ -2567,6 +2577,13 @@ static char* lo_extractRequest(LibreOfficeKit* pThis, static void lo_trimMemory(LibreOfficeKit* pThis, int nTarget); +static int +lo_startURP(LibreOfficeKit* pThis, void* pReceiveURPFromLOContext, void** pSendURPToLOContext, + int (*fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen), + int (**pfnSendURPToLO)(void* pContext, const signed char* pBuffer, int nLen)); + +static void lo_stopURP(LibreOfficeKit* pThis, void* pSendURPToLOContext); + static void lo_runLoop(LibreOfficeKit* pThis, LibreOfficeKitPollCallback pPollCallback, LibreOfficeKitWakeCallback pWakeCallback, @@ -2609,6 +2626,8 @@ LibLibreOffice_Impl::LibLibreOffice_Impl() m_pOfficeClass->dumpState = lo_dumpState; m_pOfficeClass->extractRequest = lo_extractRequest; m_pOfficeClass->trimMemory = lo_trimMemory; + m_pOfficeClass->startURP = lo_startURP; + m_pOfficeClass->stopURP = lo_stopURP; gOfficeClass = m_pOfficeClass; } @@ -3160,6 +3179,214 @@ static void lo_trimMemory(LibreOfficeKit* /* pThis */, int nTarget) } } +namespace +{ +class FunctionBasedURPInstanceProvider + : public ::cppu::WeakImplHelper<css::bridge::XInstanceProvider> +{ +private: + css::uno::Reference<css::uno::XComponentContext> m_rContext; + +public: + FunctionBasedURPInstanceProvider( + const css::uno::Reference<css::uno::XComponentContext>& rxContext); + + // XInstanceProvider + virtual css::uno::Reference<css::uno::XInterface> + SAL_CALL getInstance(const OUString& aName) override; +}; + +// InstanceProvider +FunctionBasedURPInstanceProvider::FunctionBasedURPInstanceProvider( + const Reference<XComponentContext>& rxContext) + : m_rContext(rxContext) +{ +} + +Reference<XInterface> FunctionBasedURPInstanceProvider::getInstance(const OUString& aName) +{ + Reference<XInterface> rInstance; + + if (aName == "StarOffice.ServiceManager") + { + rInstance.set(m_rContext->getServiceManager()); + } + else if (aName == "StarOffice.ComponentContext") + { + rInstance = m_rContext; + } + else if (aName == "StarOffice.NamingService") + { + Reference<XNamingService> rNamingService( + m_rContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.uno.NamingService", m_rContext), + UNO_QUERY); + if (rNamingService.is()) + { + rNamingService->registerObject("StarOffice.ServiceManager", + m_rContext->getServiceManager()); + rNamingService->registerObject("StarOffice.ComponentContext", m_rContext); + rInstance = rNamingService; + } + } + return rInstance; +} + +class FunctionBasedURPConnection : public cppu::WeakImplHelper<css::connection::XConnection> +{ +public: + explicit FunctionBasedURPConnection(void*, int (*)(void* pContext, const signed char* pBuffer, + int nLen)); + ~FunctionBasedURPConnection(); + + // These overridden member functions use "read" and "write" from the point of view of LO, + // i.e. the opposite to how startURP() uses them. + virtual sal_Int32 SAL_CALL read(Sequence<sal_Int8>& aReadBytes, + sal_Int32 nBytesToRead) override; + virtual void SAL_CALL write(const Sequence<sal_Int8>& aData) override; + virtual void SAL_CALL flush() override; + virtual void SAL_CALL close() override; + virtual OUString SAL_CALL getDescription() override; + void setBridge(Reference<XBridge>); + int addClientURPToBuffer(const signed char* pBuffer, int nLen); + void* getContext(); + inline static int g_connectionCount = 0; + +private: + std::shared_ptr<std::deque<signed char>> m_pBuffer; + void* m_pRecieveFromLOContext; + int (*m_fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen); + Reference<XBridge> m_URPBridge; + std::atomic<bool> m_closed = false; + std::condition_variable m_URPInBuffer; + std::mutex m_bufferMutex; +}; + +FunctionBasedURPConnection::FunctionBasedURPConnection( + void* pRecieveFromLOContext, + int (*fnRecieveFromLO)(void* pContext, const signed char* pBuffer, int nLen)) + : m_pBuffer(std::make_shared<std::deque<signed char>>()) + , m_pRecieveFromLOContext(pRecieveFromLOContext) + , m_fnReceiveURPFromLO(fnRecieveFromLO) +{ + g_connectionCount++; +} + +FunctionBasedURPConnection::~FunctionBasedURPConnection() +{ + Reference<XComponent> xComp(m_URPBridge, UNO_QUERY_THROW); + xComp->dispose(); // TODO: check this doesn't deadlock +} + +int sendURPToLO(void* pContext /* FunctionBasedURPConnection* */, const signed char* pBuffer, + int nLen) +{ + return static_cast<FunctionBasedURPConnection*>(pContext)->addClientURPToBuffer(pBuffer, nLen); +} + +int FunctionBasedURPConnection::addClientURPToBuffer(const signed char* pBuffer, int nLen) +{ + { + std::scoped_lock lock(m_bufferMutex); + + if (m_closed) + { + // We can't write URP to a closed connection + SAL_WARN("lok.urp", "A client attempted to write URP to a closed " + "FunctionBasedURPConnection... ignoring"); + return 0; + } + m_pBuffer->insert(m_pBuffer->end(), pBuffer, pBuffer + nLen); + } + m_URPInBuffer.notify_one(); + return nLen; +} + +void* FunctionBasedURPConnection::getContext() { return this; } + +sal_Int32 FunctionBasedURPConnection::read(Sequence<sal_Int8>& aReadBytes, sal_Int32 nBytesToRead) +{ + if (aReadBytes.getLength() != nBytesToRead) + { + aReadBytes.realloc(nBytesToRead); + } + + sal_Int8* result = aReadBytes.getArray(); + // As with osl::StreamPipe, we must always read nBytesToRead... + + { + std::unique_lock lock(m_bufferMutex); + + if (nBytesToRead < 0) + { + return 0; + } + m_URPInBuffer.wait( + lock, [this, nBytesToRead] { return static_cast<sal_Int32>(m_pBuffer->size()) >= nBytesToRead; }); + + std::copy(m_pBuffer->begin(), m_pBuffer->begin() + nBytesToRead, result); + m_pBuffer->erase(m_pBuffer->begin(), m_pBuffer->begin() + nBytesToRead); + } + + return nBytesToRead; +} + +void FunctionBasedURPConnection::write(const Sequence<sal_Int8>& aData) +{ + m_fnReceiveURPFromLO(m_pRecieveFromLOContext, aData.getConstArray(), aData.getLength()); +} + +void FunctionBasedURPConnection::flush() {} + +void FunctionBasedURPConnection::close() +{ + SAL_INFO("lok.urp", "Requested to close FunctionBasedURPConnection"); + m_closed = true; +} + +OUString FunctionBasedURPConnection::getDescription() { return ""; } + +void FunctionBasedURPConnection::setBridge(Reference<XBridge> xBridge) { m_URPBridge = xBridge; } +} + +static int +lo_startURP(LibreOfficeKit* /* pThis */, void* pRecieveFromLOContext, void** ppSendToLOContext, + int (*fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen), + int (**pfnSendURPToLO)(void* pContext, const signed char* pBuffer, int nLen)) +{ + // Here we will roughly do what desktop LO does when one passes a command-line switch like + // --accept=socket,port=nnnn;urp;StarOffice.ServiceManager. Except that no listening socket will + // be created. The communication to the URP will be through the fnReceiveURPFromLO and pfnSendURPToLO functions. + + rtl::Reference<FunctionBasedURPConnection> connection( + new FunctionBasedURPConnection(pRecieveFromLOContext, fnReceiveURPFromLO)); + + *pfnSendURPToLO = sendURPToLO; + *ppSendToLOContext = connection->getContext(); + + Reference<XBridgeFactory> xBridgeFactory = css::bridge::BridgeFactory::create(xContext); + + Reference<XInstanceProvider> xInstanceProvider(new FunctionBasedURPInstanceProvider(xContext)); + + Reference<XBridge> xBridge(xBridgeFactory->createBridge( + "functionurp" + OUString::number(FunctionBasedURPConnection::g_connectionCount), "urp", + connection, xInstanceProvider)); + + connection->setBridge(std::move(xBridge)); + + return true; +} + +/** + * Stop a function based URP connection that you started with lo_startURP above + * + * @param pSendToLOContext a pointer to the context you got back using your ppSendToLOContext before */ +static void lo_stopURP(LibreOfficeKit* /* pThis */, + void* pSendToLOContext /* FunctionBasedURPConnection* */) +{ + static_cast<FunctionBasedURPConnection*>(pSendToLOContext)->close(); +} + static void lo_registerCallback (LibreOfficeKit* pThis, LibreOfficeKitCallback pCallback, void* pData) |