OW_HTTPSvrConnection.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2001-2004 Vintela, Inc. All rights reserved.
00003 *
00004 * Redistribution and use in source and binary forms, with or without
00005 * modification, are permitted provided that the following conditions are met:
00006 *
00007 *  - Redistributions of source code must retain the above copyright notice,
00008 *    this list of conditions and the following disclaimer.
00009 *
00010 *  - Redistributions in binary form must reproduce the above copyright notice,
00011 *    this list of conditions and the following disclaimer in the documentation
00012 *    and/or other materials provided with the distribution.
00013 *
00014 *  - Neither the name of Vintela, Inc. nor the names of its
00015 *    contributors may be used to endorse or promote products derived from this
00016 *    software without specific prior written permission.
00017 *
00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc. OR THE CONTRIBUTORS
00022 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00028 * POSSIBILITY OF SUCH DAMAGE.
00029 *******************************************************************************/
00030 
00036 #include "OW_config.h"
00037 #include "OW_HTTPSvrConnection.hpp"
00038 #include "OW_IOException.hpp"
00039 #include "OW_HTTPStatusCodes.hpp"
00040 #include "OW_TempFileStream.hpp"
00041 #include "OW_HTTPChunkedIStream.hpp"
00042 #include "OW_HTTPChunkedOStream.hpp"
00043 #include "OW_HTTPDeflateIStream.hpp"
00044 #include "OW_HTTPDeflateOStream.hpp"
00045 #include "OW_HTTPLenLimitIStream.hpp"
00046 #include "OW_Select.hpp"
00047 #include "OW_Format.hpp"
00048 #include "OW_ConfigOpts.hpp"
00049 #include "OW_Assertion.hpp"
00050 #include "OW_TempFileStream.hpp"
00051 #include "OW_CIMErrorException.hpp"
00052 #include "OW_CIMFeatures.hpp"
00053 #include "OW_HTTPException.hpp"
00054 #include "OW_CIMOMHandleIFC.hpp"
00055 #include "OW_SortedVectorMap.hpp"
00056 #include "OW_StringBuffer.hpp"
00057 #include "OW_ThreadCancelledException.hpp"
00058 #include "OW_OperationContext.hpp"
00059 #include "OW_SessionLanguage.hpp"
00060 #include "OW_AuthenticationException.hpp"
00061 
00062 #if defined(BAD)
00063 #undef BAD
00064 #endif
00065 
00066 #include <iostream>
00067 
00068 namespace OW_NAMESPACE
00069 {
00070 
00071 using std::ios;
00072 using std::istream;
00073 using std::ostream;
00074 using std::flush;
00075 using std::cout;
00076 using std::cerr;
00077 using std::endl;
00078 
00079 namespace
00080 {
00081    const String COMPONENT_NAME("ow.httpserver");
00082 }
00083 
00084 #define OW_LOGDEBUG(x) OW_LOG_DEBUG(m_options.env->getLogger(COMPONENT_NAME), x)
00085 #define OW_LOGERROR(x) OW_LOG_ERROR(m_options.env->getLogger(COMPONENT_NAME), x)
00086 #define OW_LOGCUSTINFO(x) OW_LOG_INFO(m_options.env->getLogger(COMPONENT_NAME), x)
00087 #define OW_LOGFATALERROR(x) OW_LOG_FATAL_ERROR(m_options.env->getLogger(COMPONENT_NAME), x)
00088 
00090 #ifdef OW_WIN32
00091 HTTPSvrConnection::HTTPSvrConnection(
00092    const Socket& socket,
00093    HTTPServer* htin,
00094    HANDLE eventArg,
00095    const HTTPServer::Options& opts)
00096 #else
00097 HTTPSvrConnection::HTTPSvrConnection(const Socket& socket,
00098    HTTPServer* htin,
00099    IntrusiveReference<UnnamedPipe>& upipe,
00100    const HTTPServer::Options& opts)
00101 #endif
00102    : Runnable()
00103    , m_requestLine()
00104    , m_requestHeaders()
00105    , m_pHTTPServer(htin)
00106    , m_socket(socket)
00107    , m_ostr(m_socket.getOutputStream())
00108    , m_resCode(SC_OK)
00109    , m_needSendError(false)
00110    , m_responseHeaders()
00111    , m_httpVersion(HTTP_VER_BAD)
00112    , m_method(BAD)
00113    , m_istr(m_socket.getInputStream())
00114    , m_isClose(false)
00115    , m_contentLength(-1)
00116    , m_chunkedIn(false)
00117    , m_deflateCompressionIn(false)
00118    , m_deflateCompressionOut(false)
00119    , m_errDetails()
00120    , m_reqHeaderPrefix()
00121    , m_respHeaderPrefix()
00122    , m_isAuthenticated(false)
00123 #ifdef OW_WIN32
00124    , m_event(eventArg)
00125 #else
00126    , m_upipe(upipe)
00127 #endif
00128    , m_chunkedOut(false)
00129    , m_userName()
00130    , m_clientIsOpenWBEM2(false)
00131    , m_requestHandler()
00132    , m_options(opts)
00133    , m_shutdown(false)
00134 {
00135    m_socket.setTimeouts(m_options.timeout);
00136 }
00138 // Destructor
00139 HTTPSvrConnection::~HTTPSvrConnection()
00140 {
00141    try
00142    {
00143       m_socket.disconnect();
00144    }
00145    catch (...)
00146    {
00147       // don't let exceptions escape
00148    }
00149 }
00151 void
00152 HTTPSvrConnection::run()
00153 {
00154    CIMProtocolIStreamIFCRef istrToReadFrom(0);
00155    SelectTypeArray selArray;
00156 #ifdef OW_WIN32
00157    Select_t st;
00158    st.event = m_event;
00159    selArray.push_back(st);
00160 #else
00161    selArray.push_back(m_upipe->getSelectObj());
00162 #endif
00163    selArray.push_back(m_socket.getSelectObj());
00164    try
00165    {
00166       m_isAuthenticated = false;
00167       OperationContext context;
00168       while (m_istr.good())
00169       {
00170          //m_isAuthenticated = false;
00171          m_errDetails.erase();
00172          m_requestLine.clear();
00173          m_requestHeaders.clear();
00174          m_reqHeaderPrefix.erase();
00175          m_responseHeaders.clear();
00176          m_needSendError = false;
00177          m_resCode = SC_OK;
00178          m_contentLength = -1;
00179          m_chunkedIn = false;
00180          //
00181          // Add response headers common to all responses
00182          //
00183          addHeader("Date",
00184             HTTPUtils::date());
00185          addHeader("Cache-Control",
00186             "no-cache");
00187          addHeader("Server",
00188             OW_PACKAGE "/" OW_VERSION " (CIMOM)");
00189 
00190          // only select if the buffer is empty
00191          if (m_istr.rdbuf()->in_avail() == 0)
00192          {
00193             int selType = Select::SELECT_INTERRUPTED;
00194             while(selType == Select::SELECT_INTERRUPTED)
00195             {
00196                selType = Select::select(selArray, m_options.timeout * 1000); // *1000 to convert seconds to milliseconds
00197             }
00198 
00199             if (selType == Select::SELECT_ERROR)
00200             {
00201                OW_THROW(SocketException, "Error occurred during select()");
00202             }
00203             if (selType == Select::SELECT_TIMEOUT)
00204             {
00205                m_resCode = SC_REQUEST_TIMEOUT;
00206                m_errDetails = "Timeout waiting for request.";
00207                sendError(m_resCode);
00208                return;
00209             }
00210             if (selType == 0) // Unnamped pipe/event selected
00211             {
00212                m_resCode = SC_SERVICE_UNAVAILABLE;
00213                m_errDetails = "Server is shutting down."
00214                   "  Please try again later.";
00215                sendError(m_resCode);
00216                return;
00217             }
00218          }
00219          else
00220          {
00221             // check for server shutting down message.
00222             int selType = Select::select(selArray, 0); // 0 so we don't block
00223             if (selType == 0) // Unnamped pipe/event selected
00224             {
00225                m_resCode = SC_SERVICE_UNAVAILABLE;
00226                m_errDetails = "Server is shutting down."
00227                   "  Please try again later.";
00228                sendError(m_resCode);
00229                return;
00230             }
00231          }
00232 
00233          if (!HTTPUtils::parseHeader(m_requestHeaders, m_requestLine, m_istr))
00234          {
00235             if (m_shutdown)
00236             {
00237                m_errDetails = "Server is shutting down!";
00238                m_resCode = SC_INTERNAL_SERVER_ERROR;
00239             }
00240             else if (!m_istr)
00241             {
00242                // client closed the socket
00243                return;
00244             }
00245             else
00246             {
00247                m_errDetails = "There was a problem parsing the request Header";
00248                m_resCode = SC_BAD_REQUEST;
00249             }
00250             sendError(m_resCode);
00251             return;
00252          }
00253          //
00254          // Process request line
00255          //
00256          m_resCode = processRequestLine();
00257          if (m_resCode >= 300)   // problem with request detected in request line.
00258          {
00259             sendError(m_resCode);
00260             return;
00261          }
00262 
00263          // Set IP address in context
00264          context.setStringData(OperationContext::CLIENT_IPADDR, m_socket.getPeerAddress().toString());
00265          context.setStringData(OperationContext::HTTP_PATH, m_requestLine[1]);
00266 
00267          //
00268          // Process Headers
00269          //
00270          m_resCode = processHeaders(context);
00271          istrToReadFrom = convertToFiniteStream(m_istr);
00272          if (m_resCode >= 300)   // problem with request detected in headers.
00273          {
00274             cleanUpIStreams(istrToReadFrom);
00275             sendError(m_resCode);
00276             return;
00277          }
00278          
00279          //
00280          // Set up input stream to read entity
00281          //
00282          switch (m_method)
00283          {
00284             case TRACE:
00285                trace();
00286                break;
00287             case M_POST:
00288             case POST:
00289                if (!istrToReadFrom)
00290                {
00291                   OW_THROW(HTTPException,
00292                      "POST, but no content-length or chunking");
00293                }
00294                post(*istrToReadFrom, context);
00295                break;
00296             case OPTIONS:
00297                options(context);
00298                break;
00299             default:
00300                // should never happen.
00301                m_errDetails = "This should never happen.";
00302                m_resCode = SC_INTERNAL_SERVER_ERROR;
00303                cleanUpIStreams(istrToReadFrom);
00304                sendError(m_resCode);
00305                return;
00306          } // switch (m_method)
00307          m_ostr.flush();
00308          cleanUpIStreams(istrToReadFrom);
00309          if (m_isClose)
00310          {
00311             break;
00312          }
00313       } // while (m_istr.good())
00314    } // try
00315    catch (CIMErrorException& cee)
00316    {
00317       addHeader("CIMError", cee.getMessage());
00318       if (m_errDetails.empty())
00319       {
00320          m_errDetails = String("CIMError: ") + cee.getMessage();
00321       }
00322       cleanUpIStreams(istrToReadFrom);
00323       String errMsg(cee.getMessage());
00324       if (errMsg == CIMErrorException::unsupported_protocol_version ||
00325          errMsg == CIMErrorException::multiple_requests_unsupported ||
00326          errMsg == CIMErrorException::unsupported_cim_version ||
00327          errMsg == CIMErrorException::unsupported_dtd_version)
00328       {
00329          sendError(SC_NOT_IMPLEMENTED);
00330       }
00331       else
00332       {
00333          sendError(SC_BAD_REQUEST);
00334       }
00335    }
00336    catch (Exception& e)
00337    {
00338       OW_LOGERROR(Format("%1", e));
00339       m_errDetails = e.getMessage();
00340       cleanUpIStreams(istrToReadFrom);
00341       sendError(SC_INTERNAL_SERVER_ERROR);
00342    }
00343 // gcc 2.x doesn't have ios_base::failure
00344 #if !defined(__GNUC__) || __GNUC__ > 2
00345    catch (std::ios_base::failure& e)
00346    {
00347       // This happens if the socket is closed, so we don't have to do anything.
00348       OW_LOGDEBUG("Caught std::ios_base::failure, client has closed the connection");
00349    }
00350 #endif
00351    catch (std::exception& e)
00352    {
00353       m_errDetails = Format("Caught std::exception (%1) in HTTPSvrConnection::run()", e.what());
00354       OW_LOGERROR(m_errDetails);
00355       cleanUpIStreams(istrToReadFrom);
00356       sendError(SC_INTERNAL_SERVER_ERROR);
00357    }
00358    catch (ThreadCancelledException&)
00359    {
00360       OW_LOGERROR("Got Thread Cancelled Exception in HTTPSvrConnection::run()");
00361       m_errDetails = "HTTP Server thread cancelled";
00362       cleanUpIStreams(istrToReadFrom);
00363       sendError(SC_INTERNAL_SERVER_ERROR);
00364       throw;
00365    }
00366    catch (...)
00367    {
00368       OW_LOGERROR("Got Unknown Exception in HTTPSvrConnection::run()");
00369       m_errDetails = "HTTP Server caught unknown exception";
00370       cleanUpIStreams(istrToReadFrom);
00371       sendError(SC_INTERNAL_SERVER_ERROR);
00372    }
00373    //m_socket.disconnect();
00374 }
00375 void
00376 HTTPSvrConnection::cleanUpIStreams(const CIMProtocolIStreamIFCRef& istr)
00377 {
00378    if (istr)
00379    {
00380       HTTPUtils::eatEntity(*istr);
00381    }
00382 }
00383 void
00384 HTTPSvrConnection::beginPostResponse()
00385 {
00386    m_respHeaderPrefix = HTTPUtils::getCounterStr();
00387    addHeader(
00388       "Content-Type", m_requestHandler->getContentType() + "; charset=\"utf-8\"");
00389    if (m_method == M_POST)
00390    {
00391       addHeader("Ext","");
00392    }
00393    addHeader("Man",
00394       "http://www.dmtf.org/cim/mapping/http/v1.0 ; ns=" + m_respHeaderPrefix);
00395    m_respHeaderPrefix += "-";
00396    if (m_deflateCompressionOut && m_chunkedOut)
00397    {
00398       addHeader("Content-Encoding", "deflate");
00399    }
00400    if (m_chunkedOut) // we only do chunked output is the client says they can handle trailers.
00401    {
00402       addHeader( "Transfer-Encoding", "chunked");
00403       addHeader(m_respHeaderPrefix + "CIMOperation", "MethodResponse");
00404       if (m_clientIsOpenWBEM2) // it uses different pre-standard trailer names
00405       {
00406          addHeader("Trailer",
00407             m_respHeaderPrefix + "CIMError, "
00408             + m_respHeaderPrefix + "CIMErrorCode, "
00409             + m_respHeaderPrefix + "CIMErrorDescription");
00410       }
00411       else // talking to someone who can understand new WBEM Ops 1.2 trailers
00412       {
00413          addHeader("Trailer",
00414             m_respHeaderPrefix + "CIMError, "
00415             + m_respHeaderPrefix + "CIMStatusCode, "
00416             + m_respHeaderPrefix + "CIMStatusDescription");
00417       }
00418       sendHeaders(m_resCode);
00419    }
00420 }
00422 void
00423 HTTPSvrConnection::initRespStream(ostream*& ostrEntity)
00424 {
00425    OW_ASSERT(ostrEntity == 0);
00426 
00427    // Clear out any previous instance. The order is important, since Deflate may hold a pointer to Chunked,
00428    // and it's destructor may call functions on Chunked. Yeah, this is a BAD design!
00429 #ifdef OW_HAVE_ZLIB_H
00430    m_HTTPDeflateOStreamRef = 0;
00431 #endif
00432    m_HTTPChunkedOStreamRef = 0;
00433    m_TempFileStreamRef = 0;
00434 
00435    if (m_chunkedOut)
00436    {
00437       m_HTTPChunkedOStreamRef = new HTTPChunkedOStream(m_ostr);
00438       ostrEntity = m_HTTPChunkedOStreamRef.getPtr();
00439       ostrEntity->exceptions(std::ios::badbit);
00440       if (m_deflateCompressionOut)
00441       {
00442 #ifdef OW_HAVE_ZLIB_H
00443          m_HTTPDeflateOStreamRef = new HTTPDeflateOStream(*ostrEntity);
00444          ostrEntity = m_HTTPDeflateOStreamRef.getPtr();
00445          ostrEntity->exceptions(std::ios::badbit);
00446 #else
00447          OW_THROW(HTTPException, "Trying to deflate output, but no zlib!");
00448 #endif
00449       }
00450    }
00451    else
00452    {
00453       m_TempFileStreamRef = new TempFileStream;
00454       ostrEntity = m_TempFileStreamRef.getPtr();
00455       ostrEntity->exceptions(std::ios::badbit);
00456    }
00457 }
00459 void
00460 HTTPSvrConnection::sendPostResponse(ostream* ostrEntity,
00461    TempFileStream& ostrError, OperationContext& context)
00462 {
00463    int clen = -1;
00464    Int32 errCode = 0;
00465    String errDescr = "";
00466    if (!m_chunkedOut)
00467    {
00468       ostream* ostrToSend = ostrEntity;
00469 
00470       //
00471       // Set content-langage header here
00472       //
00473       bool clientSpecified, setByProvider;
00474       String clang = getContentLanguage(context, setByProvider,
00475          clientSpecified);
00476       if (setByProvider || clientSpecified)
00477       {
00478          addHeader("Content-Language", clang);
00479       }
00480 
00481       if (m_requestHandler && m_requestHandler->hasError(errCode, errDescr))
00482       {
00483          ostrToSend = &ostrError;
00484       }
00485       addHeader(m_respHeaderPrefix + "CIMOperation", "MethodResponse");
00486       TempFileStream* tfs = NULL;
00487       if ((tfs = dynamic_cast<TempFileStream*>(ostrToSend)))
00488       {
00489          clen = tfs->getSize();
00490       }
00491       if (m_deflateCompressionOut && tfs)
00492       {
00493          addHeader("Transfer-Encoding", "chunked");
00494          addHeader("Content-Encoding", "deflate");
00495          sendHeaders(m_resCode, -1);
00496       }
00497       else if (!m_requestHandler->getCIMError().empty())
00498       {
00499          addHeader(m_respHeaderPrefix + "CIMError",
00500             m_requestHandler->getCIMError());
00501       }
00502       else
00503       {
00504          sendHeaders(m_resCode, clen);
00505       }
00506       if (tfs)
00507       {
00508          if (clen > 0)
00509          {
00510             if (m_deflateCompressionOut)
00511             {
00512 #ifdef OW_HAVE_ZLIB_H
00513                // gzip test
00514                HTTPChunkedOStream costr(m_ostr);
00515                HTTPDeflateOStream deflateostr(costr);
00516                deflateostr << tfs->rdbuf();
00517                deflateostr.termOutput();
00518                costr.termOutput(HTTPChunkedOStream::E_SEND_LAST_CHUNK);
00519 #else
00520                OW_THROW(HTTPException, "Attempting to deflate response "
00521                   " but we're not compiled with zlib!  (shouldn't happen)");
00522 #endif // #ifdef OW_HAVE_ZLIB_H
00523             }
00524             else
00525             {
00526                m_ostr << tfs->rdbuf();
00527                if (!m_ostr)
00528                {
00529                   OW_THROW_ERRNO_MSG(IOException, "Failed writing");
00530                }
00531             }
00532          }
00533       }
00534       m_ostr.flush();
00535    } // if !m_chunkedOut
00536    else // m_chunkedOut
00537    {
00538       HTTPChunkedOStream* ostrChunk = NULL;
00539       if (m_deflateCompressionOut)
00540       {
00541 #ifdef OW_HAVE_ZLIB_H
00542          OW_ASSERT(dynamic_cast<HTTPDeflateOStream*>(ostrEntity));
00543          HTTPDeflateOStream* deflateostr = static_cast<HTTPDeflateOStream*>(ostrEntity);
00544          deflateostr->termOutput();
00545          OW_ASSERT(dynamic_cast<HTTPChunkedOStream*>(&deflateostr->getOutputStreamOrig()));
00546          ostrChunk = static_cast<HTTPChunkedOStream*>(&deflateostr->getOutputStreamOrig());
00547 #else
00548          OW_THROW(HTTPException, "Attempting to deflate response "
00549             " but we're not compiled with zlib!  (shouldn't happen)");
00550 #endif
00551       }
00552       else
00553       {
00554          OW_ASSERT(dynamic_cast<HTTPChunkedOStream*>(ostrEntity));
00555          ostrChunk = static_cast<HTTPChunkedOStream*>(ostrEntity);
00556       }
00557       OW_ASSERT(ostrChunk);
00558 
00559       //
00560       // Add trailer for content-language
00561       //
00562       bool clientSpecified, setByProvider;
00563       String clang = getContentLanguage(context, setByProvider,
00564          clientSpecified);
00565       if (setByProvider || clientSpecified)
00566       {
00567          OW_LOGDEBUG(Format("HTTPSvrConnection::sendPostResponse (chunk)"
00568             " setting Content-Language to %1", clang).c_str());
00569 
00570          ostrChunk->addTrailer("Content-Language", clang);
00571       }
00572 
00573       if (m_requestHandler && m_requestHandler->hasError(errCode, errDescr))
00574       {
00575          const char* CIMStatusCodeTrailer = "CIMStatusCode";
00576          if (m_clientIsOpenWBEM2)
00577          {
00578             CIMStatusCodeTrailer = "CIMErrorCode";
00579          }
00580          const char* CIMStatusDescriptionTrailer = "CIMStatusDescription";
00581          if (m_clientIsOpenWBEM2)
00582          {
00583             CIMStatusDescriptionTrailer = "CIMErrorDescription";
00584          }
00585 
00586          ostrChunk->addTrailer(m_respHeaderPrefix + CIMStatusCodeTrailer,
00587             String(errCode));
00588          if (!errDescr.empty())
00589          {
00590             StringArray lines = errDescr.tokenize("\n");
00591             errDescr.erase();
00592             for (size_t i = 0; i < lines.size(); ++i)
00593             {
00594                errDescr += lines[i] + " ";
00595             }
00596             ostrChunk->addTrailer(m_respHeaderPrefix + CIMStatusDescriptionTrailer,
00597                errDescr);
00598          }
00599          if (!m_requestHandler->getCIMError().empty())
00600          {
00601             ostrChunk->addTrailer(m_respHeaderPrefix + "CIMError",
00602                m_requestHandler->getCIMError());
00603          }
00604          ostrChunk->termOutput(HTTPChunkedOStream::E_DISCARD_LAST_CHUNK);
00605       }
00606       else
00607       {
00608          ostrChunk->termOutput(HTTPChunkedOStream::E_SEND_LAST_CHUNK);
00609       }
00610    } // else m_chunkedOut
00611 }
00613 int
00614 HTTPSvrConnection::processRequestLine()
00615 {
00616    switch (m_requestLine.size())
00617    { // first check the request line to determine HTTP version
00618       case 2:
00619          m_httpVersion = HTTP_VER_10;
00620          break;
00621       case 3:
00622          if (m_requestLine[2].equalsIgnoreCase("HTTP/1.1"))
00623          {
00624             m_httpVersion = HTTP_VER_11;
00625          }
00626          else if (m_requestLine[2].equalsIgnoreCase("HTTP/1.0"))
00627          {
00628             m_httpVersion = HTTP_VER_10;
00629          }
00630          else
00631          {
00632             m_httpVersion = HTTP_VER_BAD;
00633             m_errDetails = "The HTTP protocol version " +
00634                m_requestLine[2] + " is not supported by this server.";
00635             return SC_HTTP_VERSION_NOT_SUPPORTED;
00636          }
00637          break;
00638       default:
00639          m_errDetails = "Invalid number of tokens on request line: " +
00640             String(static_cast<unsigned int>(m_requestLine.size()));
00641          return SC_BAD_REQUEST;
00642    }
00643    // check for a supported method.
00644    if (m_requestLine[0].equals("M-POST"))
00645    {
00646       m_method = M_POST;
00647    }
00648    else if (m_requestLine[0].equals("POST"))
00649    {
00650       m_method = POST;
00651    }
00652    else if (m_requestLine[0].equals("TRACE"))
00653    {
00654       m_method = TRACE;
00655    }
00656    else if (m_requestLine[0].equals("OPTIONS"))
00657    {
00658       m_method = OPTIONS;
00659    }
00660    else
00661    {
00662       m_method = BAD;
00663       m_errDetails = "Method not allowed by server: " + m_requestLine[0];
00664       return SC_METHOD_NOT_ALLOWED;
00665    }
00666    // make sure they're trying to hit the right resource
00667    /* TODO: Fix this with respect to listeners
00668    if (!m_requestLine[1].equalsIgnoreCase("/cimom") && m_method != OPTIONS)
00669    {
00670       m_errDetails = "Access to " + m_requestLine[1] +
00671          " is not allowed on this server.";
00672       return SC_FORBIDDEN;
00673    }
00674    */
00675    return SC_OK;
00676 }
00678 // This function may seem large and complex, but it is composed of many
00679 // small, independent blocks.
00680 int
00681 HTTPSvrConnection::processHeaders(OperationContext& context)
00682 {
00683 //
00684 // Check for Authentication
00685 //
00686    // if m_options.allowAnonymous is true, we don't check.
00687    if (m_options.allowAnonymous == false)
00688    {
00689       if (!m_isAuthenticated)
00690       {
00691          m_isAuthenticated = false;
00692          try
00693          {
00694             if (performAuthentication(getHeaderValue("Authorization"), context) < 300 )
00695             {
00696                m_isAuthenticated = true;
00697             }
00698          }
00699          catch (AuthenticationException& e)
00700          {
00701             m_errDetails = e.getMessage();
00702             m_isAuthenticated = false;
00703             return SC_INTERNAL_SERVER_ERROR;
00704          }
00705          if (m_isAuthenticated == false)
00706          {
00707             return SC_UNAUTHORIZED;
00708          }
00709       }
00710       context.setStringData(OperationContext::USER_NAME, m_userName);
00711    }
00712 //
00713 // check for required headers with HTTP/1.1
00714 //
00715    if (m_httpVersion == HTTP_VER_11)
00716    {
00717       if ( ! headerHasKey("Host"))
00718       {
00719          m_errDetails = "Your browser sent a request that this server could"
00720             "not understand.  "
00721             "Client sent HTTP/1.1 request without hostname "
00722             "(see RFC2068 section 9, and 14.23)";
00723          return SC_BAD_REQUEST;
00724       }
00725    }
00726 //
00727 // determine if connection is persistent.
00728 //
00729    if (m_httpVersion != HTTP_VER_11)
00730    {
00731       m_isClose = true;  // pre 1.1 version, no persistent connections.
00732       // TODO what's up with Keep-Alive in 1.0?
00733    }
00734    else
00735    {
00736       if (headerHasKey("Connection"))
00737       {
00738          if (getHeaderValue("Connection").equals("close"))
00739          {
00740             m_isClose = true;
00741          }
00742       }
00743    }
00744 //
00745 // determine content length or transfer-encoding.
00746 //
00747    m_contentLength = -1;
00748    m_chunkedIn = false;
00749    if (headerHasKey("Transfer-Encoding"))
00750    {
00751       // If a Transfer-Encoding header field (section 14.41) is present and
00752       // has any value other than "identity", then the transfer-length is
00753       // defined by use of the "chunked" transfer-coding (section 3.6),
00754       // unless the message is terminated by closing the connection
00755       if (!getHeaderValue("Transfer-Encoding").equals("identity"))
00756       {
00757          m_contentLength = -1;
00758          m_chunkedIn = true;
00759       }
00760    }
00761    if (!m_chunkedIn)
00762    {
00763       // No chunking.  get the content-length.
00764       if (headerHasKey("Content-Length"))
00765       {
00766          String cLen = getHeaderValue("Content-Length");
00767          if (!cLen.empty())
00768          {
00769             m_contentLength = cLen.toInt64();
00770             if (m_contentLength < 0)
00771             {
00772                m_errDetails = "Bad (negative) Content-Length"; 
00773                return SC_BAD_REQUEST;
00774             }
00775          }
00776       }
00777       // POST or M_POST, no chunking: test for content length
00778       // and send 411 if none.
00779       if (m_method == M_POST || m_method == POST)
00780       {
00781          if (m_contentLength < 0 && m_httpVersion == HTTP_VER_11)
00782          {
00783             m_errDetails = "No Content-Length or Transfer-Encoding"
00784                " was specified.";
00785             return SC_LENGTH_REQUIRED;
00786          }
00787       }
00788       // no entity allowed with a trace.
00789       else if (m_method == TRACE)
00790       {
00791          if (m_contentLength > 0 || m_chunkedIn)
00792          {
00793             m_errDetails = "An entity cannot be supplied with the TRACE "
00794                "method.";
00795             return SC_BAD_REQUEST;
00796          }
00797       }
00798    } // if (!m_chunkedIn)
00799 //
00800 // Check for content-encoding
00801 //
00802    m_deflateCompressionIn = false;
00803    if (headerHasKey("Content-Encoding"))
00804    {
00805       String cc = getHeaderValue("Content-Encoding");
00806       if (cc.equalsIgnoreCase("deflate"))
00807       {
00808 #ifdef OW_HAVE_ZLIB_H
00809          m_deflateCompressionIn = true;
00810          m_deflateCompressionOut = m_options.enableDeflate;
00811 #else
00812          m_errDetails = "Content-Encoding \"deflate\" is not supported.  "
00813             "(CIMOM not compiled with zlib)";
00814          return SC_NOT_ACCEPTABLE;
00815 #endif // #ifdef OW_HAVE_ZLIB_H
00816       }
00817       else if (!cc.equals("identity"))
00818       {
00819          m_errDetails = "Invalid Content-Encoding: " + cc
00820 #ifdef OW_HAVE_ZLIB_H
00821             + "  Only \"deflate\" is supported."
00822 #endif
00823             ;
00824          return SC_NOT_ACCEPTABLE;
00825       }
00826    }
00827 //
00828 // Check for correct Accept value
00829 //
00830    if (m_method == POST || m_method == M_POST)
00831    {
00832       if (headerHasKey("Accept"))
00833       {
00834          String ac = getHeaderValue("Accept");
00835          if (ac.indexOf("text/xml") == String::npos
00836             && ac.indexOf("application/xml") == String::npos
00837             && ac.indexOf("*/*") == String::npos
00838             && ac.indexOf("text/*") == String::npos
00839             && ac.indexOf("application/*") == String::npos
00840             )
00841          {
00842             m_errDetails = "Only entities of type \"text/xml\" or "
00843                "\"application/xml\" are supported.";
00844             return SC_NOT_ACCEPTABLE;
00845          }
00846       }
00847    }
00848 //
00849 // Check for Accept charset
00850 //
00851    if (m_method == POST || m_method == M_POST)
00852    {
00853       if (headerHasKey("Accept-Charset"))
00854       {
00855          if (getHeaderValue("Accept-Charset").indexOf("utf-8") == String::npos)
00856          {
00857             m_errDetails = "Only the utf-8 charset is acceptable.";
00858             return SC_NOT_ACCEPTABLE;
00859          }
00860       }
00861    }
00862 //
00863 // Check for Accept-Encoding
00864 //
00865    if (m_method == POST || m_method == M_POST)
00866    {
00867       if (headerHasKey("Accept-Encoding"))
00868       {
00869          if (getHeaderValue("Accept-Encoding").indexOf("deflate") != String::npos)
00870          {
00871 #ifdef OW_HAVE_ZLIB_H
00872             m_deflateCompressionOut = m_options.enableDeflate;
00873 #endif
00874          }
00875          
00876          // TODO I should really look to q != 0 after deflate as well...
00877          /*   // SNIA has Accept-Encoding with no "identity", so this is commented out...
00878          if (getHeaderValue("Accept-Encoding").indexOf("identity") < 0)
00879          {
00880             m_errDetails = "The \"identity\" encoding must be accepted.";
00881             return SC_NOT_ACCEPTABLE;
00882          }
00883          */
00884       }
00885    }
00886 //
00887 // Check for TE header
00888 //
00889    if (getHeaderValue("TE").indexOf("trailers") != String::npos)
00890    {
00891       // Now that trailers are standardized, we'll just reverse this test to
00892       // disable chunking/trailers for broken clients.
00893 
00894       // Trailers not standardized yet, so only do it we're talking to
00895       // ourselves.
00896       //if (getHeaderValue("User-Agent").indexOf(OW_PACKAGE) != String::npos)
00897       //{
00898       // m_chunkedOut = true;
00899       //}
00900       if (getHeaderValue("User-Agent") == "RPT-HTTPClient/0.3-2") // SNIA browser says it can handle trailers when it really can't.
00901       {
00902          m_chunkedOut = false;
00903       }
00904       else
00905       {
00906          m_chunkedOut = true;
00907       }
00908 
00909 
00910       // now we need to see if the client is an 2.0.x version of OW which
00911       // supported a different (pre-standard) version of the trailers.
00912       if (getHeaderValue("User-Agent").startsWith("openwbem/2"))
00913       {
00914          m_clientIsOpenWBEM2 = true;
00915       }
00916    }
00917 //
00918 // Check for Accept-Language
00919 //
00920    if (headerHasKey("Accept-Language"))
00921    {
00922       String al = getHeaderValue("Accept-Language");
00923       if (al.length())
00924       {
00925          SessionLanguageRef psl(new SessionLanguage);
00926          psl->assign(al.c_str());
00927          context.setData(OperationContext::SESSION_LANGUAGE_KEY, psl);
00928          context.setStringData(OperationContext::HTTP_ACCEPT_LANGUAGE_KEY, al);
00929       }
00930    }
00931 
00932 //
00933 // Check for forbidden header keys.
00934 //
00935    if (
00936       headerHasKey("Accept-Ranges")
00937       || headerHasKey("Content-Range")
00938       || headerHasKey("If-Range")
00939       || headerHasKey("Range")
00940       || headerHasKey("Accept-Ranges")
00941       )
00942    {
00943       m_errDetails = "Illegal header in request.  See: "
00944          "http://www.dmtf.org/cim/mapping/http/v1.0";
00945       return SC_NOT_ACCEPTABLE;
00946    }
00947 //
00948 // Content-Language
00949 //
00950    // TODO
00951 //
00952 // Content-Type
00953 // 
00954    if (m_method == M_POST || m_method == POST)
00955    {
00956       if (headerHasKey("Content-Type"))
00957       {
00958          String ct = getHeaderValue("Content-Type");
00959          // strip off the parameters from the content type
00960          ct = ct.substring(0, ct.indexOf(';'));
00961          
00962          // TODO: parse and handle the parameters we may possibly care about.
00963          m_requestHandler = m_options.env->getRequestHandler(ct);
00964          if (!m_requestHandler)
00965          {
00966             m_errDetails = Format("Content-Type \"%1\" is not supported.", ct);
00967             return SC_UNSUPPORTED_MEDIA_TYPE;
00968          }
00969       }
00970       else
00971       {
00972          m_errDetails = "A Content-Type must be specified";
00973          return SC_NOT_ACCEPTABLE;
00974       }
00975    }
00976 //
00977 // Check for "Man: " header and get ns value.
00978 //
00979    if (m_method == M_POST)
00980    {
00981       if (headerHasKey("Man"))
00982       {
00983          String manLine = getHeaderValue("Man");
00984          if (manLine.indexOf("http://www.dmtf.org/cim/mapping/http/v1.0") == String::npos)
00985          {
00986             m_errDetails = "Unknown extension URI";
00987             return SC_NOT_EXTENDED;
00988          }
00989          size_t idx = manLine.indexOf(';');
00990          if (idx > 0 && idx != String::npos)
00991          {
00992             manLine = manLine.substring(idx + 0);
00993             idx = manLine.indexOf("ns");
00994             if (idx != String::npos)
00995             {
00996                idx = manLine.indexOf('=');
00997                if (idx > 0 && idx != String::npos)
00998                {
00999                   m_reqHeaderPrefix = manLine.substring(idx + 1).trim();
01000                }
01001             }
01002          }
01003       } // if (m_requestHeaders.count("Man") > 0)
01004       else
01005       {
01006          m_errDetails = "Cannot use M-POST method with no Man: header.  See: "
01007             "http://www.ietf.org/rfc/rfc2774.txt";
01008          return SC_NOT_EXTENDED;
01009       }
01010    } // if (m_method == M_POST)
01011 //
01012 // Check for Custom OW_BypassLocker header
01013 //
01014    if (headerHasKey(HTTPUtils::Header_BypassLocker))
01015    {
01016       if (getHeaderValue(HTTPUtils::Header_BypassLocker) == HTTPUtils::HeaderValue_true)
01017       {
01018          context.setStringData(OperationContext::BYPASS_LOCKERKEY, "true");
01019       }
01020    }
01021 
01022 //
01023 //
01024 //
01025    return SC_OK;
01026 }
01028 void
01029 HTTPSvrConnection::trace()
01030 {
01031    addHeader("TransferEncoding", "chunked");
01032    sendHeaders(m_resCode);
01033    HTTPChunkedOStream ostr(m_ostr);
01034    for (size_t i = 0; i < m_requestLine.size(); i++)
01035    {
01036       ostr << m_requestLine[i] << " ";
01037    }
01038    ostr << "\r\n";
01039    Map<String, String>::iterator iter;
01040    for (iter = m_requestHeaders.begin(); iter != m_requestHeaders.end(); iter++)
01041    {
01042       ostr << iter->first << ": " << iter->second << "\r\n" ;
01043    }
01044    ostr.termOutput(HTTPChunkedOStream::E_SEND_LAST_CHUNK);
01045 }
01047 void
01048 HTTPSvrConnection::post(istream& istr, OperationContext& context)
01049 {
01050    ostream* ostrEntity = NULL;
01051    initRespStream(ostrEntity);
01052    OW_ASSERT(ostrEntity);
01053    TempFileStream ostrError(400);
01054 
01055    m_requestHandler->setEnvironment(m_options.env);
01056    beginPostResponse();
01057    // process the request
01058 
01059    m_requestHandler->process(&istr, ostrEntity, &ostrError, context);
01060    sendPostResponse(ostrEntity, ostrError, context);
01061 
01062 }
01064 void
01065 HTTPSvrConnection::options(OperationContext& context)
01066 {
01067    addHeader("Allow","POST, M-POST, OPTIONS, TRACE");
01068 #ifdef OW_HAVE_ZLIB_H
01069    if (m_options.enableDeflate)
01070    {
01071       addHeader("Accept-Encoding", "deflate");
01072    }
01073 #endif
01074    String hp = HTTPUtils::getCounterStr();
01075    CIMFeatures cf;
01076    
01077    m_requestHandler = m_options.env->getRequestHandler("application/xml");
01078    if (!m_requestHandler)
01079    {
01080       OW_HTTP_THROW(HTTPException, "OPTIONS is only implemented for XML requests", SC_NOT_IMPLEMENTED);
01081    }
01082    m_requestHandler->setEnvironment(m_options.env);
01083    
01084    m_requestHandler->options(cf, context);
01085    
01086    addHeader("Opt", cf.extURL + " ; ns=" + hp);
01087    hp += "-";
01088    addHeader(hp + "CIMProtocolVersion", cf.protocolVersion);
01089    String headerKey;
01090    switch (cf.cimProduct)
01091    {
01092       case CIMFeatures::SERVER:
01093          if (cf.supportsBatch)
01094          {
01095             addHeader(hp + "CIMSupportsMultipleOperations", "");
01096          }
01097          headerKey = hp + "CIMSupportedFunctionalGroups";
01098          break;
01099       case CIMFeatures::LISTENER:
01100          if (cf.supportsBatch)
01101          {
01102             addHeader(hp + "CIMSupportsMultipleExports", "");
01103          }
01104          headerKey = hp + "CIMSupportedExportGroups";
01105          break;
01106       default:
01107          OW_ASSERT( "Attempting OPTIONS on a CIMProductIFC "
01108             "that is not a LISTENER or SERVER" == 0);
01109    }
01110    String headerVal;
01111    for (size_t i = 0; i < cf.supportedGroups.size(); i++)
01112    {
01113       headerVal += cf.supportedGroups[i];
01114       if (i < cf.supportedGroups.size() - 1)
01115       {
01116          headerVal += ", ";
01117       }
01118    }
01119    addHeader(headerKey, headerVal);
01120    if (!cf.cimom.empty())
01121    {
01122       addHeader(hp + "CIMOM", cf.cimom);
01123    }
01124    if (!cf.validation.empty())
01125    {
01126       addHeader(hp + "CIMValidation", cf.validation);
01127    }
01128    if (cf.supportedQueryLanguages.size() > 0)
01129    {
01130       headerVal.erase();
01131       for (size_t i = 0; i < cf.supportedQueryLanguages.size(); i++)
01132       {
01133          headerVal += cf.supportedQueryLanguages[i];
01134          if (i < cf.supportedQueryLanguages.size() - 1)
01135          {
01136             headerVal += ", ";
01137          }
01138       }
01139       addHeader(hp + "CIMSupportedQueryLanguages", headerVal);
01140    }
01141    sendHeaders(m_resCode);
01142 }
01144 void
01145 HTTPSvrConnection::sendError(int resCode)
01146 {
01147    if (!m_ostr)
01148    {
01149       // connection closed, bail out
01150       return;
01151    }
01152    if (m_socket.receiveTimeOutExpired())
01153    {
01154       resCode = SC_REQUEST_TIMEOUT;
01155       m_errDetails = "Timeout waiting for request.";
01156    }
01157    else if (m_shutdown)
01158    {
01159       resCode = SC_SERVICE_UNAVAILABLE;
01160       m_errDetails = "The server is shutting down.  Please try "
01161          "again later.";
01162    }
01163    String resMessage = HTTPUtils::status2String(resCode) +
01164       ": " + m_errDetails;
01165    String reqProtocol;
01166    if (m_httpVersion == HTTP_VER_11)
01167    {
01168       reqProtocol = "HTTP/1.1";
01169    }
01170    else
01171    {
01172       reqProtocol = "HTTP/1.0";
01173    }
01174    m_ostr << reqProtocol << " " << resCode << " " << resMessage << "\r\n";
01175    // TODO more headers (date and such)
01176    addHeader("Connection", "close");
01177    addHeader("Content-Length", "0");
01178    //addHeader("Content-Length",
01179    // String(tmpOstr.length()));
01180    //addHeader("Content-Type", "text/html");
01181    for (size_t i = 0; i < m_responseHeaders.size(); i++)
01182    {
01183       m_ostr << m_responseHeaders[i] << "\r\n";
01184    }
01185    m_ostr << "\r\n";
01186    m_ostr.flush();
01187 }
01189 int
01190 HTTPSvrConnection::performAuthentication(const String& info, OperationContext& context)
01191 {
01192    if (m_pHTTPServer->authenticate(this, m_userName, info, context, m_socket))
01193    {
01194       return SC_OK;
01195    }
01196    else
01197    {
01198       return SC_UNAUTHORIZED;
01199    }
01200 }
01202 void
01203 HTTPSvrConnection::sendHeaders(int sc, int len)
01204 {
01205    if (len >= 0)
01206    {
01207       addHeader("Content-Length",
01208          String(len));
01209    }
01210    m_ostr << "HTTP/1.1 " << sc << " " << HTTPUtils::status2String(sc) <<
01211       "\r\n";
01212    for (size_t i = 0; i < m_responseHeaders.size(); i++)
01213    {
01214       m_ostr << m_responseHeaders[i] << "\r\n";
01215    }
01216    m_ostr << "\r\n";
01217 }
01219 String
01220 HTTPSvrConnection::getHostName()
01221 {
01222    //return m_socket.getLocalAddress().getName();
01223    return SocketAddress::getAnyLocalHost().getName();
01224 }
01226 CIMProtocolIStreamIFCRef
01227 HTTPSvrConnection::convertToFiniteStream(istream& istr)
01228 {
01229    CIMProtocolIStreamIFCRef rval(0);
01230    if (m_chunkedIn)
01231    {
01232       rval = new HTTPChunkedIStream(istr);
01233    }
01234    else if (m_contentLength > 0)
01235    {
01236       rval = new HTTPLenLimitIStream(istr, UInt64(m_contentLength));
01237    }
01238    else
01239    {
01240       return rval;
01241    }
01242    if (m_deflateCompressionIn)
01243    {
01244 //#ifdef OW_HAVE_ZLIB_H
01245       // TODO: Fix this! we have to keep the HTTPDeflateIStream ctor argument alive for the duration.
01246       //rval = new HTTPDeflateIStream(rval);
01247 //#else
01248       OW_THROW(HTTPException, "Attempting to deflate request, but "
01249             "we're not linked with zlib!  (shouldn't happen)");
01250 //#endif // #ifdef OW_HAVE_ZLIB_H
01251    }
01252    return rval;
01253 }
01255 String
01256 HTTPSvrConnection::getContentLanguage(OperationContext& context,
01257    bool& setByProvider, bool& clientSpecified)
01258 {
01259    setByProvider = false;
01260    clientSpecified = false;
01261    String contentLang = m_options.defaultContentLanguage;
01262 
01263    OperationContext::DataRef dataref = context.getData(
01264       OperationContext::SESSION_LANGUAGE_KEY);
01265    if (!dataref)
01266    {
01267       return contentLang;
01268    }
01269 
01270    SessionLanguageRef slref = dataref.cast_to<SessionLanguage>();
01271    if (!slref)
01272    {
01273       return contentLang;
01274    }
01275 
01276    if (slref->langCount() > 0)
01277    {
01278       clientSpecified = true; // Client specified accept-language
01279    }
01280    String pcl = slref->getContentLanguage();
01281    if (pcl.length())
01282    {
01283       contentLang = pcl;
01284       setByProvider = true;
01285    }
01286 
01287    return contentLang;
01288 }
01289 
01291 void
01292 HTTPSvrConnection::doCooperativeCancel()
01293 {
01294    m_shutdown = true;
01295    m_socket.disconnect();
01296 }
01297 
01298 } // end namespace OW_NAMESPACE
01299 

Generated on Thu Feb 9 08:48:00 2006 for openwbem by  doxygen 1.4.6