| /* |
| * Copyright 2003-2004 The Apache Software Foundation. |
| // (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved |
| * |
| * Licensed 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. |
| */ |
| |
| /* |
| * @author Lilantha Darshana (lilantha@virtusa.com) |
| * @author Damitha Kumarage (damitha@jkcsworld.com, damitha@opensource.lk) |
| * @author Samisa Abeysinghe (sabeysinghe@virtusa.com) |
| */ |
| |
| #include "Platform.h" |
| #include "Channel.h" |
| #include "../../platforms/PlatformAutoSense.hpp" |
| #include <iostream> |
| #include <stdio.h> |
| |
| |
| using namespace std; |
| /** |
| * Create a Channel & initialize |
| * |
| */ |
| |
| /** Channel::Channel() Constructor |
| */ |
| Channel::Channel ():m_Sock (INVALID_SOCKET), m_iMsgLength( 0), m_sMsg(NULL), m_lTimeoutSeconds(0), m_bUseProxy(false) |
| { |
| |
| #ifdef WIN32 |
| m_lTimeoutSeconds = 10; |
| #endif |
| |
| } |
| |
| /** Channel::~Channel() Destructor |
| */ |
| Channel::~Channel () |
| { |
| |
| // If the socket value is not invalid, then close the socket before |
| // deleting the Channel object. |
| if( m_Sock != INVALID_SOCKET) |
| { |
| closeChannel(); |
| } |
| |
| if (m_sMsg) |
| delete [] m_sMsg; |
| |
| } |
| |
| /** Channel::setURL( URL) sets the URL for the channel. |
| * |
| * @param const char* URL pointer to a NULL terminated character string that |
| * contains the new URL for the channel. |
| */ |
| void Channel::setURL(const char* cpURL) |
| { |
| |
| // Set the Channel URL to the new value. |
| // NB: This will cause problems if the new URL is diferent in type from the |
| // old URL. e.g. if the new URL is https and the old URL was http, then |
| // problems will occur because each type operates differently. |
| m_URL.setURL( cpURL); |
| |
| } |
| |
| /** Channel::getURL() gets the URL for the channel. |
| * |
| * @return const char* URL pointer to a NULL terminated character string that |
| * contains the URL for the channel. |
| */ |
| const char* Channel::getURL() |
| { |
| |
| // Return the URL currently assicated with the channel object. |
| return m_URL.getURL(); |
| } |
| |
| |
| /** |
| * This channel open INET channel for the time being using primitive sockets |
| * Do we need any other type of channel; like shared memory, pipes etc. ???? |
| * |
| * @param p_RemoteNode End point address as hostname/IP |
| * @param p_RemoteEnd Port # |
| * |
| * @return true if successfuly open a soket to the endpoint. o/w exception is |
| * thrown |
| */ |
| |
| bool |
| Channel::open () //std::string & p_RemoteNode, unsigned short p_RemoteEnd) |
| throw (AxisTransportException&) |
| { |
| // if there is an open socket already, close it first |
| if (m_Sock != INVALID_SOCKET) |
| closeChannel(); |
| |
| // If the underlying socket transport has not been initialised properly, |
| // then thrown an exeption. |
| if( !Init()) |
| { |
| |
| throw AxisTransportException( SERVER_TRANSPORT_CHANNEL_INIT_ERROR); |
| } |
| |
| // If the transport was initilised, then create client and server sockets. |
| sockaddr_in clAddr; |
| |
| // Create the Client (Rx) side first. |
| if( (m_Sock = socket( PF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET) |
| { |
| clAddr.sin_family = AF_INET; // AF_INET (address family Internet). |
| clAddr.sin_port = 0; // No Specify Port required. |
| clAddr.sin_addr.s_addr = INADDR_ANY; |
| |
| // Attempt to bind the client to the client socket. |
| if( bind( m_Sock, (struct sockaddr *) &clAddr, sizeof( clAddr)) == SOCKET_ERROR) |
| { |
| // Error whilst binding. Cannot open a channel to the remote end, |
| // shutting down the channel and then throw an exception. |
| closeChannel(); |
| |
| |
| throw AxisTransportException( SERVER_TRANSPORT_SOCKET_CONNECT_ERROR); |
| } |
| |
| // Although the above fragment makes use of the bind() API, it would be |
| // just as effective to skip over this call as there are no specific |
| // local port ID requirements for this client. The only advantage that |
| // bind() offers is the accessibility of the port which the system |
| // chose via the .sin_port member of the cli_addr structure which will |
| // be set upon success of the bind() call. |
| |
| // Create the Server (Tx) side. |
| |
| sockaddr_in svAddr; |
| struct hostent * pHostEntry = NULL; |
| |
| const char* host = m_URL.getHostName(); |
| unsigned int port = m_URL.getPort(); |
| if (m_bUseProxy) |
| { |
| port = m_uiProxyPort; |
| host = m_strProxyHost.c_str(); |
| } |
| svAddr.sin_family = AF_INET; |
| svAddr.sin_port = htons (port); |
| |
| // Probably this is the host-name of the server we are connecting to... |
| if( (pHostEntry = gethostbyname(host))) |
| { |
| svAddr.sin_addr.s_addr = ((struct in_addr *) pHostEntry->h_addr)->s_addr; |
| } |
| else |
| { |
| // No this is the IP address |
| svAddr.sin_addr.s_addr = inet_addr (host); |
| } |
| |
| // Attempt to connect to the remote server. |
| if( connect( m_Sock, (struct sockaddr *) &svAddr, sizeof (svAddr)) == SOCKET_ERROR) |
| { |
| // Cannot open a channel to the remote end, shutting down the |
| // channel and then throw an exception. |
| |
| // Before we do anything else get the last error message; |
| long dw = GETLASTERROR |
| |
| closeChannel(); |
| |
| string* message = PLATFORM_GET_ERROR_MESSAGE(dw); |
| |
| char fullMessage[600]; |
| sprintf(fullMessage, |
| "Failed to open connection to server: \n \ |
| hostname='%s'\n\ |
| port='%d'\n\ |
| Error Message='%s'\ |
| Error Code='%d'\n", |
| m_URL.getHostName(), m_URL.getPort(), message->c_str(), dw); |
| |
| delete(message); |
| |
| throw AxisTransportException( CLIENT_TRANSPORT_OPEN_CONNECTION_FAILED, fullMessage); |
| } |
| |
| } |
| else |
| { |
| // Sockets error Couldn't create socket. Close the channel and throw |
| // an exception. |
| closeChannel(); |
| |
| |
| throw AxisTransportException( SERVER_TRANSPORT_SOCKET_CREATE_ERROR); |
| } |
| |
| |
| /* Turn off the Nagle algorithm - Patch by Steve Hardy */ |
| |
| /* This is needed, because our TCP stack would otherwise wait at most |
| * 200 ms before actually sending data to the server (while waiting for |
| * a full packet). This limits performance to around 5 requests per |
| * second, which is not acceptable. Turning off the Nagle algorithm |
| * allows for much faster transmission of small packets, but may |
| * degrade high-bandwidth transmissions. |
| */ |
| |
| int one = 1; |
| setsockopt(m_Sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(int)); |
| |
| return true; |
| } |
| |
| /** |
| * Channel::Init() Operating System specific initialization for sockets |
| * |
| * @return bool True if successfully initialised the OS specifics. |
| * False if unsuccessful |
| */ |
| |
| bool Channel::Init () |
| { |
| |
| #ifdef WIN32 |
| WSADATA wsaData; // Contains vendor-specific information, such as the |
| // maximum number of sockets available and the maximum |
| // datagram size. |
| |
| // wsaData filled by Windows Sockets DLLs. |
| if( WSAStartup( WS_VERSION_REQD, &wsaData)) |
| { |
| // Error - Could not setup underlying Windows socket transport |
| // mechanism. |
| m_LastErr = "WinSock DLL not responding."; |
| |
| return false; |
| } |
| else |
| { |
| // Query to see whether the available version matches what is required |
| if ((LOBYTE( wsaData.wVersion) < WS_VERSION_MAJOR()) || |
| (LOBYTE( wsaData.wVersion) == WS_VERSION_MAJOR() && |
| HIBYTE( wsaData.wVersion) < WS_VERSION_MINOR())) |
| { |
| // Error - Underlying Windows socket transport version is not |
| // compatible with what is required. |
| char buf[100]; |
| |
| sprintf( buf, |
| "Windows Sockets version %d.%d not supported by winsock2.dll", |
| LOBYTE (wsaData.wVersion), HIBYTE (wsaData.wVersion)); |
| |
| Error( buf); |
| |
| closeChannel(); |
| |
| |
| return false; |
| } |
| } |
| #else |
| /* cout << "no need for linux" << endl; */ |
| /* other OS specific Intitialization goes here */ |
| #endif |
| |
| |
| return true; |
| } |
| |
| /** |
| * Channel::operator << (Msg) Write (send) a message to the remote server. |
| * Sending a message will block the application (i.e. synchronous communication). |
| * NB: We may need to do this asynchronizely; preferably either non-blocking |
| * send or pthread. |
| * |
| * @param const char * Msg NULL terminated character string containing the |
| * message to be written to the open channel. |
| * NB: For GSKit SSL, the message has been encoded and may contain embedded |
| * null characters. To overcome this problem, the m_iMsgLength value must be |
| * set to the message length before calling this method. If m_iMsgLength is |
| * not 0, this method will assume the the value is the message length. |
| * m_iMsgLength is set to 0 in the constructor, so unless it is intentionally |
| * changed, it will not have any effect on this method. |
| * |
| * @return const Channel & is the address of the Channel object (the 'this' |
| * pointer). |
| */ |
| const Channel &Channel::operator << (const char *msg) |
| { |
| // Check that the Tx/Rx sockets are valid (this will have been done if the |
| // application has called the open method first. |
| if( INVALID_SOCKET == m_Sock) |
| { |
| // Error - Writing cannot be done without having a open socket to |
| // remote end. Throw an exception. |
| |
| throw AxisTransportException( SERVER_TRANSPORT_INVALID_SOCKET); |
| } |
| |
| int size = strlen( msg); |
| int nByteSent; |
| |
| if( m_iMsgLength) |
| { |
| size = m_iMsgLength; |
| } |
| |
| |
| if( (nByteSent = send( m_Sock, msg, size, MSG_DONTROUTE)) == SOCKET_ERROR) |
| { |
| // Output streaming error while writing data. Close the channel and |
| // throw an exception. |
| closeChannel(); |
| |
| |
| throw AxisTransportException(SERVER_TRANSPORT_OUTPUT_STREAMING_ERROR); |
| } |
| |
| |
| return *this; |
| } |
| |
| /** |
| * Channel::operator >> (Msg) Read (receive) a message from the remote server; |
| * Reading may be done in 'chunks'. |
| * |
| * @param std::string & Msg is string that will receive the message. |
| * NB: For GSKit SSL, the message has been encoded and may contain embedded |
| * null characters. To overcome this problem, the m_iMsgLength value must be |
| * set to the negative of the message length before calling this method. If |
| * m_iMsgLength is not negative, this method will assume the BUF_SIZE value is |
| * the message length. m_iMsgLength is set to 0 in the constructor, so unless |
| * it is intentionally changed, it will not have any effect on this method. |
| * |
| * @return const Channel & is the address of the Channel object (the 'this' |
| * pointer). |
| */ |
| const Channel &Channel::operator >> (std::string & msg) |
| { |
| |
| msg = ""; |
| |
| if (INVALID_SOCKET == m_Sock) |
| { |
| // Error - Reading cannot be done without having a open socket Input |
| // streaming error on undefined channel; please open the |
| // channel first |
| |
| |
| throw AxisTransportException( SERVER_TRANSPORT_INVALID_SOCKET); |
| } |
| |
| int nByteRecv = 0; |
| char buf[BUF_SIZE]; |
| int iBufSize = BUF_SIZE; |
| |
| if( m_iMsgLength < 0) |
| { |
| iBufSize = -m_iMsgLength; |
| } |
| else |
| { |
| iBufSize--; |
| } |
| |
| //assume timeout not set; set default tatus to OK |
| int iTimeoutStatus = 1; |
| |
| //check if timeout set |
| if(m_lTimeoutSeconds) |
| { |
| iTimeoutStatus = applyTimeout(); |
| } |
| |
| // Handle timeout outcome |
| if( iTimeoutStatus < 0) |
| { |
| // Error |
| |
| |
| // Select SOCKET_ERROR. Channel error while waiting for timeout |
| throw AxisTransportException( SERVER_TRANSPORT_TIMEOUT_EXCEPTION, |
| "Channel error while waiting for timeout"); |
| } |
| |
| if( iTimeoutStatus == 0) |
| { |
| // Timeout expired - select timeout expired. |
| // Channel error connection timeout before receving |
| |
| |
| throw AxisTransportException( SERVER_TRANSPORT_TIMEOUT_EXPIRED, |
| "Channel error: connection timed out before receving"); |
| } |
| |
| // Either timeout was not set or data available before timeout; so read |
| |
| if( (nByteRecv = recv( m_Sock, (char *) &buf, iBufSize, 0)) == SOCKET_ERROR) |
| { |
| // Recv SOCKET_ERROR, Channel error while getting data |
| /* closeChannel(); */ |
| |
| |
| throw AxisTransportException( SERVER_TRANSPORT_INPUT_STREAMING_ERROR, |
| "Channel error while getting data"); |
| } |
| |
| if( nByteRecv) |
| { |
| /* printf("nByteRecv:%d\n", nByteRecv); */ |
| buf[nByteRecv] = '\0'; |
| /* got a part of the message, so add " \ "to form */ |
| msg = buf; |
| |
| if( m_iMsgLength < 0) |
| { |
| m_iMsgLength = nByteRecv; |
| |
| if (m_sMsg) |
| { |
| delete [] m_sMsg; |
| m_sMsg = NULL; |
| } |
| m_sMsg = new char[nByteRecv]; |
| |
| memcpy( m_sMsg, buf, nByteRecv); |
| } |
| } |
| else |
| { |
| if( m_iMsgLength < 0) |
| { |
| m_iMsgLength = nByteRecv; |
| } |
| ;//printf ("execution break\n"); |
| } |
| |
| return *this; |
| } |
| |
| /** |
| * Channel::readNonBlocking( Msg, BlockingRequired) Read (receive) a message |
| * from the remote server; Reading may be done in 'chunks'? |
| * |
| * @param string & Msg |
| * @param bool BlockingRequired |
| * |
| * @return const Channel & is the address of the Channel object (the 'this' |
| * pointer). |
| */ |
| const Channel &Channel::readNonBlocking( std::string & msg, bool bBlockingRequired) |
| { |
| msg = ""; |
| if (INVALID_SOCKET == m_Sock) |
| { |
| /*Reading cannot be done without having a open socket*/ |
| throw AxisTransportException(SERVER_TRANSPORT_INVALID_SOCKET); |
| } |
| |
| int nByteRecv = 0; |
| char buf[BUF_SIZE]; |
| |
| //Samisa: I want to set the socket to non blocking mode here |
| //for Winsock there is no simple way to do this but for Linux I used MSG_DONTWAIT |
| //there is no MSG_DONTWAIT defined in Winsock |
| int flags = 0; |
| |
| #if defined WIN32 |
| // Set the socket I/O mode; iMode = 0 for blocking; iMode != 0 for non-blocking |
| int iMode = 1; |
| |
| if( bBlockingRequired) |
| { |
| iMode = 0; |
| } |
| |
| ioctlsocket( m_Sock, FIONBIO, (u_long FAR*) &iMode); |
| |
| flags = 0; |
| #elif defined AIX |
| flags=MSG_WAITALL; |
| if (!bBlockingRequired) |
| { |
| flags=MSG_NONBLOCK; |
| } |
| #elif defined( __OS400__ ) |
| fcntl(m_Sock, F_SETFL, (int)O_NONBLOCK); |
| flags = 0; |
| #else |
| //for linux |
| if( !bBlockingRequired) |
| { |
| flags = MSG_DONTWAIT; |
| } |
| |
| //TODO: define flags (or other means) to enable non blocking for other operating systems |
| #endif |
| |
| if ((nByteRecv = recv (m_Sock, (char *) &buf, BUF_SIZE - 1, flags)) |
| == SOCKET_ERROR) |
| { |
| /*Channel error while getting data*/ |
| return *this; |
| } |
| if (nByteRecv) |
| { |
| buf[nByteRecv] = '\0'; |
| msg = buf; |
| } |
| else |
| Error( "Channel::readNonBlocking: execution break"); |
| |
| return *this; |
| } |
| |
| /** |
| * Channel::closeChannel() Close and clean-up using specific Operating System |
| * calls. |
| */ |
| void Channel::closeChannel () |
| { |
| |
| if( INVALID_SOCKET == m_Sock) // Check if socket already closed : AXISCPP-185 |
| { |
| return; |
| } |
| |
| #ifdef WIN32 |
| closesocket( m_Sock); |
| |
| // Check for any possible error conditions from WSACleanup() and report |
| // them before exiting, as this information might indicate a network |
| // layer problem in the system. |
| |
| WSACleanup (); |
| #else |
| ::close( m_Sock); |
| |
| #endif |
| m_Sock = INVALID_SOCKET; // fix for AXISCPP-185 |
| |
| } |
| |
| /** |
| * Channel::Error( Error) Log any errors that cause on channel usage or |
| * initilaization. |
| */ |
| void Channel::Error( const char *err) |
| { |
| #ifdef _DEBUG |
| std::cerr << err << std::endl; |
| #endif |
| } |
| |
| /** |
| * Channel::setTimeout( Seconds) |
| * |
| * @param const long Seconds |
| */ |
| void Channel::setTimeout(const long lSeconds) |
| { |
| |
| m_lTimeoutSeconds = lSeconds; |
| |
| } |
| |
| /** |
| * Channel::applyTimeout() |
| * |
| * @return int |
| */ |
| int Channel::applyTimeout() |
| { |
| |
| fd_set set; |
| struct timeval timeout; |
| |
| // Initialize the file descriptor set. |
| FD_ZERO( &set); |
| FD_SET( m_Sock, &set); |
| |
| /* Initialize the timeout data structure. */ |
| timeout.tv_sec = m_lTimeoutSeconds; |
| timeout.tv_usec = 0; |
| |
| /* select returns 0 if timeout, 1 if input available, -1 if error. */ |
| return select( FD_SETSIZE, &set, NULL, NULL, &timeout); |
| } |
| |
| // This is used by SimpleAxisServer |
| void Channel::setSocket(unsigned int uiNewSocket) |
| { |
| m_Sock = uiNewSocket; |
| } |
| |
| /** |
| * Debug routine will be deleted. |
| */ |
| void Channel::hexOutput( char * psData, int iDataLength) |
| { |
| unsigned char * pByte = (unsigned char *) psData; |
| string sLineHex; |
| string sLineChar; |
| int iByteCount = 0; |
| int iLineCount; |
| char szDigits[6]; |
| |
| while( iByteCount < iDataLength) |
| { |
| sprintf( szDigits, "%04X ", iByteCount); |
| |
| sLineHex = szDigits; |
| sLineChar = ""; |
| iLineCount = 0; |
| |
| while( iLineCount < 16 && iByteCount < iDataLength) |
| { |
| unsigned char Char = *pByte; |
| |
| if( Char < 32 || Char > 127) |
| { |
| Char = '.'; |
| } |
| |
| sprintf( szDigits, "%02X ", *pByte); |
| |
| sLineHex = sLineHex + szDigits; |
| sLineChar = sLineChar + (char) Char; |
| |
| pByte++; |
| iByteCount++; |
| iLineCount++; |
| } |
| |
| for( int iCount = iLineCount; iCount < 16; iCount++) |
| { |
| sLineHex = sLineHex + " "; |
| } |
| |
| printf( "%s %s\n", sLineHex.c_str(), sLineChar.c_str()); |
| } |
| } |
| |
| void Channel::setProxy (const char *pcProxyHost, unsigned int uiProxyPort) |
| { |
| m_strProxyHost = pcProxyHost; |
| m_uiProxyPort = uiProxyPort; |
| m_bUseProxy = true; |
| } |