diff options
Diffstat (limited to 'mod_gsoap/gsoap_win/isapi/gsoap/ISAPI_Server.cpp')
-rw-r--r-- | mod_gsoap/gsoap_win/isapi/gsoap/ISAPI_Server.cpp | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/mod_gsoap/gsoap_win/isapi/gsoap/ISAPI_Server.cpp b/mod_gsoap/gsoap_win/isapi/gsoap/ISAPI_Server.cpp new file mode 100644 index 0000000..e576f52 --- /dev/null +++ b/mod_gsoap/gsoap_win/isapi/gsoap/ISAPI_Server.cpp @@ -0,0 +1,336 @@ +/** Implementation of the ISAPI_Server class + * @file ISAPI_Server.cpp + * @author Christian Aberger + * Copyright (C) 2001 WebWare (http://www.webware.at) + * Load a gosap server dll dynamically (if not already loaded) and serve the request. + * See http://www.aberger.at/SOAP for documentation. + * + */ + +#include <memory> +#include <strstream> +#ifdef __DEBUG_ISAPI +#include <fstream> +#endif +#include <cassert> +using namespace std; + +#include "ISAPI_Server.h" +#include "isapistream.h" +#include "ISAPI_HttpContext.h" +#include "ISAPI_SoapServerFactory.h" + +static const char *GSOAP_ID = "mod_gsoap isapi extension 0.0.2"; +static const char *crlf = "\r\n"; + +///> helper function to build full path to dll. +static string MakeDllName(EXTENSION_CONTROL_BLOCK *pECB, const char *pszDllName); + +/* --- plugin functions -- */ +static int mod_gsoap_plugin_copy(struct soap *soap, struct soap_plugin *dst, struct soap_plugin *src) { + *dst = *src; + return SOAP_OK; +} +static void mod_gsoap_delete(struct soap *soap, struct soap_plugin *p) { +} +static int mod_gsoap_plugin(struct soap *soap, struct soap_plugin *p, void *arg) { + p->id = GSOAP_ID; + p->data = arg; + p->fcopy = mod_gsoap_plugin_copy; + p->fdelete = mod_gsoap_delete; + return SOAP_OK; +} + +/** bundle the request/response data into an object to be able to attach it to soap. */ +class SoapTransaction { +public: + SoapTransaction(soap *); + virtual ~SoapTransaction(); + + static SoapTransaction *transaction(soap *); + void SendHeaders(); ///< send output headers of gsoap to client, if not done so already. + size_t SupplyRequestHeaders(char *pBuf, const size_t len); // send input request headers to gsoap. + void BuildHeaders(); ///< build the headers that must be sent to gsoap. +public: + soap *_soap; + std::string _header; ///< header part returned to client e.g. "200 OK". + std::string _request_header; ///< the (remaining part of ) the first line of the request. + DWORD _dwReturnCode; ///< the return code for the HttpExtensionProc. + ISAPI_HttpRequest *_request; + HttpResponse *_response; + isapistream *_istream; + const mod_gsoap_interface *_interface; +#ifdef _DEBUG_ISAPI + ofstream *_debug_in; ///< debugging output stream + ofstream *_debug_out; ///< debugging output stream +#endif +protected: + bool _headers_sent; ///< true if Http headers have already been sent. + bool _headers_supplied; ///< true if Http headers have already supplied to gsoap. +}; + +SoapTransaction::SoapTransaction(soap *soap) +: _soap(soap) +, _dwReturnCode(HSE_STATUS_SUCCESS) +, _header("200 OK") +, _request(NULL) +, _response(NULL) +, _interface(NULL) +, _istream(NULL) +, _headers_sent(false) +, _headers_supplied(false) +#ifdef _DEBUG_ISAPI +, _debug_in(NULL) +, _debug_out(NULL) +#endif +{ + assert(soap); +#ifdef _DEBUG_ISAPI + TCHAR szPath[_MAX_PATH]; + ::GetTempPath(sizeof szPath, szPath); + string strIn = szPath; + string strOut = szPath; + strIn += "mod_gsoap_debug_in.log"; + strOut += "mod_gsoap_debug_out.log"; + strOut += "mod_gsoap_debug.log"; + _debug_in = new ofstream(strIn.c_str(), ios::out | ios::app); + _debug_out = new ofstream(strOut.c_str(), ios::out | ios::app); + *_debug_in << "--------------------------------START--------------------" << endl; + *_debug_out << "--------------------------------START--------------------" << endl; +#endif +} +SoapTransaction::~SoapTransaction() { +#ifdef _DEBUG_ISAPI + *_debug_in << "--------------------------------STOP---------------------" << endl; + *_debug_out << "--------------------------------STOP---------------------" << endl; + delete _debug_in; + delete _debug_out; +#endif +} +inline SoapTransaction *SoapTransaction::transaction(soap *soap) { + assert(NULL != soap); + return reinterpret_cast<SoapTransaction *>(soap->fplugin(soap, GSOAP_ID)); +} +void SoapTransaction::SendHeaders() { + if (_headers_sent) { + return; + } + ostrstream headers; + for (HttpMessage::ContentHeaders::const_iterator it = _response->getContentHeaders().begin(); it != _response->getContentHeaders().end(); ++it) { + if (0 != strcasecmp(it->first.c_str(), "Connection")) { + headers << it->first << ": " << it->second << crlf; + } + } + headers << crlf; + string strOut(headers.str(), headers.pcount()); +#ifdef _DEBUG_ISAPI + *_debug_out << strOut; +#endif + BOOL bHeaders = _request->ECB()->ServerSupportFunction(_request->ECB()->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, + (LPVOID)"200 OK", NULL, (LPDWORD)strOut.c_str()); + headers.freeze(false); + _headers_sent = true; +} +/** Emits HTTP key: val header entries. + @return SOAP_OK, or a gSOAP error code. + Built-in gSOAP function: http_post_header. + + IIS handles most of the headers (Connection etc.) itself. + The main thing we need to extract is SOAPAction. +*/ +static int http_post_header(soap *soap, const char *key, const char *value) { + if (NULL != key) { + SoapTransaction *pTrans = SoapTransaction::transaction(soap); + if (value) { + pTrans->_response->getContentHeaders()[key] = value; + } + } + return SOAP_OK; +} +/** set the http - response code from the soap error message. */ +static int http_response(soap *soap, int soap_error, size_t count) { + SoapTransaction *pTrans = SoapTransaction::transaction(soap); + if (SOAP_OK == soap_error) { + pTrans->_dwReturnCode = HSE_STATUS_SUCCESS; + } else { + pTrans->_dwReturnCode = HSE_STATUS_ERROR; + pTrans->_header = "500 ERR"; + } + return SOAP_OK; +} +/** gsoap function to append the data to send to our output buffer */ +static int fsend(soap *soap, const char *pBuf, size_t len) { + SoapTransaction *pTrans = SoapTransaction::transaction(soap); + pTrans->SendHeaders(); +#ifdef _DEBUG_ISAPI + pTrans->_debug_out->write(pBuf, len); +#endif + pTrans->_istream->write(pBuf, len); + return SOAP_OK; +} +size_t SoapTransaction::SupplyRequestHeaders(char *pBuf, const size_t len) { + int nLen = 0; + if (!_headers_supplied) { + if (!_request_header.empty()) { // we must send 1st line so that gsoap parses everything correctly. + nLen = _request_header.length(); + if (nLen > len) { + nLen = len; + memcpy(pBuf, _request_header.c_str(), len); + _request_header = _request_header.substr(len); + } else { + memcpy(pBuf, _request_header.c_str(), _request_header.length()); + _request_header.erase(); + _headers_supplied = true; + } +#ifdef _DEBUG_ISAPI + _debug_in->write(pBuf, nLen); +#endif + } + } + return nLen; +} +/** gsoap function that requests the next piece of data from us */ +static size_t frecv(soap *soap, char *pBuf, size_t len) { + SoapTransaction *pTrans = SoapTransaction::transaction(soap); + size_t nLen = pTrans->SupplyRequestHeaders(pBuf, len); + if (0 == nLen) { // query string and headers have been sent already. + assert(pTrans->_istream->good()); + if (!pTrans->_istream->eof()) { + pTrans->_istream->readsome(pBuf, len); + nLen = pTrans->_istream->gcount(); +#ifdef _DEBUG_ISAPI + pTrans->_debug_in->write(pBuf, nLen); +#endif + assert(pTrans->_istream->good()); + } + } + return nLen; +} + +void SoapTransaction::BuildHeaders() { + ostrstream s; + /* we must rebuild the 1st request line, cause IIS has no API to get it :( */ + s << "POST /" + _request->_querystring + " HTTP/1.0\r\n"; + for (ISAPI_HttpRequest::ContentHeaders::const_iterator it = _request->getContentHeaders().begin(); it != _request->getContentHeaders().end(); ++it) { + s << it->first << ": " << it->second << crlf; + } + s << crlf; + _request_header.assign(s.str(), s.pcount()); +} +BOOL ISAPI_Server::GetExtensionVersion(HSE_VERSION_INFO *pVer) { + lstrcpyn((LPSTR) pVer->lpszExtensionDesc, "WebWare SOAP ISAPI extension", HSE_MAX_EXT_DLL_NAME_LEN); + return TRUE; +} +BOOL ISAPI_Server::TerminateExtension(DWORD) { + ISAPI_SoapServerFactory::instance()->shutdown(); + return TRUE; +} +/* forwards the real work to soap_serve */ +static DWORD serve( + const mod_gsoap_interface *pInterface, + ISAPI_HttpRequest& req, + HttpResponse res, + isapistream& is) +{ + assert(NULL != pInterface && pInterface->linked()); + + soap soap; + (*pInterface->fsoap_init)(&soap); + SoapTransaction trans(&soap); + if (NULL != pInterface->fsoap_register_plugin_arg) { + (*pInterface->fsoap_register_plugin_arg)(&soap, mod_gsoap_plugin, (void *)&trans); + } + //soap.user = &trans; + trans._interface = pInterface; + trans._istream = &is; + trans._request = &req; + trans._response = &res; + + trans.BuildHeaders(); + +#ifdef WITH_ZLIB + // always allow gzip in -- but only allow it out if the client can handle it + soap_set_imode(&soap, SOAP_ENC_ZLIB); + + string str = req.getContentHeaders()["Accept-Encoding"]; + if (strstr(str.c_str(), "gzip")) + { + soap_set_omode(&soap, SOAP_ENC_ZLIB); + http_post_header( &soap, "Content-Encoding", "gzip" ); + } + +#endif + + // set callback functions: + soap.frecv = frecv; + soap.fsend = fsend; + soap.fresponse = http_response; + soap.fposthdr = http_post_header; + + (*pInterface->fsoap_serve)(&soap); + if (NULL != pInterface->fsoap_delete) { + (*pInterface->fsoap_delete)(&soap, NULL); + } + (*pInterface->fsoap_done)(&soap); + (*pInterface->fsoap_end)(&soap); + + return trans._dwReturnCode; +} + +static void SendErrorMessage(isapistream& is, const char *pszError) { + is << "<html><title>gsoap error message</title><body>" << pszError << "<p>"; + is << "see <a href=\"http://www.aberger.at/SOAP\">WebWare gsoap ISAPI module</a> documentation."; + is << "</body><html>"; +} + +/** Parse the request, load dll if not already loaded, let it create the response and return the result. + */ +DWORD ISAPI_Server::HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB) { + DWORD dwRes = HSE_STATUS_SUCCESS; + ISAPI_HttpRequest req(pECB); + HttpResponse res; + + isapistream is(pECB); + + string strQuery = req.getQueryString(); + string strDll = strQuery; + string::size_type pos = strQuery.find('&'); + if (string::npos != pos) { + strDll = strQuery.substr(0, pos); + strQuery = strQuery.substr(pos + 1); + } + if (!strDll.empty()) { + strDll += ".dll"; + strDll = MakeDllName(pECB, strDll.c_str()); // due to security reasons we only allow loading of dlls from the local directory (which must not be writable!). + const mod_gsoap_interface *pInterface = ISAPI_SoapServerFactory::instance()->getInterface(strDll.c_str()); + if (NULL != pInterface) { + if (HttpRequest::POST == req.getMethod()) { + dwRes = serve(pInterface, req, res, is); + } else { + SendErrorMessage(is, "You must use a POST request to get answer from gsoap !"); + } + } else { + is << "could not create server '" << strDll << "'. Check your request if it is correct. " + << ISAPI_SoapServerFactory::instance()->getLastError(); + } + } else { + SendErrorMessage(is, "No gsoap server dll specified in request. Please add the name of your soap server dll, e.g. http://localhost/gsoap/mod_gsoap?calc"); + } + is.flush(); + return dwRes; +} + +/** Given a dllname make a fully qualified path to the dll in the current isapi directory + @param pECB the Extension Control Block of the Request + @param pszDllName the unqualified dllname +*/ +static string MakeDllName(EXTENSION_CONTROL_BLOCK *pECB, const char *pszDllName) { + string strPath; + char szPath[_MAX_PATH + 1]; + ZeroMemory(szPath, sizeof szPath); + DWORD dwBufferSize = sizeof szPath; + pECB->GetServerVariable(pECB->ConnID, "APPL_PHYSICAL_PATH", szPath, &dwBufferSize); + strPath = szPath; + strPath += pszDllName; + return strPath; +} |