/*############################################################################## Copyright (C) 2011 HPCC Systems. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ############################################################################## */ #pragma warning(disable : 4786) #ifdef WIN32 #ifdef ESPHTTP_EXPORTS #define esp_http_decl __declspec(dllexport) #endif #endif //Jlib #include "jliball.hpp" //ESP Binidings #include "http/platform/httptransport.ipp" #include "bindutil.hpp" IEspHttpException* createEspHttpException(int code, const char *_msg, const char* _httpstatus) { return new CEspHttpException(code, _msg, _httpstatus); } bool httpContentFromFile(const char *filepath, StringBuffer &mimetype, MemoryBuffer &fileContents) { StringBuffer strfile(filepath); if (!checkFileExists(strfile.str())) if (!checkFileExists(strfile.toUpperCase().str())) if (!checkFileExists(strfile.toLowerCase().str())) return false; Owned file = createIFile(strfile.str()); if (file && file->isFile()) { Owned io = file->open(IFOread); if (io) { size32_t filesize = io->size(); io->read(0, filesize, fileContents.reserveTruncate(filesize)); mimetype.clear(); const char *ext = strrchr(filepath, '.'); if (ext) { ext++; if (!stricmp(ext, "html") || !stricmp(ext, "htm")) mimetype.append("text/html"); else if (!stricmp(ext, "js")) mimetype.append("text/javascript"); else if (!stricmp(ext, "jpeg") || !stricmp(ext, "jpg")) mimetype.append("image/gif"); else if (!stricmp(ext, "gif")) mimetype.append("image/gif"); else if (!stricmp(ext, "png")) mimetype.append("image/png"); else if (!stricmp(ext, "xml") || !stricmp(ext, "xsl")) mimetype.append("text/xml"); else if (!stricmp(ext, "txt") || !stricmp(ext, "text")) mimetype.append("text/plain"); else if (!stricmp(ext, "zip")) mimetype.append("application/zip"); else if (!stricmp(ext, "pdf")) mimetype.append("application/pdf"); else if (!stricmp(ext, "pdf")) mimetype.append("application/pdf"); else if (!stricmp(ext, "xpi")) mimetype.append("application/x-xpinstall"); else if (!stricmp(ext, "exe") || !stricmp(ext, "class")) mimetype.append("application/octet-stream"); else if (!stricmp(ext, "xsl") || !stricmp(ext, "xslt")) mimetype.append("text/xml"); else if (!stricmp(ext, "css")) mimetype.append("text/css"); else if (!stricmp(ext, "svg")) mimetype.append("image/svg+xml"); } if (!mimetype.length()) mimetype.append("application/octet-stream"); return true; } } return false; } /*************************************************************************** CHttpMessage Implementation This class implements common functions shared by both CHttpRequest and CHttpResponse ****************************************************************************/ CHttpMessage::CHttpMessage(ISocket& socket) : m_socket(socket) { m_bufferedsocket.setown(createBufferedSocket(&socket)); m_content_length = -1; m_content_length64 = -1; m_port = 80; m_paramCount = 0; m_attachCount = 0; m_supportClientXslt=-1; m_isForm = false; }; CHttpMessage::~CHttpMessage() { try { m_bufferedsocket.clear(); m_context.clear(); m_queryparams.clear(); m_content_stream.clear(); } catch(...) { ERRLOG("In CHttpMessage::~CHttpMessage() -- Unknown exception."); } }; int CHttpMessage::parseOneHeader(char* oneline) { if (getEspLogLevel()>LogNormal) DBGLOG("%s", oneline); if(!oneline) return -1; char* name = oneline; while(*name == ' ') name++; char* end = name; while(*end != '\0' && *end != ':') end++; char* value; if (*end == ':') { *end = '\0'; value = end + 1; } else value = end; while(*value == ' ') value++; if(!stricmp(name, "Content-Type")) { m_content_type.set(value); } else if(!stricmp(name, "Content-Length")) { if(value != NULL) { m_content_length = atoi(value); m_content_length64 = atoi64_l(value,strlen(value)); } } else if(!stricmp(name, "Host")) { if(value != NULL) { char* colon = strchr(value, ':'); if(colon != NULL) { *colon = '\0'; char* port = colon + 1; if(port != NULL) { m_port = atoi(port); } } m_host.set(value); } } else if(!stricmp(name, "Set-Cookie") || !stricmp(name, "Cookie")) { parseCookieHeader(value); } else { addHeader(name, value); //Insert into headers hashtable } return 0; } bool CHttpMessage::supportClientXslt() { if (m_supportClientXslt==-1) { bool ie6=false; bool moz5=false; bool opera=false; bool ff=false; StringBuffer uastr; getHeader("User-Agent", uastr); char *uagent=strdup(uastr.str()); char *saveptr; char *token = strtok_r(uagent, "();", &saveptr); while(token) { while (*token==' ') token++; if (!strnicmp(token, "msie", 4)) ie6=(token[5]>='6'); else if (!strnicmp(token, "mozilla", 7)) moz5=(token[8]>='5'); else if (!strnicmp(token, "opera", 5)) opera=true; else if (!strnicmp(token, "firefox", 7)) ff=true; token = strtok_r( NULL, "();" , &saveptr); } free(uagent); m_supportClientXslt=(ff || moz5 || (ie6 && !opera)) ? 1 : 0; } return m_supportClientXslt==1; } /* void CHttpMessage::addRawXMLParameter(const char* path, IPropertyTree* tree) { const char* name = tree->queryName(); if (stricmp(name, "RawArray")==0) { Owned row = tree->getElements("Item"); int items = 0; for (row->first(); row->isValid(); row->next()) { IPropertyTree* pRow = &row->query(); StringBuffer newpath(path); newpath.appendf(".%d",items); const char* value = pRow->queryProp(NULL); if (value) { if (*value) m_queryparams->setProp(newpath, value); } else { Owned field = pRow->getElements("*"); int baseLen = newpath.length(); for (field->first(); field->isValid(); field->next()) { newpath.appendf(".%s", field->query().queryName()); addRawXMLParameter(newpath, &field->query()); newpath.setLength(baseLen); } } items++; } if (items>0) { StringBuffer newpath(path),v; newpath.append(".itemcount"); v.append(items); m_queryparams->setProp(newpath, v); m_paramCount++; } } else { const char* value = tree->queryProp(NULL); if (value) { if (*value) m_queryparams->setProp(path, value); } else // subtree { Owned field = tree->getElements("*"); for (field->first(); field->isValid(); field->next()) { const char* fieldName = field->query().queryName(); StringBuffer newpath(path); if (stricmp(fieldName, "RawArray")!=0) newpath.appendf(".%s",field->query().queryName()); addRawXMLParameter(newpath, &field->query()); } } } } void CHttpMessage::addRawXMLParameter(const char* path, const char *value) { Owned tree; try { tree.setown(createPTreeFromXMLString(value)); } catch(IException* e) { StringBuffer msg; e->errorMessage(msg); ERRLOG("Error parsing struct array: %s", msg.str()); return; } addRawXMLParameter(path, tree); } */ void CHttpMessage::addParameter(const char* paramname, const char *value) { if (!m_queryparams) m_queryparams.setown(createProperties(false)); if (strcmp(paramname,"form")==0) m_isForm = true; if (!m_isForm) { // remove the leading '.' if (*paramname=='.') paramname++; } m_queryparams->setProp(paramname, value); m_paramCount++; } StringBuffer& CHttpMessage::getParameter(const char* paramname, StringBuffer& paramval) { if (m_queryparams) { const char *value = m_queryparams->queryProp(paramname); if (value) paramval.append(value); } return paramval; } void CHttpMessage::addAttachment(const char* name, StringBuffer& value) { if(name != NULL) { m_attachments.setValue(name, value); m_attachCount++; } } StringBuffer& CHttpMessage::getAttachment(const char* name, StringBuffer& attachment) { StringBuffer *value = m_attachments.getValue(name); if (value) { attachment.append(value->length(), value->str()); } return attachment; } StringBuffer& CHttpMessage::getParamStr(StringBuffer& ret) { return ret.append(m_paramstr); } IProperties *CHttpMessage::queryParameters() { if (!m_queryparams) m_queryparams.setown(createProperties(false)); return m_queryparams.get(); } IProperties *CHttpMessage::getParameters() { if (!m_queryparams) m_queryparams.setown(createProperties(false)); return m_queryparams.getLink(); } int CHttpMessage::processHeaders(IMultiException *me) { return 0; } int CHttpMessage::readContent() { char buf[1024 + 1]; int buflen = 1024; if(m_content_length > 0) { int totallen = m_content_length; if(buflen > totallen) buflen = totallen; int readlen = 0; for(;;) { readlen = m_bufferedsocket->read(buf, buflen); if(readlen < 0) { DBGLOG(">> Socket timed out because of incorrect Content-Length passed in from the other side"); break; } if(readlen == 0) break; buf[readlen] = 0; m_content.append(readlen, buf); totallen -= readlen; if(totallen <= 0) break; if(buflen > totallen) buflen = totallen; } return 0; } return 0; } int CHttpMessage::readContentTillSocketClosed() { const int buflen = 1024; char buf[buflen + 1]; StringBuffer headerValue; getHeader("Transfer-Encoding", headerValue); if (!stricmp(headerValue.str(), "chunked"))//Transfer-Encoding: chunked { for(;;) { int readlen = m_bufferedsocket->readline(buf, buflen, NULL); if(readlen <= 0) break; buf[readlen] = 0; int chunkSize; sscanf(buf, "%x", &chunkSize); if (chunkSize<=0) break; while (chunkSize > 0) { const int len = min(chunkSize, buflen); readlen = m_bufferedsocket->read(buf, len); if(readlen <= 0) break; chunkSize -= readlen; m_content.append(readlen, buf); } if (m_bufferedsocket->read(buf, 2) <= 0)//CR/LF break; } } else { for(;;) { int readlen = m_bufferedsocket->read(buf, buflen); if(readlen <= 0) break; buf[readlen] = 0; m_content.append(readlen, buf); } } return 0; } int CHttpMessage::receive(bool alwaysReadContent, IMultiException *me) { if (processHeaders(me)==-1) return -1; if (getEspLogLevel()>LogNormal) DBGLOG("Headers processed! content_length = %d", m_content_length); if (isUpload()) return 0; m_context->addTraceSummaryValue("contLen", m_content_length); if(m_content_length > 0) { readContent(); if (getEspLogLevel()>LogNormal) DBGLOG("length of content read = %d", m_content.length()); } else if (alwaysReadContent && m_content_length == -1) { //HTTP protocol does not require a content length: read until socket closed readContentTillSocketClosed(); if (getEspLogLevel()>LogNormal) DBGLOG("length of content read = %d", m_content.length()); } if (getEspLogRequests() || getEspLogLevel()>LogNormal) { if(isTextMessage()) DBGLOG("received HTTP content:\n%s", m_content.str()); } return 0; } StringBuffer& CHttpMessage::getContent(StringBuffer& content) { content.append(m_content.length(), m_content.str()); return content; } void CHttpMessage::setContent(const char* content) { m_content.clear(); m_content.append(content); m_content_length = strlen(content); } void CHttpMessage::setContent(unsigned len, const char* content) { m_content.clear(); m_content.append(len, content); m_content_length = len; } void CHttpMessage::setownContent(char* content) { size32_t len=strlen(content); m_content.setBuffer(len+1, content, len); m_content_length = len; } void CHttpMessage::setownContent(unsigned len, char* content) { m_content.setBuffer(len+1, content, len); m_content_length = len; } void CHttpMessage::setContent(IFileIOStream* stream) { if(stream != NULL) { m_content.clear(); m_content_length = stream->size(); m_content_length64 = stream->size(); m_content_stream.setown(stream); } } /* void CHttpMessage::appendContent(const char* content) { m_content.append(content); m_content_length += strlen(content); } */ StringBuffer& CHttpMessage::getContentType(StringBuffer& contenttype) { contenttype.append(m_content_type); return contenttype; } void CHttpMessage::setContentType(const char* contenttype) { m_content_type.set(contenttype); } void CHttpMessage::setVersion(const char* version) { m_version.set(version); } int CHttpMessage::parseFirstLine(char* oneline) { return 0; } void CHttpMessage::parseCookieHeader(char* cookiestr) { } StringBuffer& CHttpMessage::constructHeaderBuffer(StringBuffer& headerbuf, bool inclLen) { return headerbuf; } int CHttpMessage::send() { StringBuffer headers; constructHeaderBuffer(headers, true); int retcode = 0; // If m_content is empty but m_content_stream is set, the stream will not be logged here. if (getEspLogResponses() || getEspLogLevel(queryContext())>LogNormal) { DBGLOG("Sending out HTTP message:\n%s", headers.str()); if(m_content_length > 0 && m_content.length() > 0) { if(isTextMessage()) DBGLOG("sent HTTP Content:\n%s", m_content.str()); else DBGLOG("sent HTTP "); } } try { m_socket.write(headers.str(), headers.length()); if(m_content_length > 0 && m_content.length() > 0) m_socket.write(m_content.str(), m_content.length()); } catch (IException *e) { StringBuffer estr; DBGLOG("In CHttpMessage::send(%d) -- Exception(%d, %s) writing to socket(%d).", __LINE__, e->errorCode(), e->errorMessage(estr).str(), m_socket.OShandle()); e->Release(); return -1; } catch(...) { ERRLOG("In CHttpMessage::send(%d) -- Unknown exception writing to socket(%d).", __LINE__, m_socket.OShandle()); return -1; } // When m_content is empty but the stream was set, read content from the stream. if(((m_content_length > 0 && m_content.length() == 0) || (m_content_length64 > 0)) && m_content_stream.get() != NULL) { //Read the file and send out 20K at a time. __int64 content_length = m_content_length; if ((m_content_length64 > 0) && (content_length != m_content_length64)) content_length = m_content_length64; int buflen = 20*1024; if(buflen > content_length) buflen = (int) content_length; char* buffer = new char[buflen + 1]; __int64 sizesent = 0; while(sizesent < content_length) { int sizeread = m_content_stream->read(buflen, buffer); if(sizeread > 0) { sizesent += sizeread; try { m_socket.write(buffer, sizeread); } catch (IException *e) { StringBuffer estr; LOG(MCexception(e), "In CHttpMessage::send(%d) -- Exception(%d, %s) writing to socket(%d).", __LINE__, e->errorCode(), e->errorMessage(estr).str(), m_socket.OShandle()); e->Release(); retcode = -1; break; } catch(...) { ERRLOG("In CHttpMessage::send(%d) -- Unknown exception writing to socket(%d).", __LINE__, m_socket.OShandle()); retcode = -1; break; } } else { ERRLOG("Error read from file"); break; } } delete buffer; } return retcode; } int CHttpMessage::startSend() { StringBuffer sendbuf; constructHeaderBuffer(sendbuf, false); if (getEspLogLevel(queryContext())>LogNormal) DBGLOG("Start Sending chunked HTTP message:\n %s", sendbuf.str()); try { m_socket.write(sendbuf.str(), sendbuf.length()); } catch (IException *e) { StringBuffer estr; DBGLOG("In CHttpMessage::send() -- Exception(%d, %s) writing to socket(%d).", e->errorCode(), e->errorMessage(estr).str(), m_socket.OShandle()); e->Release(); return -1; } catch(...) { ERRLOG("In CHttpMessage::send() -- Unknown exception writing to socket(%d).", m_socket.OShandle()); return -1; } return 0; } int CHttpMessage::sendChunk(const char *chunk) { if (getEspLogLevel(queryContext())>LogNormal) DBGLOG("Sending HTTP chunk:\n %s", chunk); try { m_socket.write(chunk, strlen(chunk)); } catch (IException *e) { StringBuffer estr; DBGLOG("In CHttpMessage::send() -- Exception(%d, %s) writing to socket(%d).", e->errorCode(), e->errorMessage(estr).str(), m_socket.OShandle()); e->Release(); return -1; } catch(...) { ERRLOG("In CHttpMessage::send() -- Unknown exception writing to socket(%d).", m_socket.OShandle()); return -1; } return 0; } int CHttpMessage::sendFinalChunk(const char *chunk) { if (getEspLogLevel(queryContext())>LogNormal) DBGLOG("Sending HTTP Final chunk:\n %s", chunk); try { m_socket.write(chunk, strlen(chunk)); m_socket.close(); } catch (IException *e) { StringBuffer estr; DBGLOG("In CHttpMessage::send() -- Exception(%d, %s) writing to socket(%d).", e->errorCode(), e->errorMessage(estr).str(), m_socket.OShandle()); e->Release(); return -1; } catch(...) { ERRLOG("In CHttpMessage::send() -- Unknown exception writing to socket(%d).", m_socket.OShandle()); return -1; } return 0; } int CHttpMessage::close() { int ret = 0; try { if(&m_socket != NULL) { m_socket.shutdown(); m_socket.close(); } } catch (IException *e) { StringBuffer estr; ERRLOG("Exception(%d, %s) - CHttpMessage::close(), closing socket.", e->errorCode(), e->errorMessage(estr).str()); e->Release(); ret = -1; } catch(...) { ERRLOG("General Exception - CHttpMessage::close(), closing socket."); ret = -1; } return ret; } void CHttpMessage::setHeader(const char* headername, const char* headerval) { if(!headername || !*headername) return; StringBuffer val; val.append(headername).append(": ").append(headerval); ForEachItemIn(x, m_headers) { const char* curst = m_headers.item(x); if(!curst) continue; const char* colon = strchr(curst, ':'); if(!colon) continue; if(!strnicmp(headername, curst, colon - curst)) { m_headers.replace(val.str(), x); return; } } m_headers.append(val.str()); } void CHttpMessage::addHeader(const char* headername, const char* headerval) { if(headername == NULL || strlen(headername) == 0) return; StringBuffer header; header.append(headername); header.append(": "); header.append(headerval); m_headers.append(header.str()); } StringBuffer& CHttpMessage::getHeader(const char* headername, StringBuffer& headerval) { if(headername == NULL || strlen(headername) == 0) return headerval; ForEachItemIn(x, m_headers) { const char* header = m_headers.item(x); if(header == NULL) continue; const char* colon = strchr(header, ':'); if(colon == NULL) continue; if(strncmp(headername, header, colon - header) == 0) { headerval.append(colon + 2); break; } } return headerval; } bool isSoapContentType(const char* contenttype) { if(contenttype == NULL) return false; else return Utils::strncasecmp(contenttype, HTTP_TYPE_TEXT_XML, strlen(HTTP_TYPE_TEXT_XML)) == 0 || Utils::strncasecmp(contenttype, HTTP_TYPE_SOAP, strlen(HTTP_TYPE_SOAP)) == 0; } bool CHttpMessage::isSoapMessage() { if(m_content_type.get() == NULL) return false; else if(Utils::strncasecmp(m_content_type.get(), HTTP_TYPE_MULTIPART_RELATED, strlen(HTTP_TYPE_MULTIPART_RELATED)) == 0) { CMimeMultiPart* mpart = queryMultiPart(); if(mpart == NULL) return false; CMimeBodyPart* bpart = mpart->queryRootPart(); if(bpart != NULL && isSoapContentType(bpart->getContentType())) return true; else return false; } else return isSoapContentType(m_content_type.get()); } bool CHttpMessage::isFormSubmission() { return ((hasContentType(NULL) && (m_paramCount + m_attachCount) > 0) || hasContentType(HTTP_TYPE_MULTIPART_FORMDATA) || hasContentType(HTTP_TYPE_FORM_ENCODED)); } /****************************************************************************** CHttpRequest Implementation *******************************************************************************/ CHttpRequest::CHttpRequest(ISocket& socket) : CHttpMessage(socket), m_pathIsParsed(false), m_sstype(sub_serv_unknown), m_MaxRequestEntityLength(0) { }; CHttpRequest::~CHttpRequest() { }; StringBuffer& CHttpRequest::getMethod(StringBuffer & method) { return method.append(m_httpMethod.str()); } void CHttpRequest::setMethod(const char* method) { m_httpMethod.clear().append(method); } StringBuffer& CHttpRequest::getPath(StringBuffer & path) { return path.append(m_httpPath.str()); } void CHttpRequest::setPath(const char* path) { m_httpPath.clear().append(path); } void CHttpRequest::parseQueryString(const char* querystr) { if(!querystr || !*querystr) return; bool useHeap = false; int querystrlen = strlen(querystr); if(querystrlen >= 0x80000) useHeap = true; char* querystrbuf = NULL; if(useHeap) querystrbuf = (char*)malloc(querystrlen + 1); else querystrbuf = (char*)alloca(querystrlen + 1); strcpy(querystrbuf, querystr); char* ptr = querystrbuf; char* curname = ptr; char* curvalue = NULL; while(true) { while(*ptr != '\0' && *ptr != '=' && *ptr != '&') ptr++; if(*ptr == '\0') { StringBuffer nameval; Utils::url_decode(curname, nameval); addParameter(nameval.str(), ""); break; } else if(*ptr == '=') { *ptr = '\0'; ptr++; if(*ptr == '\0') { StringBuffer nameval; Utils::url_decode(curname, nameval); addParameter(nameval.str(), ""); break; } else if(*ptr == '&') { StringBuffer nameval; Utils::url_decode(curname, nameval); addParameter(nameval.str(), ""); ptr++; if(*ptr == '\0') break; else curname = ptr; } else { curvalue = ptr; while(*ptr != '\0' && *ptr != '&') ptr++; if(*ptr == '\0') { StringBuffer nameval; StringBuffer valueval; Utils::url_decode(curname, nameval); Utils::url_decode(curvalue, valueval); addParameter(nameval.str(), valueval.str()); break; } else //*ptr == '&' { *ptr = '\0'; ptr++; StringBuffer nameval; StringBuffer valueval; Utils::url_decode(curname, nameval); Utils::url_decode(curvalue, valueval); addParameter(nameval.str(), valueval.str()); if(*ptr == '\0') break; else curname = ptr; } } } else if(*ptr == '&') { *ptr=0; StringBuffer nameval; Utils::url_decode(curname, nameval); addParameter(nameval.str(), ""); ptr++; if(!*ptr) break; else curname = ptr; } } if(useHeap && querystrbuf != NULL) delete querystrbuf; } int CHttpRequest::parseFirstLine(char* oneline) { //if (getEspLogLevel()>LogNormal) // DBGLOG("First Line of request=%s", oneline); DBGLOG("HTTP First Line: %s", oneline); if(*oneline == 0) return -1; const char* curptr = oneline; StringBuffer method; curptr = Utils::getWord(curptr, method); if(!stricmp(method.str(), POST_METHOD)) { setMethod(POST_METHOD); } else if(!stricmp(method.str(), GET_METHOD)) { setMethod(GET_METHOD); } else if(!stricmp(method.str(), HEAD_METHOD)) { setMethod(HEAD_METHOD); } StringBuffer pathbuf; curptr = Utils::getWord(curptr, pathbuf); int len = pathbuf.length(); char* buff; if(len == 0) { buff = new char[2]; buff[0] = '/'; buff[1] = '\0'; } else { if(pathbuf.charAt(0) != '/') { buff = new char[len + 2]; buff[0] = '/'; strcpy(buff + 1, pathbuf.str()); } else { buff = new char[len + 1]; strcpy(buff, pathbuf.str()); } } char* qmark = strchr(buff, '?'); if(qmark != NULL) { *qmark = '\0'; } // Decode path StringBuffer path; Utils::url_decode(buff, path); setPath(path.str()); // Parse and decode parameters if(qmark != NULL) { char* querystr = qmark + 1; m_paramstr.set(querystr); addParameter("__querystring", querystr); //MORE- requested by dimitri parseQueryString(querystr); } delete[] buff; return 0; } void CHttpRequest::parseCookieHeader(char* cookiestr) { if(cookiestr == NULL) return; int version; char* curword; char* curptr = cookiestr; const char* separators = ",;"; curptr = Utils::getWord(curptr, curword, separators); StringBuffer name, value; Utils::parseNVPair(curword, name, value); CEspCookie* cookie = NULL; if(name.length() == 0 || stricmp(name.str(), "$Version")) { version = 0; cookie = new CEspCookie(name.str(), value.str()); cookie->setVersion(version); m_cookies.append(*cookie); } else { version = atoi(value.str()); } while(curptr != NULL && *curptr != 0) { curptr = Utils::getWord(curptr, curword, separators); if(curword == NULL) break; StringBuffer name, value; Utils::parseNVPair(curword, name, value); if(name.length() == 0) continue; if(name.charAt(0) != '$') { cookie = new CEspCookie(name.str(), value.str()); cookie->setVersion(version); m_cookies.append(*cookie); } else if(stricmp(name.str(), "$Path") == 0 && cookie != NULL) cookie->setPath(value.str()); else if(stricmp(name.str(), "$Domain") == 0 && cookie != NULL) cookie->setDomain(value.str()); else if(stricmp(name.str(), "$Port") == 0 && cookie != NULL) cookie->setPorts(value.str()); } } void CHttpRequest::parseEspPathInfo() { if (!m_pathIsParsed) { m_espPathEx.clear(); m_espMethodName.clear(); m_espServiceName.clear(); size32_t pathlen=m_httpPath.length(); if (!pathlen) m_sstype=(m_queryparams && m_queryparams->hasProp("main")) ? sub_serv_main : sub_serv_root; else { char *pathstr = strdup(m_httpPath.str()); char *finger = pathstr; if (!strnicmp(finger, "http://", 7)) finger+=7; char *thumb=finger; while (*thumb!=0 && *thumb!='/') { if (*thumb=='@' || *thumb==':') { finger=strchr(thumb, '/'); break; } thumb++; } bool missingTrailSlash = false; if (finger) { while (*finger == '/') finger++; //look for the service and method names // if (finger && finger[0] != '\0') { thumb=strchr(finger, '/'); if (thumb) { char *pathex = strchr(thumb+1, '/'); if (pathex) { *pathex=0; m_espPathEx.append(pathex+1); } *thumb=0; m_espMethodName.append(thumb+1); } else missingTrailSlash = true; m_espServiceName.append(finger); } } free(pathstr); if (m_espMethodName.length()) { if (!stricmp(m_espMethodName.str(), "files_")) m_sstype=sub_serv_files; else if (!stricmp(m_espMethodName.str(), "content_")) m_sstype=sub_serv_content; else if (!stricmp(m_espMethodName.str(), "result_")) m_sstype=sub_serv_result; else if (!stricmp(m_espMethodName.str(), "iframe")) m_sstype=sub_serv_iframe; else if (!stricmp(m_espMethodName.str(), "itext")) m_sstype=sub_serv_itext; else if (!stricmp(m_espMethodName.str(), "relogin_")) m_sstype=sub_serv_relogin; else if (!stricmp(m_espMethodName.str(), "version_")) m_sstype=sub_serv_getversion; } //not a special service URL, determine the action type if (m_sstype==sub_serv_unknown) { if (m_queryparams && (m_queryparams->hasProp("wsdl") || m_queryparams->hasProp("wsdl_ext"))) m_sstype=sub_serv_wsdl; else if (m_queryparams && (m_queryparams->hasProp("xsd"))) m_sstype=sub_serv_xsd; else if (m_queryparams && (m_queryparams->hasProp("reqxml_"))) m_sstype=sub_serv_reqsamplexml; else if (m_queryparams && (m_queryparams->hasProp("respxml_"))) m_sstype=sub_serv_respsamplexml; else if (m_queryparams && (m_queryparams->hasProp("soap_builder_"))) m_sstype=sub_serv_soap_builder; else if (m_queryparams && m_queryparams->hasProp("config_")) m_sstype=sub_serv_config; else if (m_espServiceName.length()==0) m_sstype=(m_queryparams && m_queryparams->hasProp("main")) ? sub_serv_main : sub_serv_root; else if (m_espMethodName.length()==0) m_sstype = missingTrailSlash ? sub_serv_index_redirect : sub_serv_index; else if (m_queryparams && m_queryparams->hasProp("form_")) m_sstype=sub_serv_form; else if (m_queryparams && m_queryparams->hasProp("form")) m_sstype=sub_serv_xform; else if (isUpload()) m_sstype=sub_serv_file_upload; else if (getParameterCount())// queryParamStr()!=NULL && *queryParamStr()!=0) m_sstype=sub_serv_query; else m_sstype=sub_serv_method; } } m_pathIsParsed=true; } } void CHttpRequest::getEspPathInfo(sub_service &sstype, StringBuffer *pathEx, StringBuffer *service, StringBuffer *method, bool upcase) { parseEspPathInfo(); sstype = m_sstype; if (pathEx) { pathEx->clear().append(m_espPathEx); if (upcase) pathEx->toUpperCase(); } if (service) { service->clear().append(m_espServiceName); service->toUpperCase(); } if (method) { method->clear().append(m_espMethodName); method->toUpperCase(); } } void CHttpRequest::getBasicRealm(StringBuffer& realm) { StringBuffer authheader; getHeader("WWW-Authenticate", authheader); if(authheader.length() == 0) return; if(Utils::strncasecmp(authheader.str(), "Basic ", strlen("Basic ")) != 0) return; const char* strt = strchr(authheader.str(), '\"'); ++strt; if (strt) { const char* end = strchr(strt, '\"'); if (end) { realm.append(strt, 0, end - strt); } } } int CHttpRequest::getPeerPort() { char peername[256]; return m_socket.peer_name(peername, 256); } StringBuffer& CHttpRequest::getPeer(StringBuffer& Peer) { StringBuffer ForwardIPs; getHeader("X-Forwarded-For", ForwardIPs); if(ForwardIPs.length() != 0) { //IPs will ne in the form xxx.xxx.xxx.xxx,yyy.yyy.yyy.yyy,zzz.zzz.zzz.zzz //We want to take the first IP in the list const char* strt = strchr(ForwardIPs.str(), ','); if(strt!= NULL) Peer.append(strt - ForwardIPs.str(),ForwardIPs.str()); else Peer.appendf("%s",ForwardIPs.str()); } else { char peerchr[256]; int port = m_socket.peer_name(peerchr, 256); Peer.append(peerchr); } return Peer; } void CHttpRequest::getBasicAuthorization(StringBuffer& userid, StringBuffer& password,StringBuffer& realm) { StringBuffer authheader; getHeader("Authorization", authheader); if(authheader.length() == 0) return; if(Utils::strncasecmp(authheader.str(), "Basic ", strlen("Basic ")) != 0) return; StringBuffer uidpair; Utils::base64decode(authheader.length() - strlen("Basic "), authheader.str() + strlen("Basic "), uidpair); const char* pairstr = strchr(uidpair.str(), '\\'); if(pairstr!=NULL) { realm.append(pairstr - uidpair.str(),uidpair.str()); pairstr++; } else { pairstr = uidpair.str(); getBasicRealm(realm); } const char* colon = strchr(pairstr, ':'); if(colon == NULL) { userid.append(pairstr); } else { userid.append(colon - pairstr, pairstr); password.append(colon + 1); } } int CHttpRequest::receive(IMultiException *me) { if (CHttpMessage::receive(false, me)==-1) return -1; //if(hasContentType("application/x-www-form-urlencoded")) if(hasContentType(HTTP_TYPE_FORM_ENCODED)) { parseQueryString(m_content.str()); } else if(hasContentType(HTTP_TYPE_MULTIPART_FORMDATA) && !isUpload()) { CMimeMultiPart* mpart = queryMultiPart(); if(mpart != NULL) { int count = mpart->getBodyCount(); for(int i = 0; i < count; i++) { CMimeBodyPart* bpart = mpart->queryBodyPart(i); if(bpart == NULL) continue; const char* cdisp = bpart->getContentDisposition(); StringBuffer contentbuf; bpart->getContent(contentbuf); StringBuffer namebuf; char* curword = NULL; char* curptr = (char*)cdisp; const char* separators = ";"; bool isFile = false; StringBuffer filename; while(curptr != NULL && *curptr != 0) { curptr = Utils::getWord(curptr, curword, separators, true); if(curword == NULL) break; StringBuffer name, value; Utils::parseNVPair(curword, name, value); if(name.length() > 0 && stricmp(name.str(), "name") == 0) { namebuf.append(value.str()); } else if(name.length() > 0 && stricmp(name.str(), "filename") == 0) { value.swapWith(filename); isFile = true; } } if(!isFile) addParameter(namebuf.str(), contentbuf.str()); else { addParameter(namebuf.str(), filename.str()); addAttachment(namebuf.str(), contentbuf); } } } } m_context->addTraceSummaryTimeStamp("rcv"); return 0; } void CHttpRequest::updateContext() { if(m_context) { m_context->setContextPath(m_httpPath.str()); StringBuffer temp; getPeer(temp); if(temp.length()) m_context->setPeer(temp.str()); m_context->setRequestParameters(queryParameters()); short servPort; temp.clear(); getServAddress(temp, servPort); m_context->setServAddress(temp.str(), servPort); StringBuffer userid, password, realm; getBasicAuthorization(userid, password, realm); if(userid.length() > 0) { m_context->setUserID(userid.str()); m_context->setPassword(password.str()); m_context->setRealm(realm.str()); } if (m_queryparams) { if (m_queryparams->getPropInt("no_ns_")) m_context->addOptions(ESPCTX_NO_NAMESPACES); if (m_queryparams->hasProp("wsdl")) m_context->addOptions(ESPCTX_WSDL); if (m_queryparams->hasProp("wsdl_ext")) m_context->addOptions(ESPCTX_WSDL|ESPCTX_WSDL_EXT); if (m_queryparams->hasProp("no_annot_")) m_context->addOptions(ESPCTX_NO_ANNOTATION); if (m_queryparams->hasProp("all_annot_")) m_context->addOptions(ESPCTX_ALL_ANNOTATION); } // set client version StringBuffer action; getHeader("SOAPAction", action); // URL first, then SOAPAction const char *verstr = queryParameters()->queryProp("ver_"); if (verstr && *verstr) m_context->setClientVersion(atof(verstr)); else { verstr=strstr(action.str(), "ver_="); if (verstr) m_context->setClientVersion(atof(verstr+5)); else m_context->setClientVersion(0.0); } StringBuffer useragent; getHeader("User-Agent", useragent); m_context->setUseragent(useragent.str()); } } StringBuffer& CHttpRequest::constructHeaderBuffer(StringBuffer& headerbuf, bool inclLength) { if(m_httpMethod.length() > 0) headerbuf.append(queryMethod()).append(" "); else headerbuf.append("POST "); if(m_httpPath.length() > 0) headerbuf.append(queryPath()).append(" "); else headerbuf.append("/ "); if(m_version.length() > 0) headerbuf.append(m_version.get()); else headerbuf.append(HTTP_VERSION); headerbuf.append("\r\n"); if(m_host.length() > 0) { headerbuf.append("Host: ").append(m_host.get()); if(m_port != 80) { headerbuf.append(":").append(m_port); } headerbuf.append("\r\n"); } headerbuf.append("Content-Type: "); if(m_content_type.length() > 0) headerbuf.append(m_content_type.get()); else headerbuf.append("text/xml; charset=UTF-8"); headerbuf.append("\r\n"); if(inclLength && m_content_length > 0) headerbuf.append("Content-Length: ").append(m_content_length).append("\r\n"); if(m_cookies.length() > 0) { headerbuf.append("Cookie: "); int version = m_cookies.item(0).getVersion(); if(version >= 1) headerbuf.append("$Version=").append('"').append(version).append('"').append(','); ForEachItemIn(x, m_cookies) { CEspCookie* cookie = &m_cookies.item(x); if(cookie == NULL) continue; cookie->appendToRequestHeader(headerbuf); } headerbuf.append("\r\n"); } ForEachItemIn(x, m_headers) { const char* oneheader = (const char*)m_headers.item(x); headerbuf.append(oneheader).append("\r\n"); } headerbuf.append("\r\n"); return headerbuf; } int CHttpRequest::processHeaders(IMultiException *me) { char oneline[MAX_HTTP_HEADER_LEN + 2]; int lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN + 1, me); if(lenread <= 0) //special case client connected and disconnected, load balancer ping? return -1; else if (lenread > MAX_HTTP_HEADER_LEN) throw createEspHttpException(HTTP_STATUS_BAD_REQUEST_CODE, "Bad Request", HTTP_STATUS_BAD_REQUEST); m_header.set(oneline); parseFirstLine(oneline); lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN + 1, me); while(lenread >= 0 && oneline[0] != '\0') { if(lenread > MAX_HTTP_HEADER_LEN) throw createEspHttpException(HTTP_STATUS_BAD_REQUEST_CODE, "Bad Request", HTTP_STATUS_BAD_REQUEST); m_header.append('\n').append(oneline); parseOneHeader(oneline); lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN + 1, me); } if (getEspLogRequests() || getEspLogLevel()>LogNormal) { DBGLOG("Request Headers:\n%s", m_header.str()); } if(m_content_length > 0 && m_MaxRequestEntityLength > 0 && m_content_length > m_MaxRequestEntityLength && (!isUpload())) throw createEspHttpException(HTTP_STATUS_BAD_REQUEST_CODE, "The request length was too long.", HTTP_STATUS_BAD_REQUEST); return 0; } int CHttpRequest::readContentToFile(StringBuffer netAddress, StringBuffer path) { char buf[1024 + 1]; int buflen = 1024; int readlen = 0; __int64 totallen = m_content_length64; if(buflen > totallen) buflen = (int) totallen; StringBuffer lengthStr; lengthStr.append(m_content_length64); Owned m_multipart = new CMimeMultiPart("1.0", m_content_type.get(), "", "", ""); m_multipart->parseContentType(m_content_type.get()); //Read the first one of data to find out the file name, as well as the beginning of the file content bool lastChuck = false; MemoryBuffer fileContent; StringBuffer fileName; while(fileName.length() < 1) { readlen = m_bufferedsocket->read(buf, buflen); if(readlen < 0) { DBGLOG(">> Socket timed out because of incorrect Content-Length passed in from the other side"); break; } if(readlen == 0) break; buf[readlen] = 0; fileContent.append(readlen, buf); //find out the file name, as well as the beginning of the file content m_multipart->readUploadFile(fileContent, fileName); totallen -= readlen; if(totallen <= 0) break; if(buflen > totallen) buflen = (int) totallen; } if (fileName.length() < 1) { DBGLOG(">> File cannot be uploaded."); return 0; } //Create IFile to store the file StringBuffer name0(fileName), name1(fileName), fileToSave, fileToUse; char* str = (char*) name1.reverse().str(); char* pStr = (char*) strchr(str, '\\'); if (!pStr) pStr = strchr(str, '/'); if (pStr) { pStr[0] = 0; name0.clear().append(str).reverse(); } fileToSave.appendf("%s/%s.part", path.str(), name0.str()); fileToUse.appendf("%s/%s", path.str(), name0.str()); RemoteFilename rfn; SocketEndpoint ep; ep.set(netAddress.str()); rfn.setPath(ep, fileToSave.str()); Owned file = createIFile(rfn); if (!file) { DBGLOG(">> File %s cannot be uploaded.", name0.str()); return 0; } Owned fileio = file->open(IFOcreate); if (!fileio) { DBGLOG(">> File %s cannot be uploaded.", name0.str()); return 0; } //If this is the last chuck of data, we need to remove the boundry line at the end of data if(totallen <= 0) lastChuck = true; else if(totallen < 1024) { //if there is only one chuck left, the boundry line may be broken in the first chuck and the last chuck. lastChuck = true; readlen = m_bufferedsocket->read(buf, buflen); if(readlen < 0) { DBGLOG(">> Socket timed out because of incorrect Content-Length passed in from the other side"); return 0; } if(readlen == 0) return 0; buf[readlen] = 0; fileContent.append(readlen, buf); } //remove the boundry line at the end of data if (lastChuck) m_multipart->checkEndOfFile(fileContent); //Save the data into the file if (fileio->write(0, fileContent.length(), fileContent.toByteArray()) != fileContent.length()) { DBGLOG(">> File %s cannot be uploaded.", name0.str()); return 0; } if(lastChuck) { file->rename(fileToUse.str()); return 0; } //The file has more than two chucks. Now, look though the rest of the chucks. __int64 offset = fileContent.length(); for(;;) { readlen = m_bufferedsocket->read(buf, buflen); if(readlen < 0) { DBGLOG(">> Socket timed out because of incorrect Content-Length passed in from the other side"); break; } if(readlen == 0) break; buf[readlen] = 0; fileContent.clear().append(readlen, buf); //For the last one or two chucks, remove the boundry line at the end of data. totallen -= readlen; if(totallen <= 0) lastChuck = true; else if(totallen < 1024) { lastChuck = true; buflen = (int) totallen; readlen = m_bufferedsocket->read(buf, buflen); if(readlen < 0) { DBGLOG(">> Socket timed out because of incorrect Content-Length passed in from the other side"); break; } if(readlen == 0) break; buf[readlen] = 0; fileContent.append(readlen, buf); } if (lastChuck) { m_multipart->checkEndOfFile(fileContent); DBGLOG(">> The length of the last chuck=<%d>", fileContent.length()); } if (fileio->write(offset, fileContent.length(), fileContent.toByteArray()) != fileContent.length()) { DBGLOG(">> File %s cannot be uploaded.", name0.str()); break; } if (lastChuck) break; offset += readlen; } file->rename(fileToUse.str()); return 0; } /****************************************************************************** CHttpResponse Implementation *******************************************************************************/ CHttpResponse::CHttpResponse(ISocket& socket) : CHttpMessage(socket), m_timeout(BSOCKET_CLIENT_READ_TIMEOUT) { } CHttpResponse::~CHttpResponse() { } void CHttpResponse::setTimeOut(unsigned int timeout) { m_timeout = timeout; } void CHttpResponse::setStatus(const char* status) { m_status.set(status); } StringBuffer& CHttpResponse::getStatus(StringBuffer& status) { status.append(m_status.get()); return status; } StringBuffer& CHttpResponse::constructHeaderBuffer(StringBuffer& headerbuf, bool inclLen) { if(m_version.length() > 0) headerbuf.append(m_version.get()).append(" "); else headerbuf.append(HTTP_VERSION).append(" "); if(m_status.length() > 0) headerbuf.append(m_status.get()); else headerbuf.append(HTTP_STATUS_OK); headerbuf.append("\r\n"); headerbuf.append("Content-Type: "); if(m_content_type.length() > 0) headerbuf.append(m_content_type.get()); else headerbuf.append("text/xml; charset=UTF-8"); headerbuf.append("\r\n"); if(inclLen && m_content_length > 0) headerbuf.append("Content-Length: ").append(m_content_length).append("\r\n"); headerbuf.append("Connection: close\r\n"); ForEachItemIn(x, m_cookies) { CEspCookie* cookie = &m_cookies.item(x); if(cookie == NULL) continue; StringBuffer cookiehn; cookie->getSetCookieHeaderName(cookiehn); headerbuf.append(cookiehn.str()).append(": "); cookie->appendToResponseHeader(headerbuf); headerbuf.append("\r\n"); } ForEachItemIn(i, m_headers) { const char* oneheader = (const char*)m_headers.item(i); headerbuf.append(oneheader).append("\r\n"); } if(m_context.get()) { StringArray& customHeaders = m_context->queryCustomHeaders(); ForEachItemIn(j, customHeaders) { const char* oneheader = (const char*)customHeaders.item(j); if(oneheader && *oneheader) headerbuf.append(oneheader).append("\r\n"); } } headerbuf.append("\r\n"); return headerbuf; } int CHttpResponse::parseFirstLine(char* oneline) { if(*oneline == 0) return -1; if (getEspLogLevel()>LogNormal) DBGLOG("http response status = %s", oneline); char* ptr = oneline; while(*ptr != '\0' && *ptr != ' ') ptr++; if(*ptr != '\0') { *ptr = 0; ptr++; } m_version.set(oneline); while(*ptr == ' ') ptr++; m_status.set(ptr); return 0; } void CHttpResponse::parseOneCookie(char* cookiestr) { if(cookiestr == NULL) return; char* curword; char* curptr = cookiestr; CEspCookie* cookie = NULL; curptr = Utils::getWord(curptr, curword, ";"); StringBuffer name, value; Utils::parseNVPair(curword, name, value); if(name.length() == 0) return; cookie = new CEspCookie(name.str(), value.str()); m_cookies.append(*cookie); while(curptr != NULL && *curptr != 0) { curptr = Utils::getWord(curptr, curword, ";"); if(curword == NULL) break; StringBuffer name, value; Utils::parseNVPair(curword, name, value); if(name.length() == 0) continue; else if(stricmp(name.str(), "Version") == 0) cookie->setVersion(atoi(value.str())); else if(stricmp(name.str(), "Path") == 0) cookie->setPath(value.str()); else if(stricmp(name.str(), "Domain") == 0) cookie->setDomain(value.str()); else if(stricmp(name.str(), "Port") == 0) cookie->setPorts(value.str()); else if(stricmp(name.str(), "Max-Age") == 0) cookie->setMaxAge(atoi(value.str())); else if(stricmp(name.str(), "Discard") == 0) cookie->setDiscard(true); else if(stricmp(name.str(), "Secure") == 0) cookie->setSecure(true); else if(stricmp(name.str(), "Comment") == 0) cookie->setComment(value.str()); else if(stricmp(name.str(), "CommentURL") == 0) cookie->setCommentURL(value.str()); } } void CHttpResponse::parseCookieHeader(char* cookiestr) { if(cookiestr == NULL) return; //TODO: for now assume each Set-Cookie only has one cookie. parseOneCookie(cookiestr); } void CHttpResponse::sendBasicChallenge(const char* realm, bool includeContent) { StringBuffer authheader; authheader.appendf("Basic realm=\"%s\"", realm); addHeader("WWW-Authenticate", authheader.str()); if (includeContent) { setContentType("text/html; charset=UTF-8"); setContent( "" "" "" "ESP - Access Denied" "" "" "" "Access Denied -- Valid username and password required!" "" "" ); } setStatus(HTTP_STATUS_UNAUTHORIZED); send(); } void CHttpResponse::sendBasicChallenge(const char* realm, const char* content) { StringBuffer authheader; authheader.appendf("Basic realm=\"%s\"", realm); addHeader("WWW-Authenticate", authheader.str()); if (content != NULL && *content != '\0') { setContentType("text/html; charset=UTF-8"); setContent(content); } setStatus(HTTP_STATUS_UNAUTHORIZED); send(); } int CHttpResponse::processHeaders(IMultiException *me) { char oneline[MAX_HTTP_HEADER_LEN + 1]; int lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN, me); if(lenread <= 0) return -1; // Process "100 Continue" headers // Some HTTP/1.1 webservers may send back "100 Continue" before it reads the posted request body. while(Utils::strncasecmp(oneline, "HTTP/1.1 100", strlen("HTTP/1.1 100")) == 0) { //Read until empty line, meaning the end of "100 Continue" part while(lenread >= 0 && oneline[0] != '\0') { lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN, me); } // Read the next line, should be the status line. lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN, me); if(lenread <= 0) return 0; } parseFirstLine(oneline); lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN, me); while(lenread >= 0 && oneline[0] != '\0') { parseOneHeader(oneline); lenread = m_bufferedsocket->readline(oneline, MAX_HTTP_HEADER_LEN, me); } return 0; } bool CHttpResponse::httpContentFromFile(const char *filepath) { StringBuffer mimetype; MemoryBuffer content; bool ok = ::httpContentFromFile(filepath, mimetype, content); if (ok) { setContent(content.length(), content.toByteArray()); setContentType(mimetype.str()); setStatus(HTTP_STATUS_OK); } else { setStatus(HTTP_STATUS_NOT_FOUND); } return ok; } int CHttpResponse::receive(IMultiException *me) { // If it's receiving a response, it's behaving as the client side of this conversation. if(m_bufferedsocket.get() != NULL) m_bufferedsocket->setReadTimeout(m_timeout); return CHttpMessage::receive(me); } int CHttpResponse::receive(bool alwaysReadContent, IMultiException *me) { // If it's receiving a response, it's behaving as the client side of this conversation. if(m_bufferedsocket.get() != NULL) m_bufferedsocket->setReadTimeout(m_timeout); if (processHeaders(me)==-1) return -1; if (getEspLogLevel()>LogNormal) DBGLOG("Response headers processed! content_length = %d", m_content_length); char status_class = '2'; if(m_status.length() > 0) status_class = *(m_status.get()); if(m_content_length > 0) { readContent(); if (getEspLogLevel()>LogNormal) DBGLOG("length of response content read = %d", m_content.length()); } else if(alwaysReadContent && status_class != '4' && status_class != '5' && m_content_length == -1) { //HTTP protocol does not require a content length: read until socket closed readContentTillSocketClosed(); if (getEspLogLevel()>LogNormal) DBGLOG("length of content read = %d", m_content.length()); } if (getEspLogRequests() || getEspLogLevel()>LogNormal) { if(isTextMessage()) DBGLOG("received HTTP response = %s", m_content.str()); } return 0; } int CHttpResponse::sendException(IEspHttpException* e) { StringBuffer msg; e->errorMessage(msg); setStatus(e->getHttpStatus()); setContentType(HTTP_TYPE_TEXT_PLAIN); setContent(msg.str()); send(); return 0; } bool CHttpResponse::handleExceptions(IXslProcessor *xslp, IMultiException *me, const char *serv, const char *meth, const char *errorXslt) { IEspContext *context=queryContext(); if (me->ordinality()>0) { StringBuffer text; me->errorMessage(text); text.append('\n'); WARNLOG("Exception(s) in %s::%s - %s", serv, meth, text.str()); if (errorXslt) { me->serialize(text.clear()); StringBuffer theOutput; xslTransformHelper(xslp, text.str(), errorXslt, theOutput, context->queryXslParameters()); setContent(theOutput.str()); setContentType("text/html"); send(); return true; } } return false; }