am_status_t BaseService::doHttpGet(const ServiceInfo& service, const std::string& uriParameters, const Http::CookieList& cookieList, Http::Response& response, const ServerInfo** serverInfo) const { am_status_t status; status = doRequest(service, BodyChunk(std::string(HTTP_GET_PREFIX)), uriParameters, cookieList, BodyChunk(std::string(HTTP_GET_SUFFIX)), BodyChunkList(), response, serverInfo); // // NOTE: The omission of a check of the HTTP status here, as compared // with doHttpPost, is intentional. The AuthService code, which is // currently the only code that does a GET, performs its own analysis // of the HTTP status and various headers to determine the appropriate // return code. Doing part of the analysis here and part in the // AuthService code would make things harder to implement and // understand. // return status; }
am_status_t BaseService::doHttpPost(const ServiceInfo& service, const std::string& uriParameters, const Http::CookieList& cookieList, const BodyChunkList& bodyChunkList, Http::Response& response, bool doFormPost, bool checkHTTPRetCode, const ServerInfo **serverInfo) const { am_status_t status; if (doFormPost) { status = doRequest(service, BodyChunk(std::string(HTTP_POST_PREFIX)), uriParameters, cookieList, BodyChunk(std::string(HTTP_POST_FORM_SUFFIX)), bodyChunkList, response, serverInfo); } else { status = doRequest(service, BodyChunk(std::string(HTTP_POST_PREFIX)), uriParameters, cookieList, BodyChunk(std::string(HTTP_POST_SUFFIX)), bodyChunkList, response, serverInfo); } if (checkHTTPRetCode && (AM_SUCCESS == status && Http::OK != response.getStatus())) { Http::Status httpStatus = response.getStatus(); if (Http::NOT_FOUND == httpStatus) { status = AM_NOT_FOUND; } else if (Http::FORBIDDEN == httpStatus) { status = AM_ACCESS_DENIED; } else { Log::log(logModule, Log::LOG_ERROR, "BaseService::doHttpPost() failed, HTTP error = %d", httpStatus); status = AM_HTTP_ERROR; } } return status; }
am_status_t BaseService::doRequest(const ServiceInfo& service, const BodyChunk& headerPrefix, const std::string& uriParameters, const Http::CookieList& cookieList, const BodyChunk& headerSuffix, const BodyChunkList& bodyChunkList, Http::Response& response, std::size_t initialBufferLen, const std::string &cert_nick_name, const ServerInfo** serverInfo) const { am_status_t status = AM_SERVICE_NOT_AVAILABLE; std::size_t dataLen = 0; // Create a temporary buffer for the Content-Line header // the extra '2' is for the <CR><LF> at the end. The // sizeof the CONTENT_LENGTH_HDR includes space for the // terminating NUL. char contentLine[sizeof(CONTENT_LENGTH_HDR) + (sizeof(dataLen) * DIGITS_PER_BYTE) + 2]; std::size_t contentLineLen; for (unsigned int i = 0; i < bodyChunkList.size(); ++i) { dataLen += bodyChunkList[i].data.size(); } contentLineLen = snprintf(contentLine, sizeof(contentLine), "%s%d\r\n", CONTENT_LENGTH_HDR, dataLen); if (sizeof(contentLine) > contentLineLen) { BodyChunk contentLineChunk(contentLine, contentLineLen); ServiceInfo::const_iterator iter; for (iter = service.begin(); iter != service.end(); ++iter) { ServerInfo svrInfo = ServerInfo((const ServerInfo&)(*iter)); if (!svrInfo.isHealthy(poll_primary_server)) { Log::log(logModule, Log::LOG_WARNING, "BaseService::doRequest(): " "Server is unavailable: %s.", svrInfo.getURL().c_str()); continue; } else { Log::log(logModule, Log::LOG_DEBUG, "BaseService::doRequest(): Using server: %s.", iter->getURL().c_str()); } Http::HeaderList headerList, proxyHeaderList; Http::Cookie hostHeader("Host", svrInfo.getHost()); headerList.push_back(hostHeader); if (useProxy) { proxyHeaderList.push_back(hostHeader); // Override (temporarily) server credentials if using proxy svrInfo.setHost(proxyHost); svrInfo.setPort(proxyPort); // We don't use SSL for initial proxy connection svrInfo.setUseSSL(false); Log::log(logModule, Log::LOG_DEBUG, "BaseService::doRequest(): Using proxy: %s:%d", proxyHost.c_str(),proxyPort); // Add Proxy-Authorization header if user defined if (useProxyAuth) { // allocate enough for a base64-encoded digest int authSize = proxyUser.size() + proxyPassword.size() + 1; // 11 extra bytes for prefix and terminator char * digest = (char *)malloc(authSize * 4/3 + 11); strcpy(digest, "Basic "); encode_base64((proxyUser + ":" + proxyPassword).c_str(), authSize,(digest + 6)); Log::log(logModule, Log::LOG_MAX_DEBUG, "BaseService::doRequest(): Using proxy auth as: %s", proxyUser.c_str()); hostHeader = Http::Cookie("Proxy-Authorization", digest); proxyHeaderList.push_back(hostHeader); free(digest); } } // retry to connect to server before marking it as down. // making the number of attempts configurable may have a negative // side effect on performance, if the the value is a high number. int retryAttempts = 3; int retryCount = 0; while(retryCount < retryAttempts) { retryCount++; try { Connection conn(svrInfo, certDBPasswd, (cert_nick_name.size()>0)?cert_nick_name:certNickName, alwaysTrustServerCert); const char *operation = "sending to"; // in case proxy is defined and target URL is HTTPS, // establish an SSL tunnel first send // CONNECT host:port string if (useProxy && iter->useSSL()) { SECStatus secStatus = SECFailure; // All the other parameters would be empty for a // proxy CONNECT Http::CookieList emptyCookieList; BodyChunk emptyChunk; BodyChunkList emptyChunkList; // Add a Keep-alive header since we're using HTTP/1.0 hostHeader = Http::Cookie("Connection", "Keep-Alive\r\n"); proxyHeaderList.push_back(hostHeader); status = sendRequest(conn, BodyChunk(std::string("CONNECT ")), iter->getHost() + ":" + Utils::toString(iter->getPort()), std::string(""), proxyHeaderList, emptyCookieList, emptyChunk, emptyChunk, emptyChunkList); if (status == AM_SUCCESS) { // Retrieve proxie's response if tunnel // established (void) response.readAndIgnore(logModule, conn); // Secure the tunnel now by upgrading the socket PRFileDesc *sock = conn.secureSocket( certDBPasswd, (cert_nick_name.size()>0)? cert_nick_name:certNickName, alwaysTrustServerCert, NULL); if (sock != static_cast<PRFileDesc *>(NULL)) { secStatus = SSL_SetURL(sock, iter->getHost().c_str()); } } if (status != AM_SUCCESS || SECSuccess != secStatus){ Log::log(logModule, Log::LOG_ERROR, "BaseService::doRequest(): could not " "establish a secure proxy tunnel"); // Can't continue and mark server as down as // it was a proxy failure return AM_FAILURE; } } if(Log::isLevelEnabled(logModule, Log::LOG_MAX_DEBUG)) { std::string commString; for(std::size_t i = 0; i<bodyChunkList.size(); ++i) { if(!bodyChunkList[i].secure) { commString.append(bodyChunkList[i].data); } else { commString.append("<secure data>"); } } for(std::size_t commPos = commString.find("%"); commPos != std::string::npos && commPos < commString.size(); commPos = commString.find("%", commPos)) { commString.replace(commPos, 1, "%%"); commPos += 2; } Log::log(logModule, Log::LOG_MAX_DEBUG, commString.c_str()); } std::string requestString = iter->getURI(); /* * In case the following request would go to a proxy * we need to use full URL and special headers. * If the resource is HTTPS, we're not posting our * request to the proxy, but to the server * through proxy tunnel */ if (useProxy && !(iter->useSSL())) { requestString = iter->getURL(); headerList = proxyHeaderList; } status = sendRequest(conn, headerPrefix, requestString, uriParameters, headerList, cookieList, contentLineChunk, headerSuffix, bodyChunkList); if (AM_SUCCESS == status) { operation = "receiving from"; status = response.readAndParse(logModule, conn, initialBufferLen); if (AM_SUCCESS == status) { Log::log(logModule, Log::LOG_MAX_DEBUG, "%.*s", response.getBodyLen(), response.getBodyPtr()); } } if (AM_NSPR_ERROR == status) { PRErrorCode nspr_code = PR_GetError(); Log::log(logModule, Log::LOG_ALWAYS, "BaseService::doRequest() NSPR failure while " "%s %s, error = %s", operation, (*iter).toString().c_str(), PR_ErrorToName(nspr_code)); } if (AM_SUCCESS == status) { if(serverInfo != NULL) *serverInfo = &(*iter); break; } else { if(retryCount < retryAttempts) { continue; } else { Log::log(logModule, Log::LOG_DEBUG, "BaseService::doRequest() Invoking markSeverDown"); svrInfo.markServerDown(poll_primary_server); } } } catch (const NSPRException& exc) { Log::log(logModule, Log::LOG_DEBUG, "BaseService::doRequest() caught %s: %s called by %s " "returned %s", exc.what(), exc.getNsprMethod(), exc.getThrowingMethod(), PR_ErrorToName(exc.getErrorCode())); if(retryCount < retryAttempts) { status = AM_NSPR_ERROR; continue; } else { Log::log(logModule, Log::LOG_DEBUG, "BaseService::doRequest() Invoking markSeverDown"); svrInfo.markServerDown(poll_primary_server); status = AM_NSPR_ERROR; } } } //end of while if (AM_SUCCESS == status) { if(serverInfo != NULL) *serverInfo = &(*iter); break; } if (status = AM_NSPR_ERROR) { continue; } } // end of for } else { status = AM_BUFFER_TOO_SMALL; } return status; }
am_status_t BaseService::sendRequest(Connection& conn, const BodyChunk& headerPrefix, const std::string& uri, const std::string& uriParameters, const Http::HeaderList& headerList, const Http::CookieList& cookieList, const BodyChunk& contentLine, const BodyChunk& headerSuffix, const BodyChunkList& bodyChunkList) const { am_status_t status; std::string requestLine(headerPrefix.data); requestLine.append(uri); requestLine.append(uriParameters); requestLine.append(HTTP_VERSION_SUFFIX); Log::log(logModule, Log::LOG_MAX_DEBUG, "BaseService::sendRequest " "Request line: %s", requestLine.c_str()); status = sendChunk(conn, BodyChunk(requestLine)); if (AM_SUCCESS == status) { std::string cookieLine; // XXX - Need to send cookies here. size_t hListSize = headerList.size(); Http::Cookie theCookie; for(size_t idx = 0; idx < hListSize; idx++) { theCookie = headerList[idx]; cookieLine.append(theCookie.name); cookieLine.append(": "); cookieLine.append(theCookie.value); cookieLine.append("\r\n"); } size_t listSize = cookieList.size(); if(listSize > 0) { cookieLine.append("Cookie: "); for (size_t iii=0; iii < listSize; iii++) { theCookie = cookieList[iii]; cookieLine.append(theCookie.name); cookieLine.append("="); cookieLine.append(theCookie.value); if (iii == listSize-1) { cookieLine.append("\r\n"); } else { cookieLine.append(";"); } } } Log::log(logModule, Log::LOG_DEBUG, "BaseService::sendRequest " "Cookie and Headers =%s ", cookieLine.c_str()); if (cookieLine.size() > 0) { status = sendChunk(conn, BodyChunk(cookieLine)); } Log::log(logModule, Log::LOG_DEBUG, "BaseService::sendRequest " "Content-Length =%s.", contentLine.data.c_str()); if (AM_SUCCESS == status) { status = sendChunk(conn, contentLine); } Log::log(logModule, Log::LOG_DEBUG, "BaseService::sendRequest " "Header Suffix =%s ", headerSuffix.data.c_str()); if (AM_SUCCESS == status) { status = sendChunk(conn, headerSuffix); if (AM_SUCCESS == status) { Log::log(logModule, Log::LOG_MAX_DEBUG, "BaseService::sendRequest(): " "Total chunks: %ld.", bodyChunkList.size()); std::size_t i = 0; for (i = 0; i < bodyChunkList.size(); ++i) { status = sendChunk(conn, bodyChunkList[i]); if (AM_SUCCESS != status) { Log::log(logModule, Log::LOG_ERROR, "BaseService::sendRequest " "Sending chunk %ld failed with error: %s", i, am_status_to_string(status)); break; } } Log::log(logModule, Log::LOG_MAX_DEBUG, "BaseService::sendRequest(): " "Sent %ld chunks.", i); } } } return status; }
am_status_t LogService::addLogDetails(const std::string& logName, const LogRecord& record, const std::string& loggedByTokenID) { ScopeLock scopeLock(mLock); char logLevel[32]; am_status_t status = AM_SUCCESS; char *msg = NULL; std::string message = record.getLogMessage(); //The encoded log message needs to be in multiple of 4 bytes. msg = (char *)malloc(((message.size() * 4/3 + 1)/4+1)*4 + 4); if(msg != NULL) { encode_base64(message.c_str(), message.size(), msg); } else { status = AM_NO_MEMORY; } if (status == AM_SUCCESS) { if(!remoteBodyChunkListInitialized) { const std::size_t NUM_EXTRA_CHUNKS = 50; remoteRequest = new Request(*this, requestPrefixChunk, logRequestPrefixChunk, NUM_EXTRA_CHUNKS); if (remoteRequest != NULL) { remoteBodyChunkList = remoteRequest->getBodyChunkList(); remoteBodyChunkListInitialized = true; } } if(bufferCount >= 1) { remoteBodyChunkList.push_back(additionalRequestPrefixChunk); BodyChunk temp; char serviceIdBuf[1024]; if (remoteRequest != NULL) { remoteRequest->getNextServiceRequestIdAsString( serviceIdBuf, sizeof(serviceIdBuf)); } temp.data = serviceIdBuf; remoteBodyChunkList.push_back(temp); } sprintf(logLevel, "%d", record.getLogLevel()); remoteBodyChunkList.push_back(logLogPrefixChunk); remoteBodyChunkList.push_back(BodyChunk(logName)); remoteBodyChunkList.push_back(logSidPrefixChunk); remoteBodyChunkList.push_back(BodyChunk(loggedByTokenID)); remoteBodyChunkList.push_back(logLogSuffixChunk); remoteBodyChunkList.push_back(logRecordPrefixChunk); remoteBodyChunkList.push_back(logLevelPrefixChunk); remoteBodyChunkList.push_back(BodyChunk(std::string(logLevel))); remoteBodyChunkList.push_back(logLevelSuffixChunk); remoteBodyChunkList.push_back(logRecMsgPrefixChunk); std::string t_mesg(msg); free(msg); Utils::expandEntityRefs(t_mesg); remoteBodyChunkList.push_back(BodyChunk(t_mesg)); remoteBodyChunkList.push_back(logRecMsgSuffixChunk); remoteBodyChunkList.push_back(logInfoMapPrefixChunk); const Properties &properties = record.getLogInfo(); Properties::const_iterator iter = properties.begin(); for(; iter != properties.end(); iter++) { const Properties::key_type &k_iter = iter->first; const Properties::mapped_type &v_iter = iter->second; std::string keyStr(""); keyStr = k_iter.c_str(); std::string valueStr(""); valueStr = v_iter.c_str(); remoteBodyChunkList.push_back(logInfoKeyPrefixChunk); remoteBodyChunkList.push_back(BodyChunk(keyStr)); remoteBodyChunkList.push_back(logInfoKeySuffixChunk); remoteBodyChunkList.push_back(logInfoValuePrefixChunk); remoteBodyChunkList.push_back(BodyChunk(valueStr)); remoteBodyChunkList.push_back(logInfoValueSuffixChunk); } remoteBodyChunkList.push_back(logInfoMapSuffixChunk); remoteBodyChunkList.push_back(logRecordSuffixChunk); remoteBodyChunkList.push_back(requestSuffixChunk); bufferCount++; } else { status = AM_NO_MEMORY; } return status; }
// this does not throw any exceptions since it returns a status. am_status_t LogService::logMessage(const ServiceInfo& service, const SSOToken& logged_by_token, const Http::CookieList& ckieList, const std::string& message, const std::string& logname) throw() { am_status_t status = AM_SUCCESS; encodedMessage = NULL; //The encoded log message needs to be in multiple of 4 bytes. encodedMessage = (char *)malloc(((message.size() * 4/3 + 1)/4 + 1)*4 + 4); if(encodedMessage != NULL) { encode_base64(message.c_str(), message.size(), encodedMessage); } else { status = AM_NO_MEMORY; } if (status == AM_SUCCESS) { if(logged_by_token.isValid()) { const std::size_t NUM_EXTRA_CHUNKS = 10; Request request(*this, requestPrefixChunk, logRequestPrefixChunk, NUM_EXTRA_CHUNKS); Http::Response response; BodyChunkList& bodyChunkList = request.getBodyChunkList(); bodyChunkList.push_back(logLogPrefixChunk); bodyChunkList.push_back(BodyChunk(logname)); bodyChunkList.push_back(logSidPrefixChunk); bodyChunkList.push_back(BodyChunk(logged_by_token.getString())); bodyChunkList.push_back(logLogSuffixChunk); bodyChunkList.push_back(logRecordPrefixChunk); bodyChunkList.push_back(logRecordTypeChunk); bodyChunkList.push_back(logRecMsgPrefixChunk); /* Ensuring message is correctly entity ref'ed. */ std::string t_string(encodedMessage); free(encodedMessage); Utils::expandEntityRefs(t_string); bodyChunkList.push_back(BodyChunk(t_string)); bodyChunkList.push_back(logRecMsgSuffixChunk); bodyChunkList.push_back(logRecordSuffixChunk); bodyChunkList.push_back(requestSuffixChunk); bodyChunkList.push_back(requestSetSuffixChunk); status = doHttpPost(service, std::string(), ckieList, bodyChunkList, response); if (AM_SUCCESS == status) { try { std::vector<std::string> loggingResponses; // don't know if the log response xml shares the basic/generic // format. loggingResponses = parseGenericResponse(response, request.getGlobalId()); status = AM_ERROR_PARSING_XML; if (loggingResponses.empty()) { status = AM_ERROR_PARSING_XML; } else { status = AM_SUCCESS; // return success only if all responses are successful for (std::size_t i = 0; i < loggingResponses.size(); ++i) { if (strstr(loggingResponses[i].c_str(), "OK")) { continue; } else if (strstr(loggingResponses[i].c_str(), "INVALID_SESSION")) { status = AM_ACCESS_DENIED; break; } else if (strstr(loggingResponses[i].c_str(), "UNAUTHORIZED")) { status = AM_ACCESS_DENIED; break; } else if (strstr(loggingResponses[i].c_str(), "ERROR")) { status = AM_REMOTE_LOG_FAILURE; break; } else { // unrecognized response status = AM_ERROR_PARSING_XML; break; } } } } catch (const XMLTree::ParseException& exc) { Log::log(logModule, Log::LOG_ERROR, "LogService::logMessage() caught exception: %s", exc.getMessage().c_str()); status = AM_ERROR_PARSING_XML; } catch (std::exception& exs) { Log::log(logModule, Log::LOG_ERROR, "LogService::logMessage() caught exception: %s", exs.what()); status = AM_ERROR_PARSING_XML; } catch (...) { Log::log(logModule, Log::LOG_ERROR, "LogService::logMessage() caught unknown exception"); status = AM_ERROR_PARSING_XML; } } } else { status = AM_INVALID_ARGUMENT; } } else { status = AM_NO_MEMORY; } return status; }