/src/libreoffice/io/source/acceptor/acc_socket.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include "acceptor.hxx" |
21 | | |
22 | | #include <unordered_set> |
23 | | |
24 | | #include <mutex> |
25 | | #include <rtl/ref.hxx> |
26 | | #include <com/sun/star/connection/XConnection.hpp> |
27 | | #include <com/sun/star/connection/XConnectionBroadcaster.hpp> |
28 | | #include <com/sun/star/connection/ConnectionSetupException.hpp> |
29 | | #include <com/sun/star/io/IOException.hpp> |
30 | | #include <cppuhelper/implbase.hxx> |
31 | | #include <utility> |
32 | | |
33 | | using namespace ::osl; |
34 | | using namespace ::cppu; |
35 | | using namespace ::com::sun::star::uno; |
36 | | using namespace ::com::sun::star::io; |
37 | | using namespace ::com::sun::star::connection; |
38 | | |
39 | | |
40 | | namespace io_acceptor { |
41 | | |
42 | | typedef std::unordered_set< css::uno::Reference< css::io::XStreamListener> > |
43 | | XStreamListener_hash_set; |
44 | | |
45 | | namespace { |
46 | | |
47 | | class SocketConnection : public ::cppu::WeakImplHelper< |
48 | | css::connection::XConnection, |
49 | | css::connection::XConnectionBroadcaster> |
50 | | |
51 | | { |
52 | | public: |
53 | | explicit SocketConnection( OUString sConnectionDescription ); |
54 | | |
55 | | virtual sal_Int32 SAL_CALL read( css::uno::Sequence< sal_Int8 >& aReadBytes, |
56 | | sal_Int32 nBytesToRead ) override; |
57 | | virtual void SAL_CALL write( const css::uno::Sequence< sal_Int8 >& aData ) override; |
58 | | virtual void SAL_CALL flush( ) override; |
59 | | virtual void SAL_CALL close( ) override; |
60 | | virtual OUString SAL_CALL getDescription( ) override; |
61 | | |
62 | | // XConnectionBroadcaster |
63 | | virtual void SAL_CALL addStreamListener(const css::uno::Reference< css::io::XStreamListener>& aListener) override; |
64 | | virtual void SAL_CALL removeStreamListener(const css::uno::Reference< css::io::XStreamListener>& aListener) override; |
65 | | |
66 | | public: |
67 | | void completeConnectionString(); |
68 | | |
69 | | ::osl::StreamSocket m_socket; |
70 | | oslInterlockedCount m_nStatus; |
71 | | OUString m_sDescription; |
72 | | |
73 | | std::mutex _mutex; |
74 | | bool _started; |
75 | | bool _closed; |
76 | | bool _error; |
77 | | XStreamListener_hash_set _listeners; |
78 | | }; |
79 | | |
80 | | } |
81 | | |
82 | | template<class T> |
83 | | static void notifyListeners(SocketConnection * pCon, bool * notified, T t) |
84 | 0 | { |
85 | 0 | XStreamListener_hash_set listeners; |
86 | |
|
87 | 0 | { |
88 | 0 | std::unique_lock guard(pCon->_mutex); |
89 | 0 | if(!*notified) |
90 | 0 | { |
91 | 0 | *notified = true; |
92 | 0 | listeners = pCon->_listeners; |
93 | 0 | } |
94 | 0 | } |
95 | |
|
96 | 0 | for(auto& listener : listeners) |
97 | 0 | t(listener); |
98 | 0 | } Unexecuted instantiation: acc_socket.cxx:void io_acceptor::notifyListeners<void (*)(com::sun::star::uno::Reference<com::sun::star::io::XStreamListener> const&)>(io_acceptor::(anonymous namespace)::SocketConnection*, bool*, void (*)(com::sun::star::uno::Reference<com::sun::star::io::XStreamListener> const&)) Unexecuted instantiation: acc_socket.cxx:void io_acceptor::notifyListeners<io_acceptor::(anonymous namespace)::callError>(io_acceptor::(anonymous namespace)::SocketConnection*, bool*, io_acceptor::(anonymous namespace)::callError) |
99 | | |
100 | | static void callStarted(const Reference<XStreamListener>& xStreamListener) |
101 | 0 | { |
102 | 0 | xStreamListener->started(); |
103 | 0 | } |
104 | | |
105 | | namespace { |
106 | | |
107 | | struct callError { |
108 | | const Any & any; |
109 | | |
110 | | explicit callError(const Any & any); |
111 | | |
112 | | void operator () (const Reference<XStreamListener>& xStreamListener); |
113 | | }; |
114 | | |
115 | | } |
116 | | |
117 | | callError::callError(const Any & aAny) |
118 | 0 | : any(aAny) |
119 | 0 | { |
120 | 0 | } |
121 | | |
122 | | void callError::operator () (const Reference<XStreamListener>& xStreamListener) |
123 | 0 | { |
124 | 0 | xStreamListener->error(any); |
125 | 0 | } |
126 | | |
127 | | static void callClosed(const Reference<XStreamListener>& xStreamListener) |
128 | 0 | { |
129 | 0 | xStreamListener->closed(); |
130 | 0 | } |
131 | | |
132 | | |
133 | | SocketConnection::SocketConnection( OUString sConnectionDescription) : |
134 | 0 | m_nStatus( 0 ), |
135 | 0 | m_sDescription(std::move( sConnectionDescription )), |
136 | 0 | _started(false), |
137 | 0 | _closed(false), |
138 | 0 | _error(false) |
139 | 0 | { |
140 | | // make it unique |
141 | 0 | m_sDescription += ",uniqueValue=" ; |
142 | 0 | m_sDescription += OUString::number( |
143 | 0 | sal::static_int_cast< sal_Int64 >( |
144 | 0 | reinterpret_cast< sal_IntPtr >(&m_socket)) ); |
145 | 0 | } |
146 | | |
147 | | void SocketConnection::completeConnectionString() |
148 | 0 | { |
149 | 0 | m_sDescription += |
150 | 0 | ",peerPort=" + OUString::number(m_socket.getPeerPort()) + |
151 | 0 | ",peerHost=" + m_socket.getPeerHost( ) + |
152 | 0 | ",localPort=" + OUString::number( m_socket.getLocalPort() ) + |
153 | 0 | ",localHost=" + m_socket.getLocalHost(); |
154 | 0 | } |
155 | | |
156 | | sal_Int32 SocketConnection::read( Sequence < sal_Int8 > & aReadBytes , sal_Int32 nBytesToRead ) |
157 | 0 | { |
158 | 0 | if( ! m_nStatus ) |
159 | 0 | { |
160 | 0 | notifyListeners(this, &_started, callStarted); |
161 | |
|
162 | 0 | if( aReadBytes.getLength() != nBytesToRead ) |
163 | 0 | { |
164 | 0 | aReadBytes.realloc( nBytesToRead ); |
165 | 0 | } |
166 | |
|
167 | 0 | sal_Int32 i = m_socket.read( |
168 | 0 | aReadBytes.getArray(), aReadBytes.getLength()); |
169 | |
|
170 | 0 | if(i != nBytesToRead) |
171 | 0 | { |
172 | 0 | OUString message = "acc_socket.cxx:SocketConnection::read: error - " + |
173 | 0 | m_socket.getErrorAsString(); |
174 | |
|
175 | 0 | IOException ioException(message, static_cast<XConnection *>(this)); |
176 | |
|
177 | 0 | Any any; |
178 | 0 | any <<= ioException; |
179 | |
|
180 | 0 | notifyListeners(this, &_error, callError(any)); |
181 | |
|
182 | 0 | throw ioException; |
183 | 0 | } |
184 | | |
185 | 0 | return i; |
186 | 0 | } |
187 | 0 | else |
188 | 0 | { |
189 | 0 | IOException ioException(u"acc_socket.cxx:SocketConnection::read: error - connection already closed"_ustr, static_cast<XConnection *>(this)); |
190 | |
|
191 | 0 | Any any; |
192 | 0 | any <<= ioException; |
193 | |
|
194 | 0 | notifyListeners(this, &_error, callError(any)); |
195 | |
|
196 | 0 | throw ioException; |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | void SocketConnection::write( const Sequence < sal_Int8 > &seq ) |
201 | 0 | { |
202 | 0 | if( ! m_nStatus ) |
203 | 0 | { |
204 | 0 | if( m_socket.write( seq.getConstArray() , seq.getLength() ) != seq.getLength() ) |
205 | 0 | { |
206 | 0 | OUString message = "acc_socket.cxx:SocketConnection::write: error - " + |
207 | 0 | m_socket.getErrorAsString(); |
208 | |
|
209 | 0 | IOException ioException(message, static_cast<XConnection *>(this)); |
210 | |
|
211 | 0 | Any any; |
212 | 0 | any <<= ioException; |
213 | |
|
214 | 0 | notifyListeners(this, &_error, callError(any)); |
215 | |
|
216 | 0 | throw ioException; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | else |
220 | 0 | { |
221 | 0 | IOException ioException(u"acc_socket.cxx:SocketConnection::write: error - connection already closed"_ustr, static_cast<XConnection *>(this)); |
222 | |
|
223 | 0 | Any any; |
224 | 0 | any <<= ioException; |
225 | |
|
226 | 0 | notifyListeners(this, &_error, callError(any)); |
227 | |
|
228 | 0 | throw ioException; |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | | void SocketConnection::flush( ) |
233 | 0 | { |
234 | |
|
235 | 0 | } |
236 | | |
237 | | void SocketConnection::close() |
238 | 0 | { |
239 | | // ensure close is called only once |
240 | 0 | if( 1 == osl_atomic_increment( (&m_nStatus) ) ) |
241 | 0 | { |
242 | 0 | m_socket.shutdown(); |
243 | 0 | notifyListeners(this, &_closed, callClosed); |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | OUString SocketConnection::getDescription() |
248 | 0 | { |
249 | 0 | return m_sDescription; |
250 | 0 | } |
251 | | |
252 | | |
253 | | // XConnectionBroadcaster |
254 | | void SAL_CALL SocketConnection::addStreamListener(const Reference<XStreamListener> & aListener) |
255 | 0 | { |
256 | 0 | std::unique_lock guard(_mutex); |
257 | |
|
258 | 0 | _listeners.insert(aListener); |
259 | 0 | } |
260 | | |
261 | | void SAL_CALL SocketConnection::removeStreamListener(const Reference<XStreamListener> & aListener) |
262 | 0 | { |
263 | 0 | std::unique_lock guard(_mutex); |
264 | |
|
265 | 0 | _listeners.erase(aListener); |
266 | 0 | } |
267 | | |
268 | | SocketAcceptor::SocketAcceptor( OUString sSocketName, |
269 | | sal_uInt16 nPort, |
270 | | bool bTcpNoDelay, |
271 | | OUString sConnectionDescription) : |
272 | 0 | m_sSocketName(std::move( sSocketName )), |
273 | 0 | m_sConnectionDescription(std::move( sConnectionDescription )), |
274 | 0 | m_nPort( nPort ), |
275 | 0 | m_bTcpNoDelay( bTcpNoDelay ), |
276 | 0 | m_bClosed( false ) |
277 | 0 | { |
278 | 0 | } |
279 | | |
280 | | |
281 | | void SocketAcceptor::init() |
282 | 0 | { |
283 | 0 | if( ! m_addr.setPort( m_nPort ) ) |
284 | 0 | { |
285 | 0 | throw ConnectionSetupException( |
286 | 0 | "acc_socket.cxx:SocketAcceptor::init - error - invalid tcp/ip port " + |
287 | 0 | OUString::number( m_nPort )); |
288 | 0 | } |
289 | 0 | if( ! m_addr.setHostname( m_sSocketName.pData ) ) |
290 | 0 | { |
291 | 0 | throw ConnectionSetupException( |
292 | 0 | "acc_socket.cxx:SocketAcceptor::init - error - invalid host " + m_sSocketName ); |
293 | 0 | } |
294 | 0 | m_socket.setOption( osl_Socket_OptionReuseAddr, 1); |
295 | |
|
296 | 0 | if(! m_socket.bind(m_addr) ) |
297 | 0 | { |
298 | 0 | throw ConnectionSetupException( |
299 | 0 | "acc_socket.cxx:SocketAcceptor::init - error - couldn't bind on " + |
300 | 0 | m_sSocketName + ":" + OUString::number(m_nPort)); |
301 | 0 | } |
302 | | |
303 | 0 | if(! m_socket.listen() ) |
304 | 0 | { |
305 | 0 | throw ConnectionSetupException( |
306 | 0 | "acc_socket.cxx:SocketAcceptor::init - error - can't listen on " + |
307 | 0 | m_sSocketName + ":" + OUString::number(m_nPort) ); |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | | Reference< XConnection > SocketAcceptor::accept( ) |
312 | 0 | { |
313 | 0 | rtl::Reference<SocketConnection> pConn(new SocketConnection( m_sConnectionDescription )); |
314 | |
|
315 | 0 | if( m_socket.acceptConnection( pConn->m_socket )!= osl_Socket_Ok ) |
316 | 0 | { |
317 | | // stopAccepting was called |
318 | 0 | return Reference < XConnection > (); |
319 | 0 | } |
320 | 0 | if( m_bClosed ) |
321 | 0 | { |
322 | 0 | return Reference < XConnection > (); |
323 | 0 | } |
324 | | |
325 | 0 | pConn->completeConnectionString(); |
326 | 0 | ::osl::SocketAddr remoteAddr; |
327 | 0 | pConn->m_socket.getPeerAddr(remoteAddr); |
328 | 0 | OUString remoteHostname = remoteAddr.getHostname(); |
329 | | // we enable tcpNoDelay for loopback connections because |
330 | | // it can make a significant speed difference on linux boxes. |
331 | 0 | if( m_bTcpNoDelay || remoteHostname == "localhost" || |
332 | 0 | remoteHostname.startsWith("127.0.0.") ) |
333 | 0 | { |
334 | 0 | sal_Int32 nTcpNoDelay = sal_Int32(true); |
335 | 0 | pConn->m_socket.setOption( osl_Socket_OptionTcpNoDelay , &nTcpNoDelay, |
336 | 0 | sizeof( nTcpNoDelay ) , osl_Socket_LevelTcp ); |
337 | 0 | } |
338 | |
|
339 | 0 | return pConn; |
340 | 0 | } |
341 | | |
342 | | void SocketAcceptor::stopAccepting() |
343 | 0 | { |
344 | 0 | m_bClosed = true; |
345 | 0 | m_socket.close(); |
346 | 0 | } |
347 | | } |
348 | | |
349 | | |
350 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |