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::doRequest(const ServiceInfo& service, const BodyChunk& headerPrefix, const std::string& uriParameters, const Http::CookieList& cookieList, const BodyChunk& headerSuffix, const BodyChunkList& bodyChunkList, Http::Response& response, const ServerInfo** serverInfo) const { am_status_t status = AM_SERVICE_NOT_AVAILABLE; int dataLen = 0; BodyChunk emptyContentLine; // 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. const std::size_t contentLineLen_sz = sizeof (CONTENT_LENGTH_HDR) + (sizeof (dataLen) * DIGITS_PER_BYTE) + 2; char contentLine[contentLineLen_sz + 1]; std::size_t contentLineLen = 0; for (unsigned int i = 0; i < bodyChunkList.size(); ++i) { dataLen += bodyChunkList[i].data.size(); } memset(&contentLine[0], 0, contentLineLen_sz); contentLineLen = snprintf(contentLine, contentLineLen_sz, "%s%d\r\n", CONTENT_LENGTH_HDR, dataLen); if (contentLineLen_sz > contentLineLen && contentLineLen > 0) { BodyChunk contentLineChunk(contentLine, contentLineLen); ServiceInfo::const_iterator iter; ServiceInfo svc(service); int j = -1, current_index = -1; if (am_web_naming_validation_status() < 2 && svc.getNumberOfServers() > 1) { /* validation is enabled and number of servers is more than one */ char *current_value = read_naming_value(AM_NAMING_LOCK, Log::getLockId()); if (current_value != NULL) { current_index = strtol(current_value, NULL, 10); if (current_index < 0 || errno == ERANGE) { current_index = 0; } else { Log::log(logModule, Log::LOG_MAX_DEBUG, "BaseService::doRequest(): will be using url index: %d", current_index); } free(current_value); } else { current_index = 0; } if ((size_t) current_index >= svc.getNumberOfServers()) { Log::log(logModule, Log::LOG_WARNING, "BaseService::doRequest(): invalid url index: %d (out of %d); validation results ignored.", current_index, svc.getNumberOfServers()); current_index = -1; } } for (iter = svc.begin(); iter != svc.end(); ++iter) { const ServerInfo &svrInfo = (*iter); if (current_index != -1) { if ((++j) != current_index) { Log::log(logModule, Log::LOG_MAX_DEBUG, "BaseService::doRequest(): skipping url(%d) %s", j, svrInfo.getURL().c_str()); continue; } else { Log::log(logModule, Log::LOG_MAX_DEBUG, "BaseService::doRequest(): using url(%d) %s", j, svrInfo.getURL().c_str()); } } Http::HeaderList headerList, proxyHeaderList; Http::Cookie hostHeader("Host", svrInfo.getHost()); headerList.push_back(hostHeader); #ifdef AGENT_PROXY_SUPPORT #ifndef _MSC_VER 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 size_t 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); } } #endif #endif // 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 { const char *operation = "sending to"; Connection conn(svrInfo.getHost().c_str(), svrInfo.getPort(), svrInfo.useSSL()); #ifdef AGENT_PROXY_SUPPORT std::string requestString = svrInfo.getURI().c_str(); /* * In case the following request would go to a proxy * we need to use full URL and special headers. */ if (useProxy && !(iter->useSSL())) { requestString = iter->getURL(); headerList = proxyHeaderList; } #endif status = sendRequest(conn, headerPrefix, svrInfo.getURI(), uriParameters, headerList, cookieList, dataLen > 0 ? contentLineChunk : emptyContentLine, headerSuffix, bodyChunkList); if (AM_SUCCESS == status) { operation = "receiving from"; status = response.readAndParse(logModule, conn); if (AM_SUCCESS == status) { Log::log(logModule, Log::LOG_MAX_DEBUG, "Response::readAndParse() (%d) %s", response.getBodyLen(), response.getBodyPtr() ? response.getBodyPtr() : "(NULL)"); } } if (AM_NSPR_ERROR == status) { Log::log(logModule, Log::LOG_ALWAYS, "BaseService::doRequest() NSPR failure while " "%s %s", operation, svrInfo.getURL().c_str()); } if (AM_SUCCESS == status) { 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_ERROR, "BaseService::doRequest() caught %s: %s called by %s", exc.what(), exc.getNsprMethod(), exc.getThrowingMethod()); if (retryCount < retryAttempts) { status = AM_NSPR_ERROR; continue; } else { Log::log(logModule, Log::LOG_ERROR, "BaseService::doRequest() Invoking markSeverDown"); /*svrInfo.markServerDown(poll_primary_server);*/ status = AM_NSPR_ERROR; } } } //end of while if (AM_SUCCESS == status) { break; } } // end of for } else { status = AM_BUFFER_TOO_SMALL; } return status; }