blob: 9219768cde451876debc11bbc447b3318d416f84 [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 <decaf/util/Config.h>
#if !defined(HAVE_WINSOCK2_H)
#include <sys/select.h>
#include <sys/socket.h>
#else
#include <Winsock2.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#define BSD_COMP /* Get FIONREAD on Solaris2. */
#include <sys/ioctl.h>
#endif
// Pick up FIONREAD on Solaris 2.5.
#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#include <decaf/net/SocketInputStream.h>
#include <decaf/net/SocketError.h>
#include <decaf/io/IOException.h>
#include <decaf/lang/Character.h>
#include <decaf/lang/exceptions/UnsupportedOperationException.h>
#include <stdlib.h>
#include <string>
#include <stdio.h>
#include <iostream>
#include <apr_portable.h>
using namespace decaf;
using namespace decaf::net;
using namespace decaf::io;
using namespace decaf::util;
using namespace decaf::lang;
using namespace std;
////////////////////////////////////////////////////////////////////////////////
SocketInputStream::SocketInputStream( net::Socket::SocketHandle socket ) {
this->socket = socket;
this->closed = false;
}
////////////////////////////////////////////////////////////////////////////////
SocketInputStream::~SocketInputStream(){}
////////////////////////////////////////////////////////////////////////////////
void SocketInputStream::close() throw( lang::Exception ){
this->closed = true;
}
////////////////////////////////////////////////////////////////////////////////
std::size_t SocketInputStream::available() const throw ( io::IOException ){
// Convert to an OS level socket.
apr_os_sock_t* oss = NULL;
apr_os_sock_get( oss, socket );
// The windows version
#if defined(HAVE_WINSOCK2_H)
unsigned long numBytes = 0;
if( ::ioctlsocket( *oss, FIONREAD, &numBytes ) == SOCKET_ERROR ){
throw SocketException( __FILE__, __LINE__, "ioctlsocket failed" );
}
return (std::size_t)numBytes;
#else // !defined(HAVE_WINSOCK2_H)
// If FIONREAD is defined - use ioctl to find out how many bytes
// are available.
#if defined(FIONREAD)
std::size_t numBytes = 0;
if( ::ioctl( *oss, FIONREAD, &numBytes ) != -1 ){
return numBytes;
}
#endif
// If we didn't get anything we can use select. This is a little
// less functional. We will poll on the socket - if there is data
// available, we'll return 1, otherwise we'll return zero.
#if defined(HAVE_SELECT)
fd_set rd;
FD_ZERO(&rd);
FD_SET( *oss, &rd );
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
int returnCode = ::select( *oss+1, &rd, NULL, NULL, &tv );
if( returnCode == -1 ){
throw IOException(
__FILE__, __LINE__,
SocketError::getErrorString().c_str() );
}
return (returnCode == 0) ? 0 : 1;
#else
return 0;
#endif /* HAVE_SELECT */
#endif // !defined(HAVE_WINSOCK2_H)
}
////////////////////////////////////////////////////////////////////////////////
unsigned char SocketInputStream::read() throw ( IOException ){
apr_status_t result = APR_SUCCESS;
char c;
apr_size_t size = 1;
result = apr_socket_recv( socket, &c, &size );
if( ( size != sizeof(c) && !closed ) || result != APR_SUCCESS ){
throw IOException( __FILE__, __LINE__,
"activemq::io::SocketInputStream::read - failed reading a byte");
}
return c;
}
////////////////////////////////////////////////////////////////////////////////
int SocketInputStream::read( unsigned char* buffer,
std::size_t bufferSize ) throw ( IOException )
{
apr_size_t size = (apr_size_t)bufferSize;
apr_status_t result = APR_SUCCESS;
// Read data from the socket, size on input is size of buffer, when done
// size is the number of bytes actually read, can be <= bufferSize.
result = apr_socket_recv( socket, (char*)buffer, &size );
// Check for EOF, on windows we only get size==0 so check that to, if we
// were closed though then we throw an IOException so the caller knows we
// aren't usable anymore.
if( ( APR_STATUS_IS_EOF( result ) || size == 0 ) && !closed ) {
return -1;
}
// Check for a closed call from socket class, if closed then this read fails.
if( closed ){
throw IOException(
__FILE__, __LINE__,
"activemq::io::SocketInputStream::read - The connection is broken" );
}
// Check for error.
if( result != APR_SUCCESS ){
throw IOException(
__FILE__, __LINE__,
"decaf::net::SocketInputStream::read - %s",
SocketError::getErrorString().c_str() );
}
return size;
}
////////////////////////////////////////////////////////////////////////////////
std::size_t SocketInputStream::skip( std::size_t num DECAF_UNUSED )
throw ( io::IOException, lang::exceptions::UnsupportedOperationException ) {
throw lang::exceptions::UnsupportedOperationException(
__FILE__, __LINE__,
"SocketInputStream::skip() method is not supported");
}