00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00036 #include "OW_config.h"
00037 #include "OW_HTTPClient.hpp"
00038 #include "OW_HTTPUtils.hpp"
00039 #include "OW_HTTPChunkedIStream.hpp"
00040 #include "OW_HTTPChunkedOStream.hpp"
00041 #include "OW_HTTPDeflateOStream.hpp"
00042 #include "OW_HTTPDeflateIStream.hpp"
00043 #include "OW_HTTPLenLimitIStream.hpp"
00044 #include "OW_Format.hpp"
00045 #include "OW_TempFileStream.hpp"
00046 #include "OW_Assertion.hpp"
00047 #include "OW_CryptographicRandomNumber.hpp"
00048 #include "OW_HTTPException.hpp"
00049 #include "OW_UserUtils.hpp"
00050 #include "OW_Select.hpp"
00051 #include "OW_SSLCtxMgr.hpp"
00052
00053 #include <fstream>
00054 #include <cerrno>
00055
00056
00057 using namespace std;
00058
00059 namespace OW_NAMESPACE
00060 {
00061
00062 using std::flush;
00063 using std::istream;
00065 HTTPClient::HTTPClient( const String &sURL, const SSLClientCtxRef& sslCtx)
00066 #ifndef OW_DISABLE_DIGEST
00067 : m_iDigestNonceCount(1) ,
00068 #else
00069 :
00070 #endif
00071 m_url(sURL)
00072 , m_pIstrReturn(0)
00073 , m_sslCtx(sslCtx)
00074 , m_socket(
00075 m_url.scheme.endsWith('s') ?
00076 (
00077 m_sslCtx ?
00078 (
00079 m_sslCtx
00080 )
00081 :
00082 (
00083 m_sslCtx = SSLClientCtxRef(new SSLClientCtx()), m_sslCtx
00084 )
00085 )
00086 :
00087 (
00088 SSLClientCtxRef(0)
00089 )
00090 )
00091 , m_requestMethod("M-POST"), m_authRequired(false)
00092 , m_istr(m_socket.getInputStream()), m_ostr(m_socket.getOutputStream())
00093 , m_doDeflateOut(false)
00094 , m_retryCount(0)
00095 , m_httpPath("/cimom")
00096 , m_uselocalAuthentication(false)
00097 {
00098
00099 m_istr.exceptions(std::ios::goodbit);
00100 m_ostr.exceptions(std::ios::goodbit);
00101
00102 #ifndef OW_WIN32
00103
00104 signal(SIGPIPE, SIG_IGN);
00105 #endif
00106
00107 setUrl();
00108 addHeaderPersistent("Host", m_url.host);
00109 addHeaderPersistent("User-Agent", OW_PACKAGE"/"OW_VERSION);
00110
00111 m_socket.setConnectTimeout(60);
00112 m_socket.setReceiveTimeout(600);
00113 m_socket.setSendTimeout(600);
00114 }
00116 HTTPClient::~HTTPClient()
00117 {
00118 try
00119 {
00120 cleanUpIStreams();
00121 }
00122 catch (...)
00123 {
00124
00125 }
00126 }
00128 void
00129 HTTPClient::cleanUpIStreams()
00130 {
00131
00132 if (m_pIstrReturn)
00133 {
00134 HTTPUtils::eatEntity(*m_pIstrReturn);
00135 m_pIstrReturn = 0;
00136 }
00137 }
00139 namespace
00140 {
00141 inline bool isUInt16(const String& s)
00142 {
00143 try
00144 {
00145 s.toUInt16();
00146 return true;
00147 }
00148 catch (StringConversionException& e)
00149 {
00150 return false;
00151 }
00152 }
00153
00154 String getAuthParam(const String& paramName, const String& authInfo)
00155 {
00156 String rval;
00157 size_t pos = authInfo.indexOf(paramName);
00158 if (pos == String::npos)
00159 {
00160 return rval;
00161 }
00162
00163 pos += paramName.length();
00164 if ((pos = authInfo.indexOf('=', pos)) == String::npos)
00165 {
00166 return rval;
00167 }
00168
00169 if (pos + 1 >= authInfo.length())
00170 {
00171 return rval;
00172 }
00173 ++pos;
00174
00175 if (authInfo[pos] == '"')
00176 {
00177 size_t endPos = authInfo.indexOf('"', pos + 1);
00178 if (endPos != String::npos)
00179 {
00180 rval = authInfo.substring(pos + 1, endPos - pos - 1);
00181 }
00182 }
00183 else
00184 {
00185 size_t endPos = authInfo.indexOf(',', pos);
00186 if (endPos != String::npos)
00187 {
00188 rval = authInfo.substring(pos, endPos - pos);
00189 }
00190 else
00191 {
00192 rval = authInfo.substring(pos);
00193 }
00194 }
00195 return rval;
00196 }
00197 }
00199 void HTTPClient::setUrl()
00200 {
00201 if (m_url.scheme.empty())
00202 {
00203 m_url.scheme = "http";
00204 }
00205 if (m_url.port.empty())
00206 {
00207 if ( m_url.scheme.endsWith('s') )
00208 {
00209 m_url.port = "5989";
00210 }
00211 else
00212 {
00213 m_url.port = "5988";
00214 }
00215 }
00216 if (m_url.scheme.endsWith('s'))
00217 {
00218 #ifdef OW_NO_SSL
00219 OW_THROW(SocketException, "SSL not available");
00220 #endif // #ifdef OW_NO_SSL
00221 }
00222 if (m_url.port.equalsIgnoreCase(URL::OWIPC)
00223 || m_url.scheme.equals("ipc"))
00224 {
00225 #ifdef OW_WIN32
00226 OW_THROW(SocketException, "IPC Method not currently available on Win32");
00227 #else
00228 m_serverAddress = SocketAddress::getUDS(OW_DOMAIN_SOCKET_NAME);
00229 #endif
00230 }
00231 else if (isUInt16(m_url.port))
00232 {
00233 m_serverAddress = SocketAddress::getByName(HTTPUtils::unescapeForURL(m_url.host),
00234 m_url.port.toUInt16());
00235 }
00236 else
00237 {
00238 #ifdef OW_WIN32
00239 OW_THROW(SocketException, "IPC Method not currently available on Win32");
00240 #else
00241 m_serverAddress = SocketAddress::getUDS(HTTPUtils::unescapeForURL(m_url.port));
00242 #endif
00243 }
00244
00245 if ((m_url.host == "localhost" || m_url.host == "127.0.0.1") && m_url.principal.empty() && m_url.credential.empty())
00246 {
00247 m_uselocalAuthentication = true;
00248 }
00249 }
00251 void
00252 HTTPClient::receiveAuthentication()
00253 {
00254 String authInfo = getHeaderValue("www-authenticate");
00255 String scheme;
00256 if (!authInfo.empty())
00257 {
00258 scheme = authInfo.tokenize()[0];
00259 scheme.toLowerCase();
00260 }
00261 m_sRealm = getAuthParam("realm", authInfo);
00262
00263 #ifndef OW_DISABLE_DIGEST
00264 CryptographicRandomNumber rn(0, 0x7FFFFFFF);
00265 m_sDigestCNonce.format( "%08x", rn.getNextNumber() );
00266
00267 for (size_t i = 0; i < 4; ++i)
00268 {
00269 String randomData;
00270 randomData.format("%08x", rn.getNextNumber());
00271 m_sDigestCNonce += randomData;
00272 }
00273
00274 if (headerHasKey("authentication-info") && m_sAuthorization=="Digest" )
00275 {
00276 String authInfo = getHeaderValue("authentication-info");
00277 m_sDigestNonce = getAuthParam("nextnonce", authInfo);
00278 getCredentialsIfNecessary();
00279 HTTPUtils::DigestCalcHA1( "md5", m_url.principal, m_sRealm,
00280 m_url.credential, m_sDigestNonce, m_sDigestCNonce, m_sDigestSessionKey );
00281 m_iDigestNonceCount = 1;
00282 }
00283 else if ( scheme.equals("digest"))
00284 {
00285 m_sAuthorization = "Digest";
00286 m_uselocalAuthentication = false;
00287 m_sDigestNonce = getAuthParam("nonce", authInfo);
00288 getCredentialsIfNecessary();
00289 HTTPUtils::DigestCalcHA1( "md5", m_url.principal, m_sRealm,
00290 m_url.credential, m_sDigestNonce, m_sDigestCNonce, m_sDigestSessionKey );
00291 }
00292 else
00293 #endif
00294 if ( scheme.equals("basic"))
00295 {
00296 m_sAuthorization = "Basic";
00297 m_uselocalAuthentication = false;
00298 }
00299 else if ( scheme.equals( "owlocal" ) || m_uselocalAuthentication)
00300 {
00301 m_sAuthorization = "OWLocal";
00302 m_uselocalAuthentication = true;
00303
00304 m_localNonce = getAuthParam("nonce", authInfo);
00305 m_localCookieFile = getAuthParam("cookiefile", authInfo);
00306
00307 }
00308
00309 if (m_sAuthorization.empty())
00310 {
00311 OW_THROW(HTTPException, "No known authentication schemes");
00312 }
00313
00314
00315 }
00316
00318 void HTTPClient::getCredentialsIfNecessary()
00319 {
00320 if (m_url.principal.empty())
00321 {
00322 if (!m_loginCB)
00323 {
00324 OW_THROW(HTTPException, "No login/password to send");
00325 }
00326 else
00327 {
00328 String realm;
00329 if (m_sRealm.empty())
00330 {
00331 realm = m_url.toString();
00332 }
00333 else
00334 {
00335 realm = m_sRealm;
00336 }
00337 String name, passwd;
00338 if (m_loginCB->getCredentials(realm, name, passwd, "") && !name.empty())
00339 {
00340 m_url.principal = name;
00341 m_url.credential = passwd;
00342 }
00343 else
00344 {
00345 OW_THROW(HTTPException, "No login/password to send");
00346 }
00347 }
00348 }
00349
00350 }
00351
00353 void HTTPClient::addCustomHeader(const String& name, const String& value)
00354 {
00355 this->addHeaderPersistent(name, value);
00356 }
00357
00359 bool HTTPClient::getResponseHeader(const String& hdrName,
00360 String& valueOut) const
00361 {
00362 bool cc = false;
00363 if (HTTPUtils::headerHasKey(m_responseHeaders, hdrName))
00364 {
00365 cc = true;
00366 valueOut = HTTPUtils::getHeaderValue(m_responseHeaders, hdrName);
00367 }
00368 return cc;
00369 }
00370
00372 void HTTPClient::sendAuthorization()
00373 {
00374 if ( !m_sAuthorization.empty())
00375 {
00376 OStringStream ostr;
00377 ostr << m_sAuthorization << " ";
00378 if ( m_sAuthorization == "Basic" )
00379 {
00380 getCredentialsIfNecessary();
00381 ostr << HTTPUtils::base64Encode( m_url.principal + ":" +
00382 m_url.credential );
00383 }
00384 #ifndef OW_DISABLE_DIGEST
00385 else if ( m_sAuthorization == "Digest" )
00386 {
00387 String sNonceCount;
00388 sNonceCount.format( "%08x", m_iDigestNonceCount );
00389 HTTPUtils::DigestCalcResponse( m_sDigestSessionKey, m_sDigestNonce, sNonceCount,
00390 m_sDigestCNonce, "auth", m_requestMethod, m_httpPath, "", m_sDigestResponse );
00391 ostr << "username=\"" << m_url.principal << "\", ";
00392 ostr << "realm=\"" << m_sRealm << "\", ";
00393 ostr << "nonce=\"" << m_sDigestNonce << "\", ";
00394 ostr << "uri=\"" + m_httpPath + ", ";
00395 ostr << "qop=\"auth\", ";
00396 ostr << "nc=" << sNonceCount << ", ";
00397 ostr << "cnonce=\"" << m_sDigestCNonce << "\", ";
00398 ostr << "response=\"" << m_sDigestResponse << "\"";
00399 m_iDigestNonceCount++;
00400 }
00401 #endif
00402 else if (m_sAuthorization == "OWLocal")
00403 {
00404 if (m_localNonce.empty())
00405 {
00406
00407
00408 ostr << "uid=\"" << UserUtils::getEffectiveUserId() << "\"";
00409 }
00410 else
00411 {
00412
00413
00414
00415 std::ifstream cookieFile(m_localCookieFile.c_str());
00416 if (!cookieFile)
00417 {
00418 OW_THROW_ERRNO_MSG(HTTPException, "Unable to open local authentication file");
00419 }
00420 String cookie = String::getLine(cookieFile);
00421 ostr << "nonce=\"" << m_localNonce << "\", ";
00422 ostr << "cookie=\"" << cookie << "\"";
00423 }
00424 }
00425 addHeaderNew("Authorization", ostr.toString());
00426 }
00427 }
00428
00430 HTTPClient::EStatusLineSummary
00431 HTTPClient::checkAndExamineStatusLine()
00432 {
00433
00434
00435
00436
00437
00438 if (m_socket.isConnected() && !m_socket.waitForInput(0))
00439 {
00440 getStatusLine();
00441 StringArray statusLine(m_statusLine.tokenize(" "));
00442 if (statusLine.size() < 2)
00443 {
00444 return E_STATUS_ERROR;
00445 }
00446 int statusCode = 500;
00447 try
00448 {
00449 statusCode = statusLine[1].toInt32();
00450 }
00451 catch (const StringConversionException&)
00452 {
00453 return E_STATUS_ERROR;
00454 }
00455 if (statusCode >= 300)
00456 {
00457 return E_STATUS_ERROR;
00458 }
00459 return E_STATUS_GOOD;
00460 }
00461 return E_STATUS_GOOD;
00462 }
00463
00465 void HTTPClient::copyStreams(std::ostream& ostr, std::istream& istr)
00466 {
00467 std::streambuf* outbuf(ostr.rdbuf());
00468 std::streambuf* inbuf(istr.rdbuf());
00469 std::streamsize rv = 0;
00470 std::streamsize curbufsize = inbuf->in_avail();
00471 std::streamsize bytesWritten;
00472
00473 std::vector<char> buffer(curbufsize);
00474 while (curbufsize != -1)
00475 {
00476 if (checkAndExamineStatusLine() == E_STATUS_ERROR)
00477 {
00478 break;
00479 }
00480
00481 streamsize bytesToRead(curbufsize > 0 ? curbufsize : 1);
00482
00483
00484 buffer.reserve(bytesToRead);
00485 buffer.push_back(0);
00486
00487 streamsize charsRead = inbuf->sgetn(&buffer[0], bytesToRead);
00488 bytesWritten = outbuf->sputn(&buffer[0], charsRead);
00489
00490 rv += bytesWritten;
00491 if (bytesWritten != charsRead)
00492 break;
00493
00494 if (std::istream::traits_type::eq_int_type(inbuf->sgetc(), std::istream::traits_type::eof()))
00495 break;
00496
00497 curbufsize = inbuf->in_avail();
00498 }
00499
00500 }
00501
00503 void HTTPClient::sendDataToServer( const Reference<TempFileStream>& tfs,
00504 const String& methodName, const String& cimObject, ERequestType requestType )
00505 {
00506
00507 checkConnection();
00508 handleAuth();
00509 String hp;
00510 if (m_requestMethod.equals("M-POST"))
00511 {
00512 hp = HTTPUtils::getCounterStr();
00513 addHeaderNew("Man", "http://www.dmtf.org/cim/mapping/http/v1.0; ns="
00514 + hp);
00515 hp += "-";
00516 }
00517 else
00518 {
00519 hp.erase();
00520 }
00521
00522 if (requestType == E_CIM_OPERATION_REQUEST || requestType == E_CIM_BATCH_OPERATION_REQUEST)
00523 {
00524 addHeaderNew(hp + "CIMOperation", "MethodCall");
00525
00526 if (requestType == E_CIM_BATCH_OPERATION_REQUEST || methodName.equals("CIMBatch"))
00527 {
00528 addHeaderNew(hp + "CIMBatch", "");
00529 }
00530 else
00531 {
00532 addHeaderNew(hp + "CIMMethod", HTTPUtils::escapeForURL(methodName));
00533 addHeaderNew(hp + "CIMObject", HTTPUtils::escapeForURL(cimObject));
00534 }
00535 }
00536
00537 if (requestType == E_CIM_EXPORT_REQUEST || requestType == E_CIM_BATCH_EXPORT_REQUEST)
00538 {
00539 addHeaderNew(hp + "CIMExport", "MethodRequest");
00540
00541 if (requestType == E_CIM_BATCH_EXPORT_REQUEST || methodName.equals("CIMBatch"))
00542 {
00543 addHeaderNew(hp + "CIMExportBatch", "");
00544 }
00545 else
00546 {
00547 addHeaderNew(hp + "CIMExportMethod", HTTPUtils::escapeForURL(methodName));
00548 }
00549 }
00550
00551 if (m_doDeflateOut)
00552 {
00553
00554 addHeaderNew("Transfer-Encoding", "chunked");
00555 addHeaderNew("Content-Encoding", "deflate");
00556 }
00557
00558
00559 m_statusLine.erase();
00560
00561
00562 sendHeaders(m_requestMethod, "HTTP/1.1");
00563
00564
00565 tfs->rewind();
00566 if (m_doDeflateOut)
00567 {
00568 #ifdef OW_HAVE_ZLIB_H
00569
00570 HTTPChunkedOStream chunkostr(m_ostr);
00571 HTTPDeflateOStream deflateostr(chunkostr);
00572
00573 copyStreams(deflateostr, *tfs);
00574 deflateostr.termOutput();
00575 chunkostr.termOutput();
00576
00577 #else
00578 OW_THROW(HTTPException, "Attempted to deflate output but not "
00579 "compiled with zlib!");
00580 #endif
00581 }
00582 else
00583 {
00584
00585 copyStreams(m_ostr, *tfs);
00586 m_ostr.flush();
00587 }
00588 m_requestHeadersNew.clear();
00589 m_responseHeaders.clear();
00590 }
00592 Reference<std::iostream>
00593 HTTPClient::beginRequest(const String& methodName,
00594 const String& cimObject)
00595 {
00596 return Reference<std::iostream>(new TempFileStream());
00597 }
00599 CIMProtocolIStreamIFCRef
00600 HTTPClient::endRequest(const Reference<std::iostream>& request, const String& methodName,
00601 const String& cimObject, ERequestType requestType, const String& cimProtocolVersion)
00602 {
00603 Reference<TempFileStream> tfs = request.cast_to<TempFileStream>();
00604 OW_ASSERT(tfs);
00605 if (!tfs->good())
00606 {
00607 OW_THROW(HTTPException, "HTTPClient: TempFileStream is bad. Temp file creation failed.");
00608 }
00609
00610 int len = tfs->getSize();
00611
00612 prepareHeaders();
00613 OW_ASSERT(!m_contentType.empty());
00614 addHeaderCommon("Content-Type", m_contentType + "; charset=\"utf-8\"");
00615 if (!m_doDeflateOut)
00616 {
00617 addHeaderCommon("Content-Length", String(len));
00618 }
00619
00620 addHeaderCommon("TE", "trailers");
00621 #ifdef OW_HAVE_ZLIB_H
00622 addHeaderCommon("Accept-Encoding", "deflate");
00623 #endif
00624
00625
00626 if (!cimProtocolVersion.empty() && cimProtocolVersion != "1.0")
00627 {
00628 addHeaderCommon("CIMProtocolVersion", cimProtocolVersion);
00629 }
00630
00631
00632 cleanUpIStreams();
00633
00634
00635 String reasonPhrase;
00636 Resp_t rt = RETRY;
00637 do
00638 {
00639 if (checkAndExamineStatusLine() == E_STATUS_GOOD)
00640 {
00641 sendDataToServer(tfs, methodName, cimObject, requestType);
00642 }
00643 reasonPhrase = checkResponse(rt);
00644 } while (rt == RETRY);
00645 if (rt == FATAL)
00646 {
00647 String CIMError = getHeaderValue("CIMError");
00648 if (CIMError.empty())
00649 {
00650 OW_THROW(HTTPException, Format("Unable to process request: %1",
00651 reasonPhrase).c_str());
00652 }
00653 else
00654 {
00655 OW_THROW(HTTPException, Format("Unable to process request: %1:%2",
00656 reasonPhrase, CIMError).c_str());
00657 }
00658 }
00659 m_pIstrReturn = convertToFiniteStream();
00660 if (!m_pIstrReturn)
00661 {
00662 OW_THROW(HTTPException, "HTTPClient: unable to understand server response. There may be no content in the reply.");
00663 }
00664 return m_pIstrReturn;
00665 }
00667 CIMFeatures
00668 HTTPClient::getFeatures()
00669 {
00670 String methodOrig = m_requestMethod;
00671 m_requestMethod = "OPTIONS";
00672 prepareHeaders();
00673 String reasonPhrase;
00674 Resp_t rt = RETRY;
00675 do
00676 {
00677 checkConnection();
00678 handleAuth();
00679 sendHeaders(m_requestMethod, "HTTP/1.1");
00680 m_ostr.flush();
00681 m_requestHeadersNew.clear();
00682 m_responseHeaders.clear();
00683 m_statusLine.erase();
00684 reasonPhrase = checkResponse(rt);
00685 } while (rt == RETRY);
00686 m_requestMethod = methodOrig;
00687 if (rt == FATAL)
00688 {
00689 OW_THROW(HTTPException, Format("Unable to process request: %1",
00690 reasonPhrase).c_str());
00691 }
00692 if (getHeaderValue("allow").indexOf("M-POST") == String::npos)
00693 {
00694 m_requestMethod = "POST";
00695 }
00696 if (getHeaderValue("Accept-Encoding").indexOf("deflate") != String::npos)
00697 {
00698 m_doDeflateOut = true;
00699 }
00700 String extURL = getHeaderValue("Opt");
00701 size_t idx = extURL.indexOf(';');
00702 if (idx < 1 || idx == String::npos)
00703 {
00704 OW_THROW(HTTPException, "No \"Opt\" header in OPTIONS response");
00705 }
00706 CIMFeatures rval;
00707 rval.extURL = extURL.substring(0, idx);
00708 rval.extURL.trim();
00709 String hp = extURL.substring(idx + 1);
00710 idx = hp.indexOf("=");
00711 hp = hp.substring(idx + 1);
00712 hp.trim();
00713 if (hp.length() != 2)
00714 {
00715 OW_THROW(HTTPException, "HTTP Ext header prefix is not a two digit "
00716 "number");
00717 }
00718 hp += "-";
00719 rval.protocolVersion = getHeaderValue(hp + "CIMProtocolVersion");
00720 String supportedGroups;
00721 rval.supportsBatch = false;
00722 if (headerHasKey(hp + "CIMSupportedFunctionalGroups"))
00723 {
00724 rval.cimProduct = CIMFeatures::SERVER;
00725 supportedGroups = getHeaderValue(hp + "CIMSupportedFunctionalGroups");
00726 if (headerHasKey(hp + "CIMSupportsMultipleOperations"))
00727 {
00728 rval.supportsBatch = true;
00729 }
00730 }
00731 else if (headerHasKey(hp + "CIMSupportedExportGroups"))
00732 {
00733 rval.cimProduct = CIMFeatures::LISTENER;
00734 supportedGroups = getHeaderValue(hp + "CIMSupportedExportGroups");
00735 if (headerHasKey(hp + "CIMSupportsMultipleExports"))
00736 {
00737 rval.supportsBatch = true;
00738 }
00739 }
00740 else
00741 {
00742 OW_THROW(HTTPException, "No CIMSupportedFunctionalGroups or "
00743 "CIMSupportedExportGroups header");
00744 }
00745 rval.supportedGroups = supportedGroups.tokenize(",");
00746 for (size_t i = 0; i < rval.supportedGroups.size(); i++)
00747 {
00748 rval.supportedGroups[i].trim();
00749 }
00750 rval.supportedQueryLanguages =
00751 getHeaderValue(hp + "CIMSupportedQueryLanguages").tokenize(",");
00752 for (size_t i = 0; i < rval.supportedQueryLanguages.size(); i++)
00753 {
00754 rval.supportedQueryLanguages[i].trim();
00755 }
00756 rval.cimom = getHeaderValue(hp + "CIMOM");
00757 rval.validation = getHeaderValue(hp + "CIMValidation");
00758 return rval;
00759 }
00761 void
00762 HTTPClient::prepareForRetry()
00763 {
00764 CIMProtocolIStreamIFCRef tmpIstr = convertToFiniteStream();
00765 if (tmpIstr)
00766 {
00767 HTTPUtils::eatEntity(*tmpIstr);
00768 }
00769 }
00771 void
00772 HTTPClient::sendHeaders(const String& method,
00773 const String& prot)
00774 {
00775 m_ostr << method << ' ' << m_httpPath << ' ' << prot << "\r\n";
00776 for (size_t i = 0; i < m_requestHeadersPersistent.size(); i++)
00777 {
00778 m_ostr << m_requestHeadersPersistent[i] << "\r\n";
00779 }
00780 for (size_t i = 0; i < m_requestHeadersCommon.size(); i++)
00781 {
00782 m_ostr << m_requestHeadersCommon[i] << "\r\n";
00783 }
00784 for (size_t i = 0; i < m_requestHeadersNew.size(); i++)
00785 {
00786 m_ostr << m_requestHeadersNew[i] << "\r\n";
00787 }
00788 m_ostr << "\r\n";
00789 }
00791 HTTPClient::Resp_t
00792 HTTPClient::processHeaders(String& reasonPhrase)
00793 {
00794 if (getHeaderValue("Connection").equalsIgnoreCase("close"))
00795 {
00796 close();
00797 }
00798
00799 Resp_t rt = RETRY;
00800 String statusLine(m_statusLine);
00801 size_t idx = statusLine.indexOf(' ');
00802 String sc;
00803 int isc = 500;
00804 if (idx > 0 && idx != String::npos)
00805 {
00806 statusLine = statusLine.substring(idx + 1);
00807 }
00808 idx = statusLine.indexOf(' ');
00809 if (idx > 0 && idx != String::npos)
00810 {
00811 sc = statusLine.substring(0,idx);
00812 reasonPhrase = statusLine.substring(idx + 1);
00813 try
00814 {
00815 isc = sc.toInt32();
00816 }
00817 catch (const StringConversionException&)
00818 {
00819 return RETRY;
00820 }
00821 }
00822 if (sc.length() != 3)
00823 {
00824 return RETRY;
00825 }
00826 switch (sc[0])
00827 {
00828 case '1':
00829 if (isc == 100)
00830 {
00831 rt = CONTINUE;
00832 }
00833 else
00834 {
00835 rt = FATAL;
00836 }
00837 break;
00838 case '2':
00839 rt = GOOD;
00840 m_authRequired = false;
00841 break;
00842 case '3':
00843 rt = FATAL;
00844 break;
00845 case '4':
00846 close();
00847 switch (isc)
00848 {
00849 case SC_REQUEST_TIMEOUT:
00850 rt = RETRY;
00851 break;
00852 case SC_UNAUTHORIZED:
00853
00854 if (!m_authRequired)
00855 {
00856 m_authRequired = true;
00857 m_retryCount = 0;
00858 rt = RETRY;
00859 }
00860 else
00861 {
00862 rt = FATAL;
00863 }
00864 break;
00865 case SC_METHOD_NOT_ALLOWED:
00866
00867 if (m_requestMethod.equals("M-POST"))
00868 {
00869 m_requestMethod = "POST";
00870 rt = RETRY;
00871 }
00872 else
00873 {
00874 rt = FATAL;
00875 }
00876 break;
00877 default:
00878 close();
00879 rt = FATAL;
00880 break;
00881 }
00882 break;
00883 case '5':
00884 switch (isc)
00885 {
00886 case SC_NOT_IMPLEMENTED:
00887 case SC_NOT_EXTENDED:
00888
00889 if (m_requestMethod.equals("M-POST"))
00890 {
00891 m_requestMethod = "POST";
00892 rt = RETRY;
00893
00894 close();
00895 }
00896 else
00897 {
00898 rt = FATAL;
00899 }
00900 break;
00901 default:
00902 rt = FATAL;
00903 break;
00904 }
00905 break;
00906 default:
00907 rt = RETRY;
00908 break;
00909 }
00910
00911
00912 String CIMError = getHeaderValue("CIMError");
00913 if (!CIMError.empty())
00914 {
00915 rt = FATAL;
00916 }
00917
00918 return rt;
00919 }
00921 CIMProtocolIStreamIFCRef
00922 HTTPClient::convertToFiniteStream()
00923 {
00924 CIMProtocolIStreamIFCRef rval(0);
00925 if (getHeaderValue("Transfer-Encoding").equalsIgnoreCase("chunked"))
00926 {
00927 rval = new HTTPChunkedIStream(m_istr);
00928 }
00929 else if (headerHasKey("Content-Length"))
00930 {
00931 UInt64 clen = getHeaderValue("Content-Length").toUInt64();
00932 rval = new HTTPLenLimitIStream(m_istr,clen);
00933 }
00934 if (getHeaderValue("Content-Encoding").equalsIgnoreCase("deflate"))
00935 {
00936 #ifdef OW_HAVE_ZLIB_H
00937 rval = new HTTPDeflateIStream(rval);
00938 #else
00939 OW_THROW(HTTPException, "Response is deflated but we're not "
00940 "compiled with zlib!");
00941 #endif // #ifdef OW_HAVE_ZLIB_H
00942 }
00943 return rval;
00944 }
00946 void
00947 HTTPClient::handleAuth()
00948 {
00949
00950 if (m_authRequired || m_uselocalAuthentication)
00951 {
00952 receiveAuthentication();
00953 sendAuthorization();
00954 }
00955 }
00957 void
00958 HTTPClient::checkConnection()
00959 {
00960 if (!m_istr || !m_ostr || !m_socket.isConnected())
00961 {
00962 m_socket.disconnect();
00963 m_socket.connect(m_serverAddress);
00964 }
00965 }
00967 void
00968 HTTPClient::getStatusLine()
00969 {
00970
00971 while (m_statusLine.trim().empty() && m_istr)
00972 {
00973 m_statusLine = String::getLine(m_istr);
00974 }
00975 }
00977 String
00978 HTTPClient::checkResponse(Resp_t& rt)
00979 {
00980 String reasonPhrase;
00981 do
00982 {
00983 getStatusLine();
00984
00985 if (!m_istr)
00986 {
00987 if (m_socket.receiveTimeOutExpired())
00988 {
00989 reasonPhrase = Format("Client receive timeout (%1 seconds) expired.", m_socket.getReceiveTimeout());
00990 }
00991 rt = FATAL;
00992 close();
00993 break;
00994 }
00995 if (!HTTPUtils::parseHeader(m_responseHeaders, m_istr))
00996 {
00997 OW_THROW(HTTPException, Format("Received junk from server statusline = %1", m_statusLine).c_str());
00998 }
00999 rt = processHeaders(reasonPhrase);
01000 if (rt == CONTINUE)
01001 {
01002 prepareForRetry();
01003 }
01004 } while (rt == CONTINUE);
01005 if (rt == RETRY)
01006 {
01007 ++m_retryCount;
01008 if (m_retryCount > 3)
01009 {
01010 rt = FATAL;
01011 }
01012 else
01013 {
01014 prepareForRetry();
01015 }
01016 }
01017 else if (rt == GOOD)
01018 {
01019 m_retryCount = 0;
01020 }
01021 return reasonPhrase;
01022 }
01024 void
01025 HTTPClient::prepareHeaders()
01026 {
01027 m_requestHeadersCommon.clear();
01028 m_responseHeaders.clear();
01029 }
01031 SocketAddress
01032 HTTPClient::getLocalAddress() const
01033 {
01034 if (!m_socket.isConnected())
01035 {
01036 m_socket.connect(m_serverAddress);
01037 }
01038 return m_socket.getLocalAddress();
01039 }
01041 SocketAddress
01042 HTTPClient::getPeerAddress() const
01043 {
01044 if (!m_socket.isConnected())
01045 {
01046 m_socket.connect(m_serverAddress);
01047 }
01048 return m_socket.getPeerAddress();
01049 }
01050
01052 void
01053 HTTPClient::setHTTPPath(const String& newPath)
01054 {
01055 m_httpPath = newPath;
01056 }
01057
01059 void HTTPClient::assumeBasicAuth()
01060 {
01061 close();
01062 m_authRequired = true;
01063 m_sAuthorization = "Basic";
01064 m_uselocalAuthentication = false;
01065 }
01066
01068 void HTTPClient::close()
01069 {
01070 m_socket.disconnect();
01071 }
01072
01073
01075 void
01076 HTTPClient::setReceiveTimeout(int seconds)
01077 {
01078 m_socket.setReceiveTimeout(seconds);
01079 }
01080
01082 int
01083 HTTPClient::getReceiveTimeout() const
01084 {
01085 return m_socket.getReceiveTimeout();
01086 }
01087
01089 void
01090 HTTPClient::setSendTimeout(int seconds)
01091 {
01092 m_socket.setSendTimeout(seconds);
01093 }
01094
01096 int
01097 HTTPClient::getSendTimeout() const
01098 {
01099 return m_socket.getSendTimeout();
01100 }
01101
01103 void
01104 HTTPClient::setConnectTimeout(int seconds)
01105 {
01106 m_socket.setConnectTimeout(seconds);
01107 }
01108
01110 int
01111 HTTPClient::getConnectTimeout() const
01112 {
01113 return m_socket.getConnectTimeout();
01114 }
01115
01117 void
01118 HTTPClient::setTimeouts(int seconds)
01119 {
01120 m_socket.setTimeouts(seconds);
01121 }
01122
01123
01124 }
01125