blob: 40d8d8e21000711dc75adc3ea8b10658b7255758 [file] [log] [blame]
/*
* 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/logstring.h>
#include <log4cxx/helpers/socketimpl.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/pool.h>
#include <log4cxx/helpers/transcoder.h>
#include "apr_network_io.h"
#include "apr_lib.h"
using namespace log4cxx;
using namespace log4cxx::helpers;
IMPLEMENT_LOG4CXX_OBJECT(SocketImpl)
#include <string.h>
#include <assert.h>
#include <apr_support.h>
#include <apr_signal.h>
SocketException::SocketException() : errorNumber(0), msg("SocketException") {
}
SocketException::SocketException(const char *what1, apr_status_t status) :
errorNumber(status) {
// build the error message text
char buffer[200];
apr_strerror(status, buffer, sizeof buffer);
msg = std::string(what1) + std::string(": ") + buffer;
}
SocketException::SocketException(apr_status_t status) :
errorNumber(status) {
// build the error message text
char buffer[200];
apr_strerror(status, buffer, sizeof buffer);
msg = std::string("SocketException: ") + buffer;
}
SocketException::SocketException(const SocketException& src)
: IOException(src), errorNumber(src.getErrorNumber()) {
}
SocketException::~SocketException() throw() {
}
const char* SocketException::what() const throw() {
return msg.c_str();
}
apr_status_t SocketException::getErrorNumber() const {
return errorNumber;
}
PlatformSocketException::PlatformSocketException(const char* what1, apr_status_t status) :
SocketException(what1, status) {
}
PlatformSocketException::PlatformSocketException(const PlatformSocketException& src)
: SocketException(src) {
}
PlatformSocketException::~PlatformSocketException() throw() {
}
ConnectException::ConnectException()
: PlatformSocketException("ConnectException", 0) {
}
ConnectException::ConnectException(apr_status_t status)
: PlatformSocketException("ConnectException", status) {
}
ConnectException::ConnectException(const ConnectException& src)
: PlatformSocketException(src) {
}
ConnectException::~ConnectException() throw() {
}
BindException::BindException()
: PlatformSocketException("BindException", 0) {
}
BindException::BindException(apr_status_t status)
: PlatformSocketException("BindException", status) {
}
BindException::BindException(const BindException& src)
: PlatformSocketException(src) {
}
BindException::~BindException() throw() {
}
InterruptedIOException::InterruptedIOException() {
}
InterruptedIOException::InterruptedIOException(const InterruptedIOException& src)
: IOException(src) {
}
InterruptedIOException::~InterruptedIOException() throw() {
}
const char* InterruptedIOException::what() const throw() {
return "Interrupted IO exception";
}
SocketTimeoutException::SocketTimeoutException() {
}
SocketTimeoutException::SocketTimeoutException(const SocketTimeoutException& src)
: InterruptedIOException(src) {
}
SocketTimeoutException::~SocketTimeoutException() throw() {
}
const char* SocketTimeoutException::what() const throw() {
return "Socket timeout exception";
}
SocketImpl::SocketImpl() : address(), socket(0), localport(-1), port(0)
{
}
SocketImpl::~SocketImpl()
{
try
{
close();
}
catch(SocketException&)
{
}
}
/** Accepts a connection. */
void SocketImpl::accept(SocketImplPtr s)
{
//
// Socket timeout is ignored. See bug LOGCXX-177.
//
// Accept new connection
apr_socket_t *clientSocket = 0;
apr_status_t status =
apr_socket_accept(&clientSocket, socket, s->memoryPool.getAPRPool());
if (APR_STATUS_IS_EAGAIN(status)) {
throw InterruptedIOException();
}
if (status != APR_SUCCESS) {
throw SocketException(status);
}
// get client socket address
apr_sockaddr_t *client_addr;
status = apr_socket_addr_get(&client_addr, APR_REMOTE, clientSocket);
if (status != APR_SUCCESS) {
throw SocketException(status);
}
// retrieve the IP address from the client socket's apr_sockaddr_t
LogString ipAddrString;
char *ipAddr;
apr_sockaddr_ip_get(&ipAddr, client_addr);
Transcoder::decode(ipAddr, ipAddrString);
// retrieve the host name from the client socket's apr_sockaddr_t
LogString hostNameString;
char *hostName;
apr_getnameinfo(&hostName, client_addr, 0);
Transcoder::decode(hostName, hostNameString);
s->address = new InetAddress(hostNameString, ipAddrString);
s->socket = clientSocket;
s->port = client_addr->port;
}
/** Returns the number of bytes that can be read from this socket
without blocking.
*/
int SocketImpl::available()
{
// TODO
return 0;
}
/** Binds this socket to the specified port number
on the specified host.
*/
void SocketImpl::bind(InetAddressPtr address1, int port1)
{
LOG4CXX_ENCODE_CHAR(host, address1->getHostAddress());
// Create server socket address (including port number)
apr_sockaddr_t *server_addr;
apr_status_t status =
apr_sockaddr_info_get(&server_addr, host.c_str(), APR_INET,
port1, 0, memoryPool.getAPRPool());
if (status != APR_SUCCESS) {
throw ConnectException(status);
}
// bind the socket to the address
status = apr_socket_bind(socket, server_addr);
if (status != APR_SUCCESS) {
throw BindException(status);
}
this->localport = port1;
}
/** Closes this socket. */
void SocketImpl::close()
{
if (socket != 0) {
apr_status_t status = apr_socket_close(socket);
if (status != APR_SUCCESS) {
throw SocketException(status);
}
address = 0;
socket = 0;
port = 0;
localport = -1;
}
}
/** Connects this socket to the specified port number
on the specified host.
*/
void SocketImpl::connect(InetAddressPtr address1, int port1)
{
LOG4CXX_ENCODE_CHAR(host, address1->getHostAddress());
// create socket address (including port)
apr_sockaddr_t *client_addr;
apr_status_t status =
apr_sockaddr_info_get(&client_addr, host.c_str(), APR_INET,
port1, 0, memoryPool.getAPRPool());
if (status != APR_SUCCESS) {
throw ConnectException(status);
}
// connect the socket
status = apr_socket_connect(socket, client_addr);
if (status != APR_SUCCESS) {
throw ConnectException();
}
this->address = address1;
this->port = port1;
}
/** Connects this socket to the specified port on the named host. */
void SocketImpl::connect(const LogString& host, int port1)
{
connect(InetAddress::getByName(host), port1);
}
/** Creates either a stream or a datagram socket. */
void SocketImpl::create(bool stream)
{
apr_status_t status =
apr_socket_create(&socket, APR_INET, stream ? SOCK_STREAM : SOCK_DGRAM,
APR_PROTO_TCP, memoryPool.getAPRPool());
if (status != APR_SUCCESS) {
throw SocketException(status);
}
}
/** Sets the maximum queue length for incoming connection
indications (a request to connect) to the count argument.
*/
void SocketImpl::listen(int backlog)
{
apr_status_t status = apr_socket_listen (socket, backlog);
if (status != APR_SUCCESS) {
throw SocketException(status);
}
}
/** Returns the address and port of this socket as a String.
*/
LogString SocketImpl::toString() const
{
LogString oss(address->getHostAddress());
oss.append(1, (logchar) 0x3A /* ':' */);
Pool p;
StringHelper::toString(port, p, oss);
return oss;
}
// thanks to Yves Mettier (ymettier@libertysurf.fr) for this routine
size_t SocketImpl::read(void * buf, size_t len) const
{
char * p = (char *)buf;
while ((size_t)(p - (char *)buf) < len)
{
apr_size_t len_read = len - (p - (const char *)buf);
apr_status_t status = apr_socket_recv(socket, p, &len_read);
if (status != APR_SUCCESS) {
throw SocketException(status);
}
if (len_read == 0) {
break;
}
p += len_read;
}
return (p - (const char *)buf);
}
// thanks to Yves Mettier (ymettier@libertysurf.fr) for this routine
size_t SocketImpl::write(const void * buf, size_t len)
{
const char * p = (const char *)buf;
while ((size_t)(p - (const char *)buf) < len)
{
apr_size_t len_written = len - (p - (const char *)buf);
// while writing to the socket, we need to ignore the SIGPIPE
// signal. Otherwise, when the client has closed the connection,
// the send() function would not return an error but call the
// SIGPIPE handler.
#if APR_HAVE_SIGACTION
apr_sigfunc_t* old = apr_signal(SIGPIPE, SIG_IGN);
apr_status_t status = apr_socket_send(socket, p, &len_written);
apr_signal(SIGPIPE, old);
#else
apr_status_t status = apr_socket_send(socket, p, &len_written);
#endif
if (status != APR_SUCCESS) {
throw SocketException(status);
}
if (len_written == 0) {
break;
}
p += len_written;
}
return (p - (const char *)buf);
}
/** Retrive setting for SO_TIMEOUT.
*/
int SocketImpl::getSoTimeout() const
{
apr_interval_time_t timeout;
apr_status_t status = apr_socket_timeout_get(socket, &timeout);
if (status != APR_SUCCESS) {
throw SocketException(status);
}
return timeout / 1000;
}
/** Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds.
*/
void SocketImpl::setSoTimeout(int timeout)
{
apr_interval_time_t time = timeout * 1000;
apr_status_t status = apr_socket_timeout_set(socket, time);
if (status != APR_SUCCESS) {
throw SocketException(status);
}
}