| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| * (the "License"); you may not use this file except in compliance with | |
| * the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include <log4cxx/private/aprserversocket.h> | |
| #include <log4cxx/private/serversocket_priv.h> | |
| #include <log4cxx/private/aprsocket.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include "apr_network_io.h" | |
| #include "apr_pools.h" | |
| #include "apr_poll.h" | |
| namespace LOG4CXX_NS | |
| { | |
| namespace helpers | |
| { | |
| #define _priv static_cast<APRServerSocketPriv*>(m_priv.get()) | |
| struct APRServerSocket::APRServerSocketPriv : public ServerSocketPrivate { | |
| Pool pool; | |
| std::mutex mutex; | |
| apr_socket_t* socket{ 0 }; | |
| apr_pollset_t* pSet{ 0 }; | |
| }; | |
| APRServerSocket::APRServerSocket(int port, bool reuseAddress, const LogString& hostname) : | |
| ServerSocket(std::make_unique<APRServerSocketPriv>()){ | |
| apr_status_t status = | |
| apr_socket_create(&_priv->socket, APR_INET, SOCK_STREAM, | |
| APR_PROTO_TCP, _priv->pool.getAPRPool()); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw SocketException(status); | |
| } | |
| status = apr_socket_opt_set(_priv->socket, APR_SO_NONBLOCK, 1); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw SocketException(status); | |
| } | |
| if (reuseAddress) { | |
| apr_status_t status = apr_socket_opt_set(_priv->socket, APR_SO_REUSEADDR, 1); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw SocketException(status); | |
| } | |
| } | |
| // Create server socket address (including port number) | |
| std::string hostname_str; | |
| const char* hostname_ptr = NULL; | |
| if (!hostname.empty()) { | |
| Transcoder::encode(hostname, hostname_str); | |
| hostname_ptr = hostname_str.c_str(); | |
| } | |
| apr_sockaddr_t* server_addr; | |
| status = | |
| apr_sockaddr_info_get(&server_addr, hostname_ptr, APR_INET, | |
| port, 0, _priv->pool.getAPRPool()); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw ConnectException(status); | |
| } | |
| // bind the socket to the address | |
| status = apr_socket_bind(_priv->socket, server_addr); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw BindException(status); | |
| } | |
| status = apr_socket_listen(_priv->socket, 50); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw SocketException(status); | |
| } | |
| } | |
| void APRServerSocket::close() | |
| { | |
| std::lock_guard<std::mutex> lock(_priv->mutex); | |
| if (_priv->pSet) | |
| apr_pollset_wakeup(_priv->pSet); | |
| if (_priv->socket != 0) | |
| { | |
| apr_status_t status = apr_socket_close(_priv->socket); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw SocketException(status); | |
| } | |
| _priv->socket = 0; | |
| } | |
| } | |
| /** Listens for a connection to be made to this socket and | |
| accepts it | |
| */ | |
| SocketPtr APRServerSocket::accept() | |
| { | |
| apr_socket_t* s; | |
| { | |
| std::lock_guard<std::mutex> lock(_priv->mutex); | |
| s = _priv->socket; | |
| } | |
| if (s == 0) | |
| { | |
| throw NullPointerException(LOG4CXX_STR("socket")); | |
| } | |
| if (!_priv->pSet) | |
| { | |
| apr_pollfd_t poll; | |
| poll.p = _priv->pool.getAPRPool(); | |
| poll.desc_type = APR_POLL_SOCKET; | |
| poll.reqevents = APR_POLLIN; | |
| poll.rtnevents = 0; | |
| poll.desc.s = s; | |
| poll.client_data = NULL; | |
| auto status = apr_pollset_create(&_priv->pSet, 1, _priv->pool.getAPRPool(), APR_POLLSET_WAKEABLE); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw SocketException(status); | |
| } | |
| apr_pollset_add(_priv->pSet, &poll); | |
| } | |
| const apr_pollfd_t* descriptors; | |
| apr_int32_t signaled; | |
| apr_interval_time_t to = _priv->timeout * 1000; | |
| apr_status_t status = apr_pollset_poll(_priv->pSet, to, &signaled, &descriptors); | |
| if (APR_STATUS_IS_TIMEUP(status)) | |
| { | |
| throw SocketTimeoutException(); | |
| } | |
| else if (status != APR_SUCCESS) | |
| { | |
| throw SocketException(status); | |
| } | |
| apr_pool_t* newPool; | |
| status = apr_pool_create(&newPool, 0); | |
| if (status != APR_SUCCESS) | |
| { | |
| throw PoolException(status); | |
| } | |
| apr_socket_t* newSocket; | |
| status = apr_socket_accept(&newSocket, s, newPool); | |
| if (status != APR_SUCCESS) | |
| { | |
| apr_pool_destroy(newPool); | |
| throw SocketException(status); | |
| } | |
| status = apr_socket_opt_set(newSocket, APR_SO_NONBLOCK, 0); | |
| if (status != APR_SUCCESS) | |
| { | |
| apr_pool_destroy(newPool); | |
| throw SocketException(status); | |
| } | |
| return std::make_shared<APRSocket>(newSocket, newPool); | |
| } | |
| } //namespace helpers | |
| } //namespace log4cxx |