blob: 9a0f4279eeb6c03c429dc4a4f309d58719970cb7 [file]
/* -*- C++ -*- */
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Axis" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
*
* @author Lilantha Darshana (lilantha@virtusa.com)
*
*/
#include <axis/client/transport/axis/Platform.hpp>
#include <axis/client/transport/axis/HttpTransport.hpp>
#include <iostream>
#include <axis/common/AxisException.h>
using namespace std;
/**
* Shutdown any established channel
*/
HttpTransport::~HttpTransport()
{
m_Channel.Close();
}
/**
* Initialize HTTP transport by establishing a channel to the remote end.
*
* @return Status of the channel establishment to the remote end provided by m_Url
* @trows Channel Exception
*/
bool HttpTransport::Init()
{
// open a channel for transport
m_HttpBindDone = false;
try
{
m_bStatus = true;
std::string host = m_Url.GetHostName();
m_Channel.Open(host, m_Url.GetPort());
m_Channel.SetTransportHandler(this);
#ifdef _DEBUG
cout << "Transport:init() successfull" << endl;
#endif
}
catch(ChannelException& chEx)
{
std::cerr << chEx.GetErr() << std::endl;
throw;
}
return true;
}
void HttpTransport::Fini()
{
m_Channel.Close();
}
/**
* Set properties of HTTP transport such as additional Header fields like SOAPAction.
*
* @param p_Property Property name; such as SOAPAction
* @param p_Value Property value
*/
void HttpTransport::SetProperty(const char* p_Property, const char* p_Value)
{
m_AdditionalHeader.push_back(std::make_pair(p_Property, p_Value));
}
/**
* Read from a HTTP transport handler and store read payload.
*
* @param p_Payload SOAP Envelop as Payload for HTTP transport
*
*/
const Transport& HttpTransport::operator >> (const char** pPayLoad)
{
if(!m_bStatus)
{
// We have the payload; this is due to Fault request made in earlier call
// to this method
*pPayLoad = m_PayLoad.c_str();
return *this;
}
std::string tmpPacket; // use temporary, need to workout for this
m_Channel >> tmpPacket;
#ifdef _DEBUG
std::cout << "\n\n\nGot the message:\r\n\r\n" << tmpPacket << "\n\n";
#endif
// Validate the HTTP packet
if(!m_bStatus) HTTPValidate(tmpPacket);
// At this point we have the payload at hand so give it out
*pPayLoad = m_PayLoad.c_str();
return *this;
}
/**
* Write a given payload to the established channel by using HTTP transport as carrier.
*
*/
const Transport& HttpTransport::operator << (const char* p_Payload)
{
HTTPBind(); // Bind the SOAP-Envelop with HTTP headers
// Write to the established channel
m_Channel << p_Payload;
return *this;
}
/**
* Build a HTTP packet with a given payload & additional HTTP properties
* Uses HTTP 1.1; if HTTP 1.0 is required we have to manage with setting
* the properties. Only POST is supported for the time being; hope to
* support M-POST
*
*/
void HttpTransport::HTTPBind()
{
if (m_HttpBindDone) return;
m_OutHttpHeaders = "";
if(m_Typ == POST) // only POST is supported for now, wish-list: M-POST??
m_OutHttpHeaders = "POST ";
// Use HTTP 1.1; if HTTP 1.0 is required we have to manage with setting the properties
m_OutHttpHeaders += m_Url.GetResource() + " HTTP/1.1\r\n"; // no support for proxy server yet
m_OutHttpHeaders += "Host: " + m_Url.GetHostName();
unsigned short port = m_Url.GetPort();
if(port != HTTP_PORT)
{
char buff[8];
sprintf(buff, "%u", port);
m_OutHttpHeaders += ":";
m_OutHttpHeaders += buff;
}
m_OutHttpHeaders += "\r\n";
m_OutHttpHeaders += "Content-Type: text/xml; charset=\"UTF-8\"\r\n"; // We have to support other charsets
//Set header values for additional prefixes, such as SOAPAction
for(int i=0; i < m_AdditionalHeader.size(); i++)
{
if (m_AdditionalHeader[i].first == "Content-Length")
{
m_OutHttpHeaders += m_AdditionalHeader[i].first;
m_OutHttpHeaders += ": ";
m_OutHttpHeaders += m_AdditionalHeader[i].second;
m_OutHttpHeaders += "\r\n";
}
else
{
m_OutHttpHeaders += m_AdditionalHeader[i].first;
m_OutHttpHeaders += ": \"";
m_OutHttpHeaders += m_AdditionalHeader[i].second;
m_OutHttpHeaders += "\"\r\n";
}
}
m_OutHttpHeaders += "\r\n";
m_Channel << m_OutHttpHeaders.c_str();
m_HttpBindDone = true;
}
/**
* Obtain the status of the HTTP packet validity.
*
* @param p_HttpPacket HTTP packet
*
* @return Status of the HTTP packet validity.
*/
bool HttpTransport::GetStatus(const std::string& p_HttpPacket)
{
HTTPValidate(p_HttpPacket);
return m_bStatus;
}
/**
* Validate HTTP packets received from the channel.
*
* @param p_HttpPacket HTTP packet
*
* @return Status code is set
*/
void HttpTransport::HTTPValidate(const std::string& p_HttpPacket)
{
// for the time being just get the payload. Here we need much work
m_bStatus = true;
std::string::size_type pos = p_HttpPacket.find('\n'), nxtpos;
int nHttpSatus;
if(pos == std::string::npos) return; //unexpected string
std::string strLine = p_HttpPacket.substr(0, pos + 1);
std::string::size_type offset = pos + 1;
// Check for HTTP header validity; HTTP 1.0 / HTTP 1.0 is supported.
if((pos = strLine.find("HTTP/1.0")) != std::string::npos
|| (pos = strLine.find("HTTP/1.1")) != std::string::npos)
{
if(((pos = strLine.find_first_of("\" ", pos + strlen("HTTP/1.x")))
!= std::string::npos) &&
(nxtpos = strLine.find_first_of("\" ", pos)) != std::string::npos)
{
pos++;
// Get the HTTP status code of the packet obtained
nHttpSatus = atoi(strLine.substr(pos, nxtpos - pos).c_str())/100;
}
else
return;
// Status code is 2xx; so valid packet. hence go ahead and extract the payload.
if(nHttpSatus == 2)
{
GetPayLoad(p_HttpPacket, offset);
}
else if(nHttpSatus == 3) // Status code is 3xx; some error has occurred
{
// error recovery mechanism should go here
Error(p_HttpPacket.c_str());
throw ChannelException("HTTP Error, cannot process response message...");
}
else if(nHttpSatus == 4) // Status code is 4xx; some error has occurred
{
// error recovery mechanism should go here
Error(p_HttpPacket.c_str());
throw ChannelException("HTTP Error, cannot process response message...");
}
else if(nHttpSatus == 5) // Status code is 5xx; some error has occurred
{
// error recovery mechanism should go here
GetPayLoad(p_HttpPacket, offset);
if (!m_bStatus)
{
Error(p_HttpPacket.c_str());
throw AxisException(HTTP_ERROR);
}
}
}
else
throw ChannelException("Unknow HTTP response, cannot process response message...");
}
/**
* Extract payload from the HTTP packet starting from a given offset
*
*/
void HttpTransport::GetPayLoad(const std::string& p_HttpPacket, std::string::size_type& offset)
{
std::string::size_type pos, nxtpos;
std::string strLine;
int len=0;
// process rest of the HTTP packet
while (true)
{
if((nxtpos = p_HttpPacket.find('\n', offset)) == std::string::npos) return;
nxtpos++;
strLine = p_HttpPacket.substr(offset, nxtpos - offset);
offset = nxtpos;
if((strLine == "\r\n") || (strLine == "\n") || strLine.size() <= 1)
break;
// Get the payload size from the header.
if((pos = strLine.find("Content-Length:")) != std::string::npos)
len = atoi(strLine.substr(pos + strlen("Content-Length: ")).c_str());
}
m_PayLoad = p_HttpPacket.substr(offset);
pos = m_PayLoad.rfind('0');
if(std::string::npos != pos && m_PayLoad[pos+1] != '\"')
{
//nxtpos = m_PayLoad.find("1df");
//if(std::string::npos != nxtpos && '\n' == m_PayLoad[nxtpos+4])
{
m_bStatus = false; // we have the payload
// Extract the SOAP message
m_PayLoad = m_PayLoad.substr(m_PayLoad.find('<'));
m_PayLoad = m_PayLoad.substr(0, m_PayLoad.rfind('>') + 1);
}
}
#ifdef _DEBUG
std::cout << "Payload:\n" << m_PayLoad << std::endl;
#endif
}
/**
* Report error on read/write
*
*/
void HttpTransport::Error(const char * err)
{
#ifdef _DEBUG
std::cerr << err << std::endl;
#endif
}
void HttpTransport::ClearAdditionalHeaders()
{
m_AdditionalHeader.clear();
}