You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by pr...@apache.org on 2005/01/07 16:23:00 UTC

cvs commit: ws-axis/c/src/transport/axis3/HTTPChannel HTTPChannel.cpp HTTPChannel.hpp

prestonf    2005/01/07 07:23:00

  Modified:    c/src/transport/axis3/HTTPChannel HTTPChannel.cpp
                        HTTPChannel.hpp
  Log:
  Hi All,
  These are modifications needed for the draft of the new http transport implementation AXIS3 (see AXISCPP-361).
  Regards,
  Fred Preston.
  
  Revision  Changes    Path
  1.2       +359 -3    ws-axis/c/src/transport/axis3/HTTPChannel/HTTPChannel.cpp
  
  Index: HTTPChannel.cpp
  ===================================================================
  RCS file: /home/cvs/ws-axis/c/src/transport/axis3/HTTPChannel/HTTPChannel.cpp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- HTTPChannel.cpp	6 Jan 2005 17:33:15 -0000	1.1
  +++ HTTPChannel.cpp	7 Jan 2005 15:23:00 -0000	1.2
  @@ -1,15 +1,39 @@
   #include "HTTPChannel.hpp"
   
  -
   HTTPChannel::HTTPChannel()
   {
   	memset( &m_URL, 0, sizeof( URL));
   
   	m_LastError = "No Errors";
  +
  +	m_Sock = INVALID_SOCKET;
  +
  +	m_bUseProxy = false;
  +    m_strProxyHost = "";
  +    m_uiProxyPort = 0;
  +
  +#ifdef WIN32
  +	m_lTimeoutSeconds = 10;
  +#else
  +	m_lTimeoutSeconds = 0;
  +#endif
  +
  +	if( !StartSockets())
  +	{
  +		throw HTTPTransportException( SERVER_TRANSPORT_CHANNEL_INIT_ERROR);
  +	}
   }
   
   HTTPChannel::~HTTPChannel()
   {
  +// If the socket value is not invalid, then close the socket before
  +// deleting the Channel object.
  +	if( m_Sock != INVALID_SOCKET)
  +	{
  +		CloseChannel();
  +	}
  +
  +	StopSockets();
   }
   
   const char * HTTPChannel::getURL()
  @@ -31,7 +55,18 @@
   
   bool HTTPChannel::open() throw (HTTPTransportException&)
   {
  -	bool	bSuccess = false;
  +	bool	bSuccess = (bool) AXIS_FAIL;
  +
  +    if( m_Sock != INVALID_SOCKET)
  +	{
  +		CloseChannel();
  +	}
  +
  +	if( (bSuccess = OpenChannel()) != AXIS_SUCCESS)
  +	{
  +		throw HTTPTransportException( SERVER_TRANSPORT_SOCKET_CONNECT_ERROR,
  +									  (char *) m_LastError.c_str());
  +	}
   
   	return bSuccess;
   }
  @@ -41,22 +76,119 @@
   	return m_LastError;
   }
   
  -const IChannel & HTTPChannel::operator >> (std::string& msg)
  +const IChannel & HTTPChannel::operator >> (const char * 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
  +
  +		m_LastError = "No open socket to read from.";
  +
  +		throw HTTPTransportException( SERVER_TRANSPORT_INVALID_SOCKET,
  +									  (char *) m_LastError.c_str());
  +    }
  +
  +    int		nByteRecv = 0;
  +    char	buf[BUF_SIZE];
  +	int		iBufSize = BUF_SIZE - 1;
  +
  +    //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
  +		m_LastError = "Channel error while waiting for timeout";
  +
  +        // Select SOCKET_ERROR. Channel error while waiting for timeout
  +        throw HTTPTransportException( SERVER_TRANSPORT_TIMEOUT_EXCEPTION, 
  +									  (char *) m_LastError.c_str());
  +    }
  +
  +    if( iTimeoutStatus == 0)
  +    {
  +		// Timeout expired - select timeout expired.
  +        // Channel error connection timeout before receving
  +		m_LastError = "Channel error: connection timed out before receving";
  +
  +        throw HTTPTransportException( SERVER_TRANSPORT_TIMEOUT_EXPIRED, 
  +									  (char *) m_LastError.c_str());
  +    }
  +
  +    // 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
  +		m_LastError = "Channel error while getting data";
  +
  +		/*CloseChannel();*/
  +
  +		throw HTTPTransportException( SERVER_TRANSPORT_INPUT_STREAMING_ERROR, 
  +									  (char *) m_LastError.c_str());
  +    }
  +
  +    if( nByteRecv)
  +    {
  +		/* printf("nByteRecv:%d\n", nByteRecv); */
  +		buf[nByteRecv] = '\0';
  +		/* got a part of the message, so add to form */
  +		memcpy( (void *) msg, buf, nByteRecv);
  +    }
  +
   	return *this;
   }
   
   const IChannel & HTTPChannel::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.
  +
  +		m_LastError = "No valid socket open";
  +
  +		throw HTTPTransportException( SERVER_TRANSPORT_INVALID_SOCKET,
  +									  (char *) m_LastError.c_str());
  +    }
  +
  +    int size = strlen( msg);
  +	int nByteSent;
  +
  +    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();
  +
  +		m_LastError = "Output streaming error while writing data";
  +
  +		throw HTTPTransportException( SERVER_TRANSPORT_OUTPUT_STREAMING_ERROR,
  +									  (char *) m_LastError.c_str());
  +    }
  +
   	return *this;
   }
   
   void HTTPChannel::setTimeout( const long lSeconds)
   {
  +    m_lTimeoutSeconds = lSeconds;
   }
   
   void HTTPChannel::setSocket( unsigned int uiNewSocket)
   {
  +    m_Sock = uiNewSocket;
   }
   
   bool HTTPChannel::setTransportProperty( AXIS_TRANSPORT_INFORMATION_TYPE type, const char* value)
  @@ -71,3 +203,227 @@
   	return NULL;
   }
   
  +void HTTPChannel::setProxy (const char *pcProxyHost, unsigned int uiProxyPort)
  +{
  +    m_strProxyHost = pcProxyHost;
  +    m_uiProxyPort = uiProxyPort;
  +    m_bUseProxy = true;
  +}
  +
  +// Protected methods
  +// -----------------
  +bool HTTPChannel::OpenChannel()
  +{
  +	bool	bSuccess = (bool) AXIS_FAIL;
  +
  +// Create the Client (Rx) side first.
  +    if( (m_Sock = socket( PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
  +	{
  +		m_LastError = "Could not Create a socket.";
  +
  +		return bSuccess;
  +	}
  +
  +// If the transport was initilised, then create client and server sockets.
  +    sockaddr_in	clAddr;
  +
  +	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();
  +
  +		m_LastError = "Error whilst binding. Cannot open a channel to the remote end,";
  +
  +		return bSuccess;
  +	}
  +
  +// 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.
  +
  +#ifdef WIN32
  +// Before we do anything else get the last error message;
  +// I'd like to put the getting of the error message into platform specifics
  +// but not sure how!  I think it would be nicer to make the platform
  +// specifics a class and not just macros.  That way we could have e.g.
  +// char * Windows#getLastErrorMessage().
  +		long lLastError = GetLastError();
  +#endif
  +		CloseChannel();
  +
  +#ifdef WIN32
  +		char	szErrorBuffer[200]; 
  +	    LPVOID	lpErrorBuffer;
  +
  +		FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
  +					   NULL,
  +					   lLastError,
  +					   MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT),
  +					   (LPTSTR) &lpErrorBuffer,
  +					   0,
  +					   NULL);
  +
  +		sprintf( szErrorBuffer, 
  +				 "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(), lpErrorBuffer, lLastError); 
  + 
  +	    LocalFree( lpErrorBuffer);
  +
  +		m_LastError = szErrorBuffer;
  +#else
  +		m_LastError = "Cannot open a channel to the remote end.";
  +
  +#endif
  +	    return bSuccess;
  +    }
  +	else
  +	{
  +		bSuccess = AXIS_SUCCESS;
  +	}
  +
  +    /* 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 bSuccess;
  +}
  +
  +void HTTPChannel::CloseChannel()
  +{
  +    if( INVALID_SOCKET != m_Sock) // Check if socket already closed : AXISCPP-185
  +	{
  +#ifdef WIN32
  +		closesocket( m_Sock);
  +#else
  +		::close( m_Sock);
  +#endif
  +		m_Sock = INVALID_SOCKET; // fix for AXISCPP-185
  +	}
  +}
  +
  +bool HTTPChannel::StartSockets()
  +{
  +	bool	bSuccess = false;
  +#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_LastError = "WinSock DLL not responding.";
  +    }
  +    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 	szErrorBuffer[100];
  +
  +			sprintf( szErrorBuffer, "Windows Sockets version %d.%d is not supported by winsock2.dll", LOBYTE( wsaData.wVersion), HIBYTE( wsaData.wVersion));
  +
  +			m_LastError = szErrorBuffer;
  +
  +			StopSockets();
  +		}
  +		else
  +		{
  +			bSuccess = true;
  +		}
  +    }
  +#else
  +    /* cout << "no need for linux" << endl; */
  +    /* other OS specific Intitialization goes here */
  +	bSuccess = true;
  +#endif
  +
  +    return bSuccess;
  +}
  +
  +void HTTPChannel::StopSockets()
  +{
  +	WSACleanup();
  +}
  +
  +/**
  + * Channel::applyTimeout()
  + *
  + * @return int 
  + */
  +int HTTPChannel::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);
  +}
  
  
  
  1.2       +54 -6     ws-axis/c/src/transport/axis3/HTTPChannel/HTTPChannel.hpp
  
  Index: HTTPChannel.hpp
  ===================================================================
  RCS file: /home/cvs/ws-axis/c/src/transport/axis3/HTTPChannel/HTTPChannel.hpp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- HTTPChannel.hpp	6 Jan 2005 17:33:15 -0000	1.1
  +++ HTTPChannel.hpp	7 Jan 2005 15:23:00 -0000	1.2
  @@ -1,8 +1,43 @@
   #if !defined(_AXIS_AXIS_CHANNEL_HPP)
   #define _AXIS_AXIS_CHANNEL_HPP
   
  +#include <stdio.h>
  +#include <string>
  +
  +// Platform specific stuff
  +#ifdef WIN32
   #include "..\IChannel.hpp"
   #include "..\URL.hpp"
  +#include "..\HTTPTransportException.hpp"
  +#include <winsock2.h>
  +
  +// What version of WinSock is required
  +const int    WS_VERSION_REQD    = 0x0101;
  +
  +// Macros to get version major & minor
  +inline WS_VERSION_MAJOR() {return HIBYTE(WS_VERSION_REQD);}
  +inline WS_VERSION_MINOR() {return LOBYTE(WS_VERSION_REQD);}
  +
  +#else
  +#include "../IChannel.hpp"
  +#include "../URL.hpp"
  +#include "../HTTPTransportException.hpp"
  +
  +#include <unistd.h>
  +#include <sys/types.h>		// basic system data types
  +#include <sys/socket.h>		// basic socket definitions
  +#include <netinet/tcp.h>
  +#include <fcntl.h>			// for nonblocking if need
  +#include <sys/time.h>
  +#include <netdb.h>
  +#include <netinet/in.h>
  +#include <arpa/inet.h>		// inet(3) functions
  +
  +const unsigned int INVALID_SOCKET =  0;
  +const int          SOCKET_ERROR   = -1;
  +
  +// Other OS specific stuff goes here
  +#endif
   
   AXIS_CPP_NAMESPACE_USE using namespace std;
   
  @@ -17,17 +52,30 @@
       virtual URL &		getURLObject();
       bool				open() throw (HTTPTransportException&);
       const std::string &	GetLastErrorMsg();
  -    const IChannel &	operator >> (std::string& msg);
  -    const IChannel &	operator << (const char* msg);
  +//    const IChannel &	operator >> (std::string& msg);
  +    const IChannel &	operator >> (const char * msg);
  +    const IChannel &	operator << (const char * msg);
       void				setTimeout( const long lSeconds);
       void				setSocket( unsigned int uiNewSocket);
  -	bool				setTransportProperty( AXIS_TRANSPORT_INFORMATION_TYPE type, const char* value);
  +	bool				setTransportProperty( AXIS_TRANSPORT_INFORMATION_TYPE type, const char * value);
   	const char *		getTransportProperty( AXIS_TRANSPORT_INFORMATION_TYPE type);
  +    void				setProxy( const char * pcProxyHost, unsigned int uiProxyPort);
   
  -private:
  -    URL		m_URL;
  -	string	m_LastError;
  +protected:
  +	bool				OpenChannel();
  +	void				CloseChannel();
  +	bool				StartSockets();
  +	void				StopSockets();
  +	int					applyTimeout();
   
  +private:
  +    URL				m_URL;				// URL
  +	string			m_LastError;		// Last error
  +    unsigned int	m_Sock;				// Socket descriptor
  +    bool			m_bUseProxy;		// Use a Proxy?
  +    std::string		m_strProxyHost;		// Proxy server name.
  +    unsigned int	m_uiProxyPort;		// Proxy server port.
  +    long			m_lTimeoutSeconds;	// Timeout in seconds
   };
   
   #endif