(file) Return to HTTPConnection.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Common

   1 martin 1.160 //%LICENSE////////////////////////////////////////////////////////////////
   2 martin 1.161 //
   3 martin 1.160 // Licensed to The Open Group (TOG) under one or more contributor license
   4              // agreements.  Refer to the OpenPegasusNOTICE.txt file distributed with
   5              // this work for additional information regarding copyright ownership.
   6              // Each contributor licenses this file to you under the OpenPegasus Open
   7              // Source License; you may not use this file except in compliance with the
   8              // License.
   9 martin 1.161 //
  10 martin 1.160 // Permission is hereby granted, free of charge, to any person obtaining a
  11              // copy of this software and associated documentation files (the "Software"),
  12              // to deal in the Software without restriction, including without limitation
  13              // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  14              // and/or sell copies of the Software, and to permit persons to whom the
  15              // Software is furnished to do so, subject to the following conditions:
  16 martin 1.161 //
  17 martin 1.160 // The above copyright notice and this permission notice shall be included
  18              // in all copies or substantial portions of the Software.
  19 martin 1.161 //
  20 martin 1.160 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  21 martin 1.161 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22 martin 1.160 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  23              // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  24              // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  25              // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  26              // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27 martin 1.161 //
  28 martin 1.160 //////////////////////////////////////////////////////////////////////////
  29 kumpf  1.120 //
  30 mike   1.2   //%/////////////////////////////////////////////////////////////////////////////
  31              
  32              #include <Pegasus/Common/Config.h>
  33 kumpf  1.16  #include <Pegasus/Common/Constants.h>
  34 mike   1.2   
  35 mike   1.112 #include "Network.h"
  36 mike   1.2   #include <iostream>
  37              #include <cctype>
  38              #include <cstdlib>
  39 mike   1.116 #include "Signal.h"
  40 mike   1.2   #include "Socket.h"
  41              #include "TLS.h"
  42              #include "HTTPConnection.h"
  43 kumpf  1.152 #include "HTTPAcceptor.h"
  44 mike   1.2   #include "Monitor.h"
  45              #include "HTTPMessage.h"
  46 kumpf  1.3   #include "Tracer.h"
  47 mike   1.104 #include "Buffer.h"
  48 kumpf  1.105 #include "LanguageParser.h"
  49 mike   1.2   
  50 gerarda 1.44  #ifdef PEGASUS_KERBEROS_AUTHENTICATION
  51 gerarda 1.46  #include <Pegasus/Common/CIMKerberosSecurityAssociation.h>
  52 gerarda 1.44  #endif
  53 kumpf   1.63  #include <Pegasus/Common/XmlWriter.h>
  54 gerarda 1.44  
  55 mike    1.2   PEGASUS_USING_STD;
  56               
  57               PEGASUS_NAMESPACE_BEGIN
  58               
  59               ////////////////////////////////////////////////////////////////////////////////
  60               //
  61               // Local routines:
  62               //
  63               ////////////////////////////////////////////////////////////////////////////////
  64               
  65 brian.campbell 1.72  /*
  66                       * string and number constants for HTTP sending/receiving
  67                       */
  68                      
  69                      // buffer size for sending receiving
  70                      static const Uint32 httpTcpBufferSize = 8192;
  71                      
  72 david.dillard  1.93  // string constants for HTTP header. "Name" represents strings on the left
  73                      // side of headerNameTerminator and "Value" represents strings on the right
  74 brian.campbell 1.72  // side of headerNameTerminator
  75                      
  76                      #define headerNameTrailer "Trailer"
  77                      #undef CRLF
  78                      #define CRLF "\r\n"
  79                      
  80                      static const char headerNameTransferTE[] = "TE";
  81                      static const char headerNameTransferEncoding[] = "transfer-encoding";
  82                      static const char headerNameContentLength[] = "content-length";
  83                      static const char headerValueTransferEncodingChunked[] = "chunked";
  84                      static const char headerValueTransferEncodingIdentity[] = "identity";
  85                      static const char headerValueTEchunked[] = "chunked";
  86                      static const char headerValueTEtrailers[] = "trailers";
  87                      static const char headerNameError[] = "CIMError";
  88                      static const char headerNameCode[] = "CIMStatusCode";
  89                      static const char headerNameDescription[] = "CIMStatusCodeDescription";
  90                      static const char headerNameOperation[] = "CIMOperation";
  91 brian.campbell 1.85  static const char headerNameContentLanguage[] = "Content-Language";
  92 brian.campbell 1.72  
  93                      // the names comes from the HTTP specification on chunked transfer encoding
  94                      
  95                      static const char headerNameTerminator[] = ": ";
  96                      static const char headerValueSeparator[] = ", ";
  97                      static const char headerLineTerminator[] = CRLF;
  98 david.dillard  1.88  static const char chunkLineTerminator[] = CRLF;
  99                      static const char chunkTerminator[] = CRLF;
 100                      static const char chunkBodyTerminator[] = CRLF;
 101                      static const char trailerTerminator[] = CRLF;
 102                      static const char chunkExtensionTerminator[] = ";";
 103 brian.campbell 1.72  
 104                      // string sizes
 105                      
 106 kumpf          1.122 static const Uint32 headerNameContentLengthLength =
 107                          sizeof(headerNameContentLength) - 1;
 108                      static const Uint32 headerValueTransferEncodingChunkedLength =
 109                          sizeof(headerValueTransferEncodingChunked) - 1;
 110                      static const Uint32 headerNameTransferEncodingLength =
 111                          sizeof(headerNameTransferEncoding) - 1;
 112 brian.campbell 1.72  static const Uint32 headerNameTerminatorLength =sizeof(headerNameTerminator)-1;
 113                      static const Uint32 headerLineTerminatorLength =sizeof(headerLineTerminator)-1;
 114                      static const Uint32 chunkLineTerminatorLength = sizeof(chunkLineTerminator)-1;
 115                      static const Uint32 chunkTerminatorLength = sizeof(chunkTerminator)-1;
 116                      static const Uint32 chunkBodyTerminatorLength = sizeof(chunkBodyTerminator)-1;
 117                      static const Uint32 trailerTerminatorLength = sizeof(trailerTerminator)-1;
 118 kumpf          1.122 static const Uint32 chunkExtensionTerminatorLength =
 119                          sizeof(chunkExtensionTerminator) - 1;
 120 brian.campbell 1.72  
 121                      // the number of bytes it takes to place a Uint32 into a string (minus null)
 122                      static const Uint32 numberAsStringLength = 10;
 123                      
 124                      /*
 125                       * given an HTTP status code, return the description. not all codes are listed
 126                       * here. Unmapped codes result in the internal error string.
 127                       * Add any required future codes here.
 128                       */
 129                      
 130                      static const String httpDetailDelimiter = headerValueSeparator;
 131                      static const String httpStatusInternal = HTTP_STATUS_INTERNALSERVERERROR;
 132                      
 133 venkat.puvvada 1.166 static const char INTERNAL_SERVER_ERROR_CONNECTION_CLOSED_KEY[] =
 134                          "Common.HTTPConnection.INTERNAL_SERVER_ERROR_CONNECTION_CLOSED";
 135                      static const char INTERNAL_SERVER_ERROR_CONNECTION_CLOSED[] =
 136                          "Internal server error. Connection with IP address $0 closed.";
 137                      
 138 brian.campbell 1.72  /*
 139                       * throw given http code with detail, file, line
 140                       * This is shared client/server code. The caller will decide what to do
 141                       * with the thrown message
 142                       */
 143                      
 144 kumpf          1.165 static void _throwEventFailure(
 145                          const String &status,
 146                          const String &detail,
 147                          const char *file,
 148                          Uint32 line)
 149 david.dillard  1.93  {
 150 kumpf          1.98      String message = status + httpDetailDelimiter + detail;
 151 kumpf          1.163     PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL1,
 152 thilo.boehm    1.157          (const char*)message.getCString());
 153 kumpf          1.98      if (status == httpStatusInternal)
 154                              throw AssertionFailureException(file, line, message);
 155                          else throw Exception(message);
 156 brian.campbell 1.72  }
 157                      
 158                      // throw a http exception. This is used for both client and server common code.
 159                      // The macro allows is used for file, line inclusion for debugging
 160                      
 161                      #define _throwEventFailure(status, detail) \
 162 kumpf          1.165   _throwEventFailure(status, String(detail), __FILE__, __LINE__)
 163 brian.campbell 1.72  
 164 kumpf          1.113 #define _socketWriteError()                                                   \
 165                          do                                                                        \
 166                          {                                                                         \
 167 marek          1.154         PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL1,                        \
 168 kumpf          1.113             "Socket write failed with error %d; could not write response "    \
 169                                      "to client.  (Client may have timed out.)",                   \
 170 mike           1.139             getSocketError()));                                               \
 171 kumpf          1.113         throw Exception("socket write error");                                \
 172                          }                                                                         \
 173                          while (0)
 174                      
 175 david.dillard  1.93  static inline Uint32 _Min(Uint32 x, Uint32 y)
 176 mike           1.2   {
 177 david.dillard  1.93      return x < y ? x : y;
 178 mike           1.2   }
 179                      
 180 kumpf          1.42  // Used to test signal handling
 181                      void * sigabrt_generator(void * parm)
 182                      {
 183                          abort();
 184                          return 0;
 185                      }
 186                      
 187                      
 188 mike           1.2   ////////////////////////////////////////////////////////////////////////////////
 189                      //
 190                      // HTTPConnection
 191                      //
 192                      ////////////////////////////////////////////////////////////////////////////////
 193                      
 194 kavita.gupta   1.159 Uint32 HTTPConnection::_idleConnectionTimeoutSeconds = 0;
 195                      
 196                      #ifndef PEGASUS_INTEGERS_BOUNDARY_ALIGNED
 197                      Mutex HTTPConnection::_idleConnectionTimeoutSecondsMutex;
 198                      #endif
 199                      
 200                      void HTTPConnection::setIdleConnectionTimeout(
 201                          Uint32 idleConnectionTimeoutSeconds)
 202                      {
 203                      #ifndef PEGASUS_INTEGERS_BOUNDARY_ALIGNED
 204                          AutoMutex lock(_idleConnectionTimeoutSecondsMutex);
 205                      #endif
 206                          _idleConnectionTimeoutSeconds = idleConnectionTimeoutSeconds;
 207                      }
 208                      
 209                      Uint32 HTTPConnection::getIdleConnectionTimeout()
 210                      {
 211                      #ifndef PEGASUS_INTEGERS_BOUNDARY_ALIGNED
 212                          AutoMutex lock(_idleConnectionTimeoutSecondsMutex);
 213                      #endif
 214                          return _idleConnectionTimeoutSeconds;
 215 kavita.gupta   1.159 }
 216                      
 217 harsha.bm      1.167 /*
 218                          Note: This method is called in client code for reconnecting with the Server 
 219                          and can also be used in the server code to check the connection status  and 
 220                          take appropriate actions.it checks whether the connection is alive by 
 221                          attempting to read 1 byte from the socket.This method MUST not be used when 
 222                          incoming data is expected from the connection.
 223                      
 224                          Returns TRUE when there is no data and peer has closed the connection
 225                          gracefully or there is an unanticipated incoming data, returns FALSE
 226                          otherwise. Note that this method does not consider the errors returned
 227                          from read().
 228                      */
 229                      
 230                      Boolean HTTPConnection::needsReconnect()
 231                      {
 232                          char buffer;
 233                      
 234                          int n =  _socket->read(&buffer, sizeof(buffer));
 235                          
 236                          return n >= 0;
 237                      }
 238 harsha.bm      1.167 
 239 mike           1.2   HTTPConnection::HTTPConnection(
 240                          Monitor* monitor,
 241 kumpf          1.137     SharedPtr<MP_Socket>& socket,
 242 kumpf          1.120     const String& ipAddress,
 243 kumpf          1.152     HTTPAcceptor* owningAcceptor,
 244 sushma.fernandes 1.119     MessageQueue* outputMessageQueue)
 245 david.dillard    1.93      :
 246 kumpf            1.98      Base(PEGASUS_QUEUENAME_HTTPCONNECTION),
 247                            _monitor(monitor),
 248                            _socket(socket),
 249 kumpf            1.120     _ipAddress(ipAddress),
 250 kumpf            1.152     _owningAcceptor(owningAcceptor),
 251 kumpf            1.98      _outputMessageQueue(outputMessageQueue),
 252                            _contentOffset(-1),
 253                            _contentLength(-1),
 254 kumpf            1.102     _connectionClosePending(false),
 255 dave.sudlik      1.144     _acceptPending(false),
 256 venkat.puvvada   1.166     _firstRead(true),
 257                            _internalError(false)
 258 kumpf            1.98  {
 259                            PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::HTTPConnection");
 260                        
 261                            _authInfo.reset(new AuthenticationInfo(true));
 262 kumpf            1.7   
 263 r.kieninger      1.158 #ifndef PEGASUS_OS_ZOS
 264 kumpf            1.98      // Add SSL verification information to the authentication information
 265                            if (_socket->isSecure())
 266                            {
 267 kumpf            1.122         if (_socket->isPeerVerificationEnabled() &&
 268                                    _socket->isCertificateVerified())
 269 kumpf            1.98          {
 270 sushma.fernandes 1.132             _authInfo->setConnectionAuthenticated(true);
 271 kumpf            1.98              _authInfo->setAuthType(AuthenticationInfoRep::AUTH_TYPE_SSL);
 272 kumpf            1.122             _authInfo->setClientCertificateChain(
 273                                        _socket->getPeerCertificateChain());
 274 kumpf            1.98          }
 275 thilo.boehm      1.141     }
 276 thilo.boehm      1.117 #else
 277 kumpf            1.123         if (_socket->isClientAuthenticated())
 278 thilo.boehm      1.117         {
 279 thilo.boehm      1.141             _authInfo->setAuthType(_socket->getAuthType());
 280 sushma.fernandes 1.132             _authInfo->setConnectionAuthenticated(true);
 281 thilo.boehm      1.141             _authInfo->setConnectionUser(_socket->getAuthenticatedUser());
 282 thilo.boehm      1.117         }
 283                        #endif
 284                        
 285 kumpf            1.98      _responsePending = false;
 286                            _connectionRequestCount = 0;
 287                            _transferEncodingChunkOffset = 0;
 288                        
 289 thilo.boehm      1.157     PEG_TRACE((TRC_HTTP, Tracer::LEVEL3,
 290                                "Connection IP address = %s",(const char*)_ipAddress.getCString()));
 291 kumpf            1.120 
 292 sushma.fernandes 1.121     _authInfo->setIpAddress(_ipAddress);
 293                        
 294 kumpf            1.98      PEG_METHOD_EXIT();
 295 mike             1.2   }
 296                        
 297                        HTTPConnection::~HTTPConnection()
 298                        {
 299 kumpf            1.98      PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::~HTTPConnection");
 300 kumpf            1.7   
 301 dmitry.mikulin   1.145     // We need to acquire this mutex in order to give handleEnqueue()
 302                            // a chance to finish processing. If we don't, we may run into a
 303                            // situation where the connection is marked to be closed by the
 304 kumpf            1.163     // idle connection timeout mechanism and there are no pending
 305                            // responses (the _responsePending flag is cleared in
 306 dmitry.mikulin   1.145     // _handleWriteEvent()). This causes the monitor to clean up the
 307 kumpf            1.163     // connection. But if processing is not out of
 308                            // HTTPConnection::handleEnqueue(), we are running a risk of
 309 dmitry.mikulin   1.145     // accessing a deleted object and crashing cimserver.
 310                            AutoMutex connectionLock(_connection_mut);
 311 kumpf            1.98       _socket->close();
 312 kumpf            1.7   
 313 kumpf            1.98      PEG_METHOD_EXIT();
 314 mike             1.2   }
 315                        
 316 kumpf            1.118 void HTTPConnection::enqueue(Message *message)
 317                        {
 318                            handleEnqueue(message);
 319                        }
 320                        
 321 venkat.puvvada   1.166 void HTTPConnection::handleInternalServerError(
 322                            Uint32 respMsgIndex,
 323                            Boolean isComplete)
 324                        {
 325                            PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::handleInternalServerError");
 326                        
 327                            PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
 328                                "Internal server error. Connection queue id : %u, IP address :%s, "
 329                                    "Response Index :%u, Response is Complete :%u.",
 330                                getQueueId(),
 331                                (const char*)_ipAddress.getCString(),
 332                                respMsgIndex,
 333                                isComplete));
 334                        
 335                            _internalError = true;
 336                            Buffer buffer;
 337                            HTTPMessage message(buffer);
 338                            message.setIndex(respMsgIndex);
 339                            message.setComplete(isComplete);
 340                            AutoMutex connectionLock(_connection_mut);
 341                            _handleWriteEvent(message); 
 342 venkat.puvvada   1.166     PEG_METHOD_EXIT();
 343                        }
 344                        
 345 mday             1.5   void HTTPConnection::handleEnqueue(Message *message)
 346 mike             1.2   {
 347 kumpf            1.98      PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::handleEnqueue");
 348                        
 349 kumpf            1.123     if (! message)
 350 kumpf            1.98      {
 351                                PEG_METHOD_EXIT();
 352                                return;
 353                            }
 354                        
 355 mike             1.116     AutoMutex connectionLock(_connection_mut);
 356 kumpf            1.7   
 357 kumpf            1.98      switch (message->getType())
 358                            {
 359                                case SOCKET_MESSAGE:
 360                                {
 361 marek            1.131             PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
 362 kumpf            1.98                  "HTTPConnection::handleEnqueue - SOCKET_MESSAGE");
 363                                    SocketMessage* socketMessage = (SocketMessage*)message;
 364                                    if (socketMessage->events & SocketMessage::READ)
 365                                        _handleReadEvent();
 366                                    break;
 367                                }
 368                        
 369                                case HTTP_MESSAGE:
 370                                {
 371 marek            1.131             PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
 372 kumpf            1.98                  "HTTPConnection::handleEnqueue - HTTP_MESSAGE");
 373                        
 374 kumpf            1.165             HTTPMessage* httpMessage = dynamic_cast<HTTPMessage*>(message);
 375                                    PEGASUS_ASSERT(httpMessage);
 376                                    _handleWriteEvent(*httpMessage);
 377 kumpf            1.98              break;
 378                                }
 379                        
 380                                default:
 381                                    // ATTN: need unexpected message error!
 382                                    break;
 383 kumpf            1.122     } // switch
 384 david.dillard    1.93  
 385 kumpf            1.122     delete message;
 386 david.dillard    1.93  
 387 kumpf            1.122     PEG_METHOD_EXIT();
 388 brian.campbell   1.72  }
 389 mike             1.2   
 390 brian.campbell   1.72  /*
 391 dave.sudlik      1.144  * Used on Server side to close outstanding connections waiting for SSL
 392 kumpf            1.163  * handshake to complete if timeout expired or to close idle connections if
 393 dave.sudlik      1.144  * idleConnectionTimeout config property value has specified.
 394                         * Returns 'true' if connection is closed (or is closePending).
 395 dave.sudlik      1.148  * timeNow will be updated to current time if connection's _idleStartTime
 396                         * is greater than timeNow.
 397 dave.sudlik      1.144 */
 398                        
 399                        Boolean HTTPConnection::closeConnectionOnTimeout(struct timeval* timeNow)
 400                        {
 401                            // if SSL Handshake is not complete.
 402                            if (_acceptPending)
 403                            {
 404                                PEGASUS_ASSERT(!_isClient());
 405 sushma.fernandes 1.151         if ((timeNow->tv_sec - _acceptPendingStartTime.tv_sec >
 406                                         PEGASUS_SSL_ACCEPT_TIMEOUT_SECONDS) &&
 407                                    (timeNow->tv_sec > _acceptPendingStartTime.tv_sec))
 408 dave.sudlik      1.144         {
 409 marek            1.154             PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL4,
 410 dave.sudlik      1.144                 "HTTPConnection: close acceptPending connection for timeout");
 411 kumpf            1.163             _closeConnection();
 412 dave.sudlik      1.144             return true;  // return 'true' to indicate connection was closed
 413                                }
 414                            }
 415                            // else if connection timeout is active
 416 kavita.gupta     1.159     else if (getIdleConnectionTimeout())
 417 dave.sudlik      1.144     {
 418 kumpf            1.163         // For performance reasons timeNow is calculated only once in
 419                                // Monitor. Update timeNow if connection's _idleStartTime has
 420 kavita.gupta     1.159         // more recent time.
 421 dave.sudlik      1.148         if (timeNow->tv_sec < _idleStartTime.tv_sec)
 422                                {
 423                                    Time::gettimeofday(timeNow);
 424                                }
 425                                else if ((Uint32)(timeNow->tv_sec - _idleStartTime.tv_sec) >
 426 kavita.gupta     1.159             getIdleConnectionTimeout())
 427 dave.sudlik      1.144         {
 428 marek            1.154             PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL3,
 429 dave.sudlik      1.144                 "HTTPConnection: close idle connection for timeout "
 430 kavita.gupta     1.159                 "of %d seconds\n", getIdleConnectionTimeout()));
 431 dave.sudlik      1.144             _closeConnection();
 432                                    return true;  // return 'true' to indicate connection was closed
 433                                }
 434                            }
 435                            return false;  // connection was not closed
 436                        }
 437                        
 438                        /*
 439 david.dillard    1.93   * handle the message coming down from the above. This is shared client and
 440                         * server code. If the message is coming in chunks, then validate the chunk
 441 brian.campbell   1.72   * sequence number. If the message is being processed on the server side,
 442                         * make sure the client has requested transfer encodings and/or trailers before
 443                         * sending them. If not, the message must be queued up until a complete flag
 444                         * has arrived.
 445                         */
 446                        
 447 kumpf            1.165 Boolean HTTPConnection::_handleWriteEvent(HTTPMessage& httpMessage)
 448 brian.campbell   1.72  {
 449 kumpf            1.156     String httpStatusString;
 450 mike             1.104     Buffer& buffer = httpMessage.message;
 451 kumpf            1.165     Boolean isFirst = httpMessage.isFirst();
 452                            Boolean isLast = httpMessage.isComplete();
 453 kumpf            1.98      Sint32 totalBytesWritten = 0;
 454 kumpf            1.128     Uint32 messageLength = buffer.size();
 455 mike             1.2   
 456 kumpf            1.98      try
 457                            {
 458 chuck            1.95          // delivery behavior:
 459 kumpf            1.98          // 1 handler.processing() : header + optional body?
 460                                // 2 handler.deliver()    : 1+ fully XML encoded object(s)
 461                                // 3 handler.complete()   : deliver() + isLast = true
 462                        
 463                                Uint32 bytesRemaining = messageLength;
 464                                char *messageStart = (char *) buffer.getData();
 465                                Uint32 bytesToWrite = httpTcpBufferSize;
 466 kumpf            1.165         Uint32 messageIndex = httpMessage.getIndex();
 467 kumpf            1.98          Boolean isChunkResponse = false;
 468                                Boolean isChunkRequest = false;
 469                                Boolean isFirstException = false;
 470                        
 471                                if (_isClient() == false)
 472                                {
 473 marek            1.154             PEG_TRACE((TRC_XML_IO, Tracer::LEVEL4,
 474 kumpf            1.130                 "<!-- Response: queue id: %u -->\n%s",
 475 dave.sudlik      1.125                 getQueueId(),
 476 marek            1.131                 buffer.getData()));
 477 kumpf            1.98              if (isFirst == true)
 478                                    {
 479                                        _incomingBuffer.clear();
 480                                        // tracks the message coming from above
 481                                        _transferEncodingChunkOffset = 0;
 482                                        _mpostPrefix.clear();
 483                                        cimException = CIMException();
 484                                    }
 485                                    else
 486                                    {
 487                                        // this is coming from our own internal code, therefore it is an
 488                                        // internal error. somehow the chunks came out of order.
 489                                        if (_transferEncodingChunkOffset+1 != messageIndex)
 490 kumpf            1.122                     _throwEventFailure(
 491                                                httpStatusInternal, "chunk sequence mismatch");
 492 kumpf            1.98                  _transferEncodingChunkOffset++;
 493                                    }
 494                        
 495 venkat.puvvada   1.166             // If there is an internal error on this connection, just return
 496                                    // from here if the current message is not the last message becasue
 497                                    // this connection will be closed once all messages are received.
 498                                    if (_internalError)
 499                                    {
 500                                        if (isLast)
 501                                        {
 502                                            _responsePending = false;
 503                                            _closeConnection();
 504                                            Logger::put_l(
 505                                                Logger::ERROR_LOG,
 506                                                System::CIMSERVER,
 507                                                Logger::SEVERE,
 508                                                MessageLoaderParms(
 509                                                    INTERNAL_SERVER_ERROR_CONNECTION_CLOSED_KEY,
 510                                                    INTERNAL_SERVER_ERROR_CONNECTION_CLOSED,
 511                                                    _ipAddress));
 512                                        }
 513                                        return true;
 514                                    }
 515                        
 516 kumpf            1.98              // save the first error
 517                                    if (httpMessage.cimException.getCode() != CIM_ERR_SUCCESS)
 518                                    {
 519 kumpf            1.156                 httpStatusString = httpMessage.cimException.getMessage();
 520 kumpf            1.98                  if (cimException.getCode() == CIM_ERR_SUCCESS)
 521                                        {
 522                                            cimException = httpMessage.cimException;
 523 kumpf            1.122                     // set language to first error language (overriding
 524                                            // anything there)
 525 kumpf            1.98                      contentLanguages = cimException.getContentLanguages();
 526                                            isFirstException = true;
 527                                        }
 528                                    }
 529                                    else if (cimException.getCode() == CIM_ERR_SUCCESS)
 530                                    {
 531                                        if (isFirst == true)
 532                                            contentLanguages = httpMessage.contentLanguages;
 533                                        else if (httpMessage.contentLanguages != contentLanguages)
 534 kumpf            1.105                     contentLanguages.clear();
 535 kumpf            1.98                  else contentLanguages = httpMessage.contentLanguages;
 536                                    }
 537 kumpf            1.122             // check to see if the client requested chunking OR trailers.
 538                                    // trailers are tightly integrated with chunking, so it can also
 539                                    // be used.
 540 kumpf            1.98  
 541                                    if (isChunkRequested() == true)
 542                                    {
 543                                        isChunkRequest = true;
 544                                    }
 545                                    else
 546                                    {
 547 kumpf            1.122                 // we are not sending chunks because the client did not
 548                                        // request it
 549 kumpf            1.98  
 550 kumpf            1.122                 // save the entire FIRST error response for non-chunked error
 551                                        // responses this will be used as the error message
 552 kumpf            1.98  
 553 kumpf            1.165                 if (isFirstException)
 554 kumpf            1.98                  {
 555 kumpf            1.165                     PEGASUS_ASSERT(messageLength != 0);
 556 kumpf            1.98                      cimException = CIMException(cimException.getCode(),
 557                                                String(messageStart, messageLength));
 558                                        }
 559                        
 560                                        if (isFirst == false)
 561                                        {
 562                                            // subsequent chunks from the server, just append
 563                        
 564 kumpf            1.128                     messageLength += _incomingBuffer.size();
 565 kumpf            1.98                      _incomingBuffer.reserveCapacity(messageLength+1);
 566 mike             1.104                     _incomingBuffer.append(buffer.getData(), buffer.size());
 567 kumpf            1.98                      buffer.clear();
 568                                            // null terminate
 569                                            messageStart = (char *) _incomingBuffer.getData();
 570                                            messageStart[messageLength] = 0;
 571 kumpf            1.122                     // put back in buffer, so the httpMessage parser can work
 572                                            // below
 573 kumpf            1.98                      _incomingBuffer.swap(buffer);
 574                                        }
 575                        
 576                                        if (isLast == false)
 577                                        {
 578 kumpf            1.122                     // this tells the send loop below to do nothing until we
 579                                            // are at the last response
 580 kumpf            1.98                      bytesRemaining = 0;
 581                                        }
 582                                        else
 583                                        {
 584                                            if (cimException.getCode() != CIM_ERR_SUCCESS)
 585                                            {
 586                                                buffer.clear();
 587                                                // discard all data collected to this point
 588                                                _incomingBuffer.clear();
 589                                                String messageS = cimException.getMessage();
 590                                                CString messageC = messageS.getCString();
 591                                                messageStart = (char *) (const char *) messageC;
 592 a.dunfey         1.126                         messageLength = (Uint32)strlen(messageStart);
 593 kumpf            1.98                          buffer.reserveCapacity(messageLength+1);
 594                                                buffer.append(messageStart, messageLength);
 595                                                // null terminate
 596                                                messageStart = (char *) buffer.getData();
 597                                                messageStart[messageLength] = 0;
 598                                            }
 599                                            bytesRemaining = messageLength;
 600                                        }
 601                        
 602                                    } // if not sending chunks
 603                        
 604                                    // We now need to adjust the contentLength line.
 605 kumpf            1.122             // If chunking was requested and this is the first chunk, then
 606                                    // we need to enter this block so we can adjust the header and
 607                                    // send to the client the first set of bytes right away.
 608                                    // If chunking was NOT requested, we have to wait for the last
 609                                    // chunk of the message to get (and set) the size of the content
 610                                    // because we are going to send it the traditional (i.e
 611                                    // non-chunked) way
 612 kumpf            1.98  
 613                                    if (isChunkRequest == true && isFirst == true ||
 614                                            isChunkRequest == false && isLast == true)
 615                                    {
 616                                        // need to find the end of the header
 617                                        String startLine;
 618                                        Array<HTTPHeader> headers;
 619                                        Uint32 contentLength = 0;
 620                        
 621 kumpf            1.122                 // Note: this gets the content length from subtracting the
 622                                        // header length from the messageLength, not by parsing the
 623                                        // content length header field
 624 kumpf            1.98  
 625                                        httpMessage.parse(startLine, headers, contentLength);
 626                                        Uint32 httpStatusCode = 0;
 627                                        String httpVersion;
 628                                        String reasonPhrase;
 629 kumpf            1.122                 Boolean isValid = httpMessage.parseStatusLine(
 630                                            startLine, httpVersion, httpStatusCode, reasonPhrase);
 631 kumpf            1.98                  Uint32 headerLength = messageLength - contentLength;
 632                                        char save = messageStart[headerLength];
 633                                        messageStart[headerLength] = 0;
 634                        
 635                                        char *contentLengthStart =
 636                                            strstr(messageStart, headerNameContentLength);
 637                        
 638                                        char* contentLengthEnd = contentLengthStart ?
 639                                            strstr(contentLengthStart, headerLineTerminator) : 0;
 640                        
 641                                        messageStart[headerLength] = save;
 642                        
 643                                        // the message may or may not have the content length specified
 644                                        // depending on the type of request it is
 645                        
 646                                        if (contentLengthStart)
 647                                        {
 648                                            // the message has the content length specified.
 649 kumpf            1.122                     // If we are NOT sending a chunked response, then we need
 650                                            // to overlay the contentLength number to reflect the
 651                                            // actual byte count of the content (i.e message body).
 652                                            // If we ARE sending a chunked response, then we will
 653                                            // overlay the transferEncoding keyword name and value
 654 kumpf            1.98                      // on top of the contentLength keyword and value.
 655                        
 656                                            // Important note:
 657 kumpf            1.122                     // for performance reasons, the contentLength and/or
 658                                            // transferEncoding strings are being overlayed
 659                                            // DIRECTLY inside the message buffer WITHOUT changing
 660                                            // the actual length in bytes of the message.
 661                                            // The XmlWriter has been modified to pad out the
 662                                            // maximum number in zeros to accomodate any number.
 663                                            // The maximum contentLength name and value is identical
 664                                            // to the transferEncoding name and value and can
 665                                            // be easily interchanged. By doing this, we do not have
 666                                            // to piece together the header (and more importantly,
 667                                            // the lengthy body) all over again!
 668 kumpf            1.98                      // This is why the http line lengths are validated below
 669                        
 670                                            Uint32 transferEncodingLineLengthExpected =
 671                                                headerNameTransferEncodingLength +
 672                                                headerNameTerminatorLength +
 673                                                headerValueTransferEncodingChunkedLength;
 674                        
 675                                            Uint32 contentLengthLineLengthExpected =
 676                                                headerNameContentLengthLength +
 677                                                headerNameTerminatorLength + numberAsStringLength;
 678                        
 679                                            Uint32 contentLengthLineLengthFound =
 680 a.dunfey         1.126                         (Uint32)(contentLengthEnd - contentLengthStart);
 681 kumpf            1.98  
 682                                            if (isValid == false || ! contentLengthEnd ||
 683                                                    contentLengthLineLengthFound !=
 684                                                    transferEncodingLineLengthExpected ||
 685                                                    transferEncodingLineLengthExpected !=
 686                                                    contentLengthLineLengthExpected)
 687                                            {
 688 kumpf            1.122                         // these should match up since this is coming
 689                                                // directly from our code in XmlWriter! If not,
 690                                                // some code changes have got out of sync
 691 kumpf            1.98  
 692                                                _throwEventFailure(httpStatusInternal,
 693                                                    "content length was incorrectly formatted");
 694                                            }
 695                        
 696                                            // we will be sending a chunk response if:
 697                                            // 1. chunking has been requested AND
 698                                            // 2. contentLength has been set
 699                                            //    (meaning a non-bodyless message has come in) OR
 700                                            // 3. this is not the last message
 701 kumpf            1.122                     //    (meaning the data is coming in pieces and we should
 702                                            //    send chunked)
 703 kumpf            1.98  
 704 kumpf            1.122                     if (isChunkRequest == true &&
 705                                                (contentLength > 0 || isLast == false))
 706                                            {
 707 kumpf            1.98                          isChunkResponse = true;
 708 kumpf            1.122                     }
 709 kumpf            1.98  
 710                                            save = contentLengthStart[contentLengthLineLengthExpected];
 711                                            contentLengthStart[contentLengthLineLengthExpected] = 0;
 712                        
 713                                            // overlay the contentLength value
 714                                            if (isChunkResponse == false)
 715                                            {
 716 kumpf            1.122                         // overwrite the content length number with the actual
 717                                                // byte count
 718 kumpf            1.98                          char *contentLengthNumberStart = contentLengthStart +
 719 kumpf            1.122                             headerNameContentLengthLength +
 720                                                    headerNameTerminatorLength;
 721 kumpf            1.98                          char format[6];
 722                                                sprintf (format, "%%.%uu", numberAsStringLength);
 723 kumpf            1.122                         // overwrite the bytes in buffer with the content
 724                                                //encoding length
 725                                                sprintf(contentLengthNumberStart,
 726                                                    format, contentLength);
 727                                                contentLengthStart[contentLengthLineLengthExpected] =
 728                                                    save;
 729 kumpf            1.98                      }
 730                                            else
 731                                            {
 732                                                // overlay the contentLength name and value with the
 733                                                // transferEncoding name and value
 734                        
 735 kumpf            1.122                         sprintf(contentLengthStart, "%s%s%s",
 736                                                    headerNameTransferEncoding,
 737                                                    headerNameTerminator,
 738                                                    headerValueTransferEncodingChunked);
 739 kumpf            1.98                          bytesToWrite = messageLength - contentLength;
 740                        
 741 kumpf            1.122                         contentLengthStart[contentLengthLineLengthExpected] =
 742                                                    save;
 743 kumpf            1.98                          // look for 2-digit prefix (if mpost was use)
 744 kumpf            1.164                         HTTPMessage::lookupHeaderPrefix(
 745                                                    headers,
 746                                                    headerNameOperation,
 747 kumpf            1.98                              _mpostPrefix);
 748                                            } // else chunk response is true
 749                        
 750                                        } // if content length was found
 751                        
 752                                        if (isChunkRequest == false)
 753                                        {
 754                                            if (isLast == true)
 755                                            {
 756 kumpf            1.105                         if (contentLanguages.size() != 0)
 757 kumpf            1.98                          {
 758 kumpf            1.122                             // we must insert the content-language into the
 759                                                    // header
 760 mike             1.104                             Buffer contentLanguagesString;
 761 kumpf            1.98  
 762 kumpf            1.122                             // this is the keyword:value(s) + header line
 763                                                    // terminator
 764                                                    contentLanguagesString <<
 765                                                        headerNameContentLanguage <<
 766 kumpf            1.98                                  headerNameTerminator <<
 767 kumpf            1.105                                 LanguageParser::buildContentLanguageHeader(
 768                                                            contentLanguages).getCString() <<
 769 kumpf            1.98                                  headerLineTerminator;
 770                        
 771 kumpf            1.122                             Uint32 insertOffset =
 772                                                        headerLength - headerLineTerminatorLength;
 773                                                    messageLength =
 774 kumpf            1.128                                 contentLanguagesString.size() + buffer.size();
 775 kumpf            1.98                              buffer.reserveCapacity(messageLength+1);
 776 kumpf            1.128                             messageLength = contentLanguagesString.size();
 777 kumpf            1.122                             messageStart =
 778                                                        (char *)contentLanguagesString.getData();
 779                                                    // insert the content language line before end
 780                                                    // of header
 781 kumpf            1.98                              // note: this can be expensive on large payloads
 782 kumpf            1.122                             buffer.insert(
 783                                                        insertOffset, messageStart, messageLength);
 784 kumpf            1.128                             messageLength = buffer.size();
 785 kumpf            1.98                              // null terminate
 786                                                    messageStart = (char *) buffer.getData();
 787                                                    messageStart[messageLength] = 0;
 788                                                    bytesRemaining = messageLength;
 789                                                } // if there were any content languages
 790 chuck            1.95  
 791                        #ifdef PEGASUS_KERBEROS_AUTHENTICATION
 792 kumpf            1.122                         // The following is processing to wrap (encrypt) the
 793                                                // response from the server when using kerberos
 794                                                // authentications.
 795                                                // If the security association does not exist then
 796                                                // kerberos authentication is not being used.
 797                                                CIMKerberosSecurityAssociation *sa =
 798                                                    _authInfo->getSecurityAssociation();
 799 chuck            1.95  
 800 chuck            1.96                          if (sa)
 801                                                {
 802 kumpf            1.122                             // The message needs to be parsed in order to
 803                                                    // distinguish between the headers and content.
 804                                                    // When parsing, the code breaks out of the loop
 805                                                    // as soon as it finds the double separator that
 806                                                    // terminates the headers so the headers and
 807                                                    // content can be easily separated.
 808 chuck            1.95  
 809 chuck            1.96                              Boolean authrecExists = false;
 810 kumpf            1.164                             const char* authorization;
 811 kumpf            1.122                             if (HTTPMessage::lookupHeader(
 812                                                            headers, "WWW-Authenticate",
 813                                                            authorization, false))
 814 chuck            1.96                              {
 815                                                        authrecExists = true;
 816 kumpf            1.98                              }
 817                        
 818 kumpf            1.122                             // The following is processing to wrap (encrypt)
 819                                                    // the response from the server when using
 820                                                    // kerberos authentications.
 821                                                    sa->wrapResponseMessage(
 822                                                        buffer, contentLength, authrecExists);
 823 chuck            1.96                              messageLength = buffer.size();
 824                        
 825                                                    // null terminate
 826                                                    messageStart = (char *) buffer.getData();
 827                                                    messageStart[messageLength] = 0;
 828                                                    bytesRemaining = messageLength;
 829                                                }  // endif kerberos security assoc exists
 830 chuck            1.95  #endif
 831 kumpf            1.98                      } // if this is the last chunk
 832                                            else bytesRemaining = 0;
 833                                        } // if chunk request is false
 834                                    } // if this is the first chunk containing the header
 835                                    else
 836                                    {
 837                                        // if chunking was requested, then subsequent messages that are
 838                                        // received need to turn response chunking on
 839                        
 840                                        if (isChunkRequest == true && messageIndex > 0)
 841                                        {
 842                                            isChunkResponse = true;
 843                                            bytesToWrite = messageLength;
 844                                        }
 845                                    }
 846                        
 847                                    // the data is sitting in buffer, but we want to cache it in
 848                                    // _incomingBuffer because there may be more chunks to append
 849                                    if (isChunkRequest == false)
 850                                        _incomingBuffer.swap(buffer);
 851                        
 852 kumpf            1.98          } // if not a client
 853                        
 854 kumpf            1.163         PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
 855 thilo.boehm      1.129                 "HTTPConnection::_handleWriteEvent: Server write event.");
 856                        
 857 kumpf            1.98          SignalHandler::ignore(PEGASUS_SIGPIPE);
 858                        
 859 kumpf            1.162         const char *sendStart = messageStart;
 860 kumpf            1.98          Sint32 bytesWritten = 0;
 861                        
 862                                if (isFirst == true && isChunkResponse == true && bytesToWrite > 0)
 863                                {
 864                                    // send the header first for chunked reponses.
 865                        
 866                                    // dont include header terminator yet
 867                                    Uint32 headerLength = bytesToWrite;
 868                                    bytesToWrite -= headerLineTerminatorLength;
 869 kumpf            1.165             PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
 870                                        "HTTPConnection::_handleWriteEvent: "
 871                                            "Sending header for chunked reponses.");
 872 kumpf            1.98  
 873                                    bytesWritten = _socket->write(sendStart, bytesToWrite);
 874                                    if (bytesWritten < 0)
 875 kumpf            1.113                 _socketWriteError();
 876 kumpf            1.98              totalBytesWritten += bytesWritten;
 877                                    bytesRemaining -= bytesWritten;
 878                        
 879                                    // put in trailer header.
 880 mike             1.104             Buffer trailer;
 881 kumpf            1.98              trailer << headerNameTrailer << headerNameTerminator <<
 882                                        _mpostPrefix << headerNameCode <<    headerValueSeparator <<
 883                                        _mpostPrefix << headerNameDescription << headerValueSeparator <<
 884                                        headerNameContentLanguage << headerLineTerminator;
 885 kumpf            1.162             sendStart = trailer.getData();
 886 kumpf            1.128             bytesToWrite = trailer.size();
 887 thilo.boehm      1.129 
 888 kumpf            1.165             PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
 889                                        "HTTPConnection::_handleWriteEvent: "
 890 thilo.boehm      1.129                     "Sending trailer header for chunked responses.");
 891                        
 892 kumpf            1.98              bytesWritten = _socket->write(sendStart, bytesToWrite);
 893                        
 894                                    if (bytesWritten < 0)
 895 kumpf            1.113                 _socketWriteError();
 896 kumpf            1.98              totalBytesWritten += bytesWritten;
 897                                    // the trailer is outside the header buffer, so dont include in
 898                                    // tracking variables
 899                        
 900                                    // now send header terminator
 901                                    bytesToWrite = headerLineTerminatorLength;
 902                                    sendStart = messageStart + headerLength - bytesToWrite;
 903 thilo.boehm      1.129 
 904 kumpf            1.165             PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
 905                                        "HTTPConnection::_handleWriteEvent: "
 906 thilo.boehm      1.129                     "Sending header terminator for chunked responses.");
 907                        
 908 kumpf            1.98              bytesWritten = _socket->write(sendStart, bytesToWrite);
 909                                    if (bytesWritten < 0)
 910 kumpf            1.113                 _socketWriteError();
 911 kumpf            1.98              totalBytesWritten += bytesWritten;
 912                                    bytesRemaining -= bytesWritten;
 913                        
 914                                    messageStart += headerLength;
 915                                    messageLength -= headerLength;
 916                                    sendStart = messageStart;
 917                                    bytesWritten = 0;
 918                                    bytesToWrite = bytesRemaining;
 919                                } // if first chunk of chunked response
 920                        
 921                                // room enough for hex string representing chunk length and terminator
 922                                char chunkLine[sizeof(Uint32)*2 + chunkLineTerminatorLength+1];
 923                        
 924                                for (; bytesRemaining > 0; )
 925                                {
 926                                    if (isChunkResponse == true)
 927                                    {
 928 kumpf            1.122                 // send chunk line containing hex string and chunk line
 929                                        // terminator
 930 kumpf            1.98                  sprintf(chunkLine, "%x%s", bytesToWrite, chunkLineTerminator);
 931                                        sendStart = chunkLine;
 932 a.dunfey         1.126                 Sint32 chunkBytesToWrite = (Sint32)strlen(sendStart);
 933 thilo.boehm      1.129 
 934 kumpf            1.165                 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
 935                                            "HTTPConnection::_handleWriteEvent: "
 936 thilo.boehm      1.129                         "Sending chunk with chunk line terminator.");
 937                        
 938 kumpf            1.98                  bytesWritten = _socket->write(sendStart, chunkBytesToWrite);
 939                                        if (bytesWritten < 0)
 940 kumpf            1.113                     _socketWriteError();
 941 kumpf            1.98                  totalBytesWritten += bytesWritten;
 942                                    }
 943                        
 944                                    // for chunking, we will send the entire chunk data in one send, but
 945                                    // for non-chunking, we will send incrementally
 946                                    else bytesToWrite = _Min(bytesRemaining, bytesToWrite);
 947                        
 948                                    // send non-chunked data
 949 kumpf            1.122             //
 950 gs.keenan        1.114             // Socket writes larger than 64K cause some platforms to return
 951                                    //  errors. When the socket write can't send the full buffer at
 952                                    //  one time, subtract the bytes sent and loop until the whole
 953                                    //  buffer has gone.  Use httpTcpBufferSize for maximum send size.
 954 kumpf            1.122             //
 955 gs.keenan        1.114             for (; bytesRemaining > 0; )
 956                                    {
 957                                      sendStart = messageStart + messageLength - bytesRemaining;
 958                                      bytesToWrite = _Min(httpTcpBufferSize, bytesRemaining);
 959 thilo.boehm      1.129 
 960 kumpf            1.165               PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
 961                                          "HTTPConnection::_handleWriteEvent: "
 962 thilo.boehm      1.129                       "Sending non-chunked data.");
 963                        
 964 gs.keenan        1.114               bytesWritten = _socket->write(sendStart, bytesToWrite);
 965                                      if (bytesWritten < 0)
 966                                          _socketWriteError();
 967                                      totalBytesWritten += bytesWritten;
 968                                      bytesRemaining -= bytesWritten;
 969                                    }
 970 kumpf            1.98  
 971                                    if (isChunkResponse == true)
 972                                    {
 973 kumpf            1.122                 // send chunk terminator, on the last chunk, it is the chunk
 974                                        // body terminator
 975 mike             1.104                 Buffer trailer;
 976 dave.sudlik      1.125                 Boolean traceTrailer = false;
 977 kumpf            1.98                  trailer << chunkLineTerminator;
 978                        
 979 kumpf            1.122                 // on the last chunk, attach the last chunk termination
 980                                        // sequence: 0 + last chunk terminator + optional trailer +
 981                                        // chunkBodyTerminator
 982 kumpf            1.98  
 983                                        if (isLast == true)
 984                                        {
 985                                            if (bytesRemaining > 0)
 986                                                _throwEventFailure(httpStatusInternal,
 987                                                    "more bytes after indicated last chunk");
 988                                            trailer << "0" << chunkLineTerminator;
 989                                            Uint32 httpStatus = cimException.getCode();
 990                        
 991                                            if (httpStatus != 0)
 992                                            {
 993                                                char httpStatusP[11];
 994                                                sprintf(httpStatusP, "%u",httpStatus);
 995                        
 996 dave.sudlik      1.125                         traceTrailer = true;
 997 kumpf            1.122                         trailer << _mpostPrefix << headerNameCode <<
 998                                                    headerNameTerminator << httpStatusP <<
 999                                                    headerLineTerminator;
1000                                                const String& httpDescription =
1001                                                    cimException.getMessage();
1002 kumpf            1.98                          if (httpDescription.size() != 0)
1003                                                    trailer << _mpostPrefix << headerNameDescription <<
1004 kumpf            1.122                                 headerNameTerminator << httpDescription <<
1005                                                        headerLineTerminator;
1006 kumpf            1.98                      }
1007                        
1008                                            // Add Content-Language to the trailer if requested
1009 kumpf            1.105                     if (contentLanguages.size() != 0)
1010 kumpf            1.98                      {
1011 dave.sudlik      1.125                         traceTrailer = true;
1012 kumpf            1.98                          trailer << _mpostPrefix
1013                                                    << headerNameContentLanguage << headerNameTerminator
1014 kumpf            1.105                             << LanguageParser::buildContentLanguageHeader(
1015                                                           contentLanguages)
1016 kumpf            1.98                              << headerLineTerminator;
1017                                            }
1018                        
1019                                            // now add chunkBodyTerminator
1020                                            trailer << chunkBodyTerminator;
1021                                        } // if isLast
1022                        
1023 dave.sudlik      1.125                 if (traceTrailer)
1024                                        {
1025 marek            1.154                     PEG_TRACE((TRC_XML_IO, Tracer::LEVEL4,
1026 kumpf            1.130                         "<!-- Trailer: queue id: %u -->\n%s",
1027 dave.sudlik      1.125                         getQueueId(),
1028 marek            1.131                         trailer.getData()));
1029 dave.sudlik      1.125                 }
1030 kumpf            1.162                 sendStart = trailer.getData();
1031 kumpf            1.98                  Sint32 chunkBytesToWrite = (Sint32) trailer.size();
1032 thilo.boehm      1.129 
1033 kumpf            1.165                 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
1034                                            "HTTPConnection::_handleWriteEvent: "
1035                                                "Sending the last chunk with chunk body terminator");
1036 thilo.boehm      1.129 
1037 kumpf            1.98                  bytesWritten = _socket->write(sendStart, chunkBytesToWrite);
1038                                        if (bytesWritten < 0)
1039 kumpf            1.113                     _socketWriteError();
1040 kumpf            1.98                  totalBytesWritten += bytesWritten;
1041                                    } // isChunkResponse == true
1042                        
1043                                } // for all bytes in message
1044                        
1045                            } // try
1046                        
1047                            catch (Exception &e)
1048                            {
1049 kumpf            1.156         httpStatusString = e.getMessage();
1050 venkat.puvvada   1.166         _internalError = true;
1051                            }
1052                            catch (PEGASUS_STD(bad_alloc)&)
1053                            {
1054                                httpStatusString = "Out of memory";
1055                                _internalError = true;
1056 kumpf            1.98      }
1057                            catch (...)
1058                            {
1059 venkat.puvvada   1.166         httpStatusString = "Unknown error";
1060                                _internalError = true;
1061                            }
1062                        
1063                            if (httpStatusString.size())
1064                            {
1065                                PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
1066                                    "Internal error: %s, connection queue id: %u",
1067                                    (const char*)httpStatusString.getCString(),
1068                                    getQueueId()));
1069 kumpf            1.98      }
1070                        
1071                            if (isLast == true)
1072                            {
1073                                _incomingBuffer.clear();
1074                                _transferEncodingTEValues.clear();
1075                        
1076 a.dunfey         1.127         // Reset the transfer encoding chunk offset. If it is not reset here,
1077                                // then a request sent with chunked encoding may not be properly read
1078                                // off of a connection in which a chunked response has been sent.
1079                                _transferEncodingChunkOffset = 0;
1080                        
1081 kumpf            1.98          //
1082                                // decrement request count
1083                                //
1084                        
1085 kumpf            1.133         _responsePending = false;
1086 kumpf            1.98  
1087 kumpf            1.156         if (httpStatusString.size() == 0)
1088 kumpf            1.98          {
1089 kumpf            1.147             PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
1090                                        "A response has been sent (%d of %u bytes have been written). "
1091                                            "A total of %u requests have been processed on this "
1092                                            "connection.",
1093                                        totalBytesWritten,
1094                                        messageLength,
1095                                        _connectionRequestCount));
1096 kumpf            1.98          }
1097                        
1098                                //
1099                                // Since we are done writing, update the status of entry to IDLE
1100                                // and notify the Monitor.
1101                                //
1102                                if (_isClient() == false)
1103                                {
1104 venkat.puvvada   1.166             if (_internalError)
1105                                    {
1106                                        _closeConnection();
1107                                        Logger::put_l(
1108                                            Logger::ERROR_LOG,
1109                                            System::CIMSERVER,
1110                                            Logger::SEVERE,
1111                                            MessageLoaderParms(
1112                                                INTERNAL_SERVER_ERROR_CONNECTION_CLOSED_KEY,
1113                                                INTERNAL_SERVER_ERROR_CONNECTION_CLOSED,
1114                                                _ipAddress));
1115                                    }
1116 j.alex           1.99              // Check for message to close
1117 venkat.puvvada   1.166             else if (httpMessage.getCloseConnect())
1118 j.alex           1.99              {
1119 kumpf            1.165                 PEG_TRACE((TRC_HTTP, Tracer::LEVEL3,
1120                                            "HTTPConnection::_handleWriteEvent: \"Connection: Close\" "
1121 kumpf            1.135                         "in client message."));
1122                                        _closeConnection();
1123 kumpf            1.122             }
1124                                    else
1125                                    {
1126 dave.sudlik      1.148                 // Update connection idle time.
1127 kavita.gupta     1.159                 if (getIdleConnectionTimeout())
1128 dave.sudlik      1.148                 {
1129                                            Time::gettimeofday(&_idleStartTime);
1130                                        }
1131 marek            1.154                 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
1132 kumpf            1.143                     "Now setting state to %d", MonitorEntry::STATUS_IDLE));
1133                                        _monitor->setState(_entry_index, MonitorEntry::STATUS_IDLE);
1134 j.alex           1.99                  _monitor->tickle();
1135                                    }
1136 kumpf            1.98              cimException = CIMException();
1137                                }
1138                            }
1139 sushma.fernandes 1.78  
1140 kumpf            1.156     return httpStatusString.size() == 0 ? false : true;
1141 mday             1.5   
1142                        }
1143                        
1144                        void HTTPConnection::handleEnqueue()
1145                        {
1146                           Message* message = dequeue();
1147                        
1148                            if (!message)
1149                                return;
1150                            handleEnqueue(message);
1151 mike             1.2   }
1152                        
1153                        Boolean _IsBodylessMessage(const char* line)
1154                        {
1155                            //ATTN: Make sure this is the right place to check for HTTP/1.1 and
1156                            //      HTTP/1.0 that is part of the authentication challenge header.
1157 kumpf            1.9       // ATTN-RK-P2-20020305: How do we make sure we have the complete list?
1158 h.sterling       1.75  
1159                            // List of request methods which do not have message bodies
1160                            const char* METHOD_NAMES[] =
1161                            {
1162                                "GET",
1163                                "HEAD"
1164                            };
1165                        
1166 kumpf            1.122     // List of response codes which the client accepts and which should not
1167                            // (normally) have message bodies.  The RFC is vague regarding which
1168                            // response codes support or require bodies.  These are being reported by
1169                            // class (4xx, 5xx, etc) because the CIM client should be able to handle
1170                            // any status code, including those not explicitly defined in RFC 2616.
1171                            // Therefore, listing codes individually will not work because the client
1172                            // socket will hang on a code not in this list if no content length is
1173                            // specified.
1174 h.sterling       1.75      // See bugzilla 1586
1175                            const char* RESPONSE_CODES[] =
1176                            {
1177 kumpf            1.98          "HTTP/1.1 3XX",
1178                                "HTTP/1.0 3XX",
1179 h.sterling       1.75          "HTTP/1.1 4XX",
1180                                "HTTP/1.0 4XX",
1181                                "HTTP/1.1 5XX",
1182                                "HTTP/1.0 5XX"
1183                            };
1184                        
1185 david.dillard    1.93      // Check for bodyless HTTP request method
1186 h.sterling       1.75      const Uint32 METHOD_NAMES_SIZE = sizeof(METHOD_NAMES) / sizeof(char*);
1187                        
1188                            for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++)
1189                            {
1190 a.dunfey         1.126         Uint32 n = (Uint32)strlen(METHOD_NAMES[i]);
1191 h.sterling       1.75  
1192 kumpf            1.98          if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n]))
1193                                    return true;
1194 h.sterling       1.75      }
1195                        
1196                            // Check for bodyless HTTP status code
1197                            const Uint32 RESPONSE_CODES_SIZE = sizeof(RESPONSE_CODES) / sizeof(char*);
1198                        
1199                            for (Uint32 i = 0; i < RESPONSE_CODES_SIZE; i++)
1200                            {
1201 a.dunfey         1.126         Uint32 n = (Uint32)strlen(RESPONSE_CODES[i]);
1202 h.sterling       1.75  
1203 kumpf            1.98          if (strncmp(line, RESPONSE_CODES[i], n - 2) == 0 && isspace(line[n]))
1204 h.sterling       1.75                  return true;
1205                                    }
1206                        
1207                            return false;
1208                        }
1209                        
1210                        /*
1211                        Boolean _IsBodylessMessage(const char* line)
1212                        {
1213                            //ATTN: Make sure this is the right place to check for HTTP/1.1 and
1214                            //      HTTP/1.0 that is part of the authentication challenge header.
1215                            // ATTN-RK-P2-20020305: How do we make sure we have the complete list?
1216 mike             1.2       const char* METHOD_NAMES[] =
1217                            {
1218                                "GET",
1219 kumpf            1.9           "HTTP/1.1 400",
1220                                "HTTP/1.0 400",
1221 mike             1.2           "HTTP/1.1 401",
1222 kumpf            1.10          "HTTP/1.0 401",
1223 kumpf            1.63          "HTTP/1.1 413",
1224                                "HTTP/1.0 413",
1225 kumpf            1.60          "HTTP/1.1 500",
1226                                "HTTP/1.0 500",
1227 kumpf            1.10          "HTTP/1.1 501",
1228 kumpf            1.60          "HTTP/1.0 501",
1229                                "HTTP/1.1 503",
1230                                "HTTP/1.0 503"
1231 mike             1.2       };
1232                        
1233                            const Uint32 METHOD_NAMES_SIZE = sizeof(METHOD_NAMES) / sizeof(char*);
1234                        
1235                            for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++)
1236                            {
1237 kumpf            1.98          Uint32 n = strlen(METHOD_NAMES[i]);
1238 mike             1.2   
1239 kumpf            1.98          if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n]))
1240                                    return true;
1241 mike             1.2       }
1242                        
1243                            return false;
1244 h.sterling       1.75  }*/
1245 mike             1.2   
1246 david.dillard    1.93  void HTTPConnection::_getContentLengthAndContentOffset()
1247 mike             1.2   {
1248 kumpf            1.128     Uint32 size = _incomingBuffer.size();
1249 kumpf            1.98      if (size == 0)
1250                                return;
1251 mike             1.2       char* data = (char*)_incomingBuffer.getData();
1252                            char* line = (char*)data;
1253                            char* sep;
1254                            Uint32 lineNum = 0;
1255                            Boolean bodylessMessage = false;
1256 kumpf            1.109     Boolean gotContentLength = false;
1257                            Boolean gotTransferEncoding = false;
1258                            Boolean gotContentLanguage = false;
1259                            Boolean gotTransferTE = false;
1260 mike             1.2   
1261 kumpf            1.163     while ((sep =
1262 sushma.fernandes 1.138         HTTPMessage::findSeparator(line, (Uint32)(size - (line - data)))))
1263 mike             1.2       {
1264 kumpf            1.98          char save = *sep;
1265                                *sep = '\0';
1266                        
1267                                // Did we find the double separator which terminates the headers?
1268                        
1269                                if (line == sep)
1270                                {
1271                                    *sep = save;
1272                                    line = sep + ((save == '\r') ? 2 : 1);
1273 a.dunfey         1.126             _contentOffset = (Sint32)(line - _incomingBuffer.getData());
1274 kumpf            1.98  
1275                                    // reserve space for entire non-chunked message
1276                                    if (_contentLength > 0)
1277                                    {
1278 dave.sudlik      1.110                 try
1279                                        {
1280 kumpf            1.122                     Uint32 capacity = (Uint32)(_contentLength +
1281 dave.sudlik      1.110                         _contentOffset + 1);
1282                                            _incomingBuffer.reserveCapacity(capacity);
1283                                            data = (char *)_incomingBuffer.getData();
1284                                            data[capacity-1] = 0;
1285                                        }
1286 kumpf            1.123                 catch (const PEGASUS_STD(bad_alloc)&)
1287 dave.sudlik      1.110                 {
1288                                            _throwEventFailure(HTTP_STATUS_REQUEST_TOO_LARGE,
1289                                                "Error reserving space for non-chunked message");
1290                                        }
1291 kumpf            1.123                 catch (...)
1292 dave.sudlik      1.110                 {
1293 kumpf            1.122                     _throwEventFailure(
1294                                                httpStatusInternal, "unexpected exception");
1295 dave.sudlik      1.110                 }
1296 kumpf            1.98              }
1297                        
1298                                    break;
1299                                }
1300 mike             1.2   
1301 kumpf            1.98          // If this is one of the bodyless methods, then we can assume the
1302                                // message is complete when the "\r\n\r\n" is encountered.
1303 mike             1.2   
1304 kumpf            1.98          if (lineNum == 0 && _IsBodylessMessage(line))
1305                                    bodylessMessage = true;
1306                        
1307                                // Look for the content-length if not already found:
1308                        
1309                                char* colon = strchr(line, ':');
1310                        
1311                                if (colon)
1312                                {
1313                                    *colon  = '\0';
1314                        
1315                                    // remove whitespace after colon before value
1316                                    char *valueStart = colon + 1;
1317 kumpf            1.123             while (*valueStart == ' ' || *valueStart == '\t')
1318 kumpf            1.98                  valueStart++;
1319                        
1320                                    // we found some non-whitespace token
1321                                    if (valueStart != sep)
1322                                    {
1323                                        char *valueEnd = sep - 1;
1324                        
1325 kumpf            1.122                 // now remove whitespace from end of line back to last byte
1326                                        // of value
1327 kumpf            1.123                 while (*valueEnd == ' ' || *valueEnd == '\t')
1328 kumpf            1.98                      valueEnd--;
1329                        
1330                                        char valueSave = *(valueEnd+1);
1331                        
1332                                        if (System::strcasecmp(line, headerNameContentLength) == 0)
1333                                        {
1334 kumpf            1.109                     if (gotContentLength)
1335                                            {
1336                                                _throwEventFailure(HTTP_STATUS_BADREQUEST,
1337                                                    "Duplicate Content-Length header detected");
1338                                            }
1339                                            gotContentLength = true;
1340                        
1341 kumpf            1.98                      if (_transferEncodingValues.size() == 0)
1342 kumpf            1.109                     {
1343                                                // Use a dummy character conversion to catch an
1344                                                // invalid character in the value.
1345                                                char dummy;
1346                                                if (sscanf(valueStart, "%d%c",
1347                                                        &_contentLength, &dummy) != 1)
1348                                                {
1349                                                    _throwEventFailure(HTTP_STATUS_BADREQUEST,
1350                                                        "Invalid Content-Length header detected");
1351                                                }
1352                                            }
1353                                            else
1354                                            {
1355                                                _contentLength = -1;
1356                                            }
1357 kumpf            1.98                  }
1358 kumpf            1.122                 else if (System::strcasecmp(
1359                                                     line, headerNameTransferEncoding) == 0)
1360 kumpf            1.98                  {
1361 kumpf            1.109                     if (gotTransferEncoding)
1362                                            {
1363                                                _throwEventFailure(HTTP_STATUS_BADREQUEST,
1364                                                    "Duplicate Transfer-Encoding header detected");
1365                                            }
1366                                            gotTransferEncoding = true;
1367                        
1368 kumpf            1.98                      _transferEncodingValues.clear();
1369                        
1370 kumpf            1.122                     if (strcmp(valueStart,
1371                                                    headerValueTransferEncodingChunked) == 0)
1372                                                _transferEncodingValues.append(
1373                                                    headerValueTransferEncodingChunked);
1374                                            else if (strcmp(valueStart,
1375                                                         headerValueTransferEncodingIdentity) == 0)
1376 kumpf            1.98                          ; // do nothing
1377                                            else _throwEventFailure(HTTP_STATUS_NOTIMPLEMENTED,
1378 kumpf            1.122                              "unimplemented transfer-encoding value");
1379 kumpf            1.98                      _contentLength = -1;
1380                                        }
1381 kumpf            1.122                 else if (System::strcasecmp(
1382                                                     line, headerNameContentLanguage) == 0)
1383 kumpf            1.98                  {
1384 kumpf            1.122                     // note: if this is a chunked header, then this will be
1385                                            // ignored later
1386                                            String contentLanguagesString(
1387 a.dunfey         1.126                         valueStart, (Uint32)(valueEnd - valueStart + 1));
1388 kumpf            1.98                      try
1389                                            {
1390 kumpf            1.109                         ContentLanguageList contentLanguagesValue =
1391 kumpf            1.105                             LanguageParser::parseContentLanguageHeader(
1392                                                        contentLanguagesString);
1393 kumpf            1.109 
1394                                                if (gotContentLanguage)
1395                                                {
1396                                                    // Append these content languages to the existing
1397                                                    // list.
1398                                                    for (Uint32 i = 0;
1399                                                         i < contentLanguagesValue.size(); i++)
1400                                                    {
1401                                                        contentLanguages.append(
1402                                                            contentLanguagesValue.getLanguageTag(i));
1403                                                    }
1404                                                }
1405                                                else
1406                                                {
1407                                                    contentLanguages = contentLanguagesValue;
1408                                                    gotContentLanguage = true;
1409                                                }
1410 kumpf            1.98                      }
1411 kumpf            1.123                     catch (...)
1412 kumpf            1.98                      {
1413 marek            1.131                         PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
1414 kumpf            1.122                             "HTTPConnection: ERROR: contentLanguages had "
1415                                                        "parsing failure. clearing languages. error "
1416                                                        "data=%s",
1417 marek            1.131                             (const char *)contentLanguagesString.getCString()));
1418 kumpf            1.105                         contentLanguages.clear();
1419 kumpf            1.98                      }
1420                                        }
1421                                        else if (System::strcasecmp(line, headerNameTransferTE) == 0)
1422                                        {
1423 kumpf            1.109                     if (gotTransferTE)
1424                                            {
1425                                                _throwEventFailure(HTTP_STATUS_BADREQUEST,
1426                                                    "Duplicate TE header detected");
1427                                            }
1428                                            gotTransferTE = true;
1429                        
1430 kumpf            1.98                      _transferEncodingTEValues.clear();
1431                                            static const char valueDelimiter = ',';
1432                                            char *valuesStart = valueStart;
1433                        
1434                                            // now tokenize the values
1435                                            while (*valuesStart)
1436                                            {
1437                                                // strip off whitepsace from the front
1438 kumpf            1.123                         while (*valuesStart == ' ' || *valuesStart == '\t')
1439 kumpf            1.98                              valuesStart++;
1440                        
1441                                                if (valuesStart == valueEnd)
1442                                                    break;
1443                        
1444                                                char *v = strchr(valuesStart, valueDelimiter);
1445                                                if (v)
1446                                                {
1447                                                    if (v == valuesStart)
1448                                                    {
1449                                                        valuesStart++;
1450                                                        continue;
1451                                                    }
1452                                                    v--;
1453                                                    // strip off whitespace from the end
1454 kumpf            1.123                             while (*v == ' ' || *v == '\t')
1455 kumpf            1.98                                  v--;
1456                                                    v++;
1457                                                    *v = 0;
1458                                                }
1459                        
1460                                                _transferEncodingTEValues.append(valuesStart);
1461                        
1462                                                if (v)
1463                                                {
1464                                                    *v = valueDelimiter;
1465                                                    valuesStart = v+1;
1466                                                }
1467                                                else break;
1468                                            }
1469                                        }
1470                        
1471                                        *(valueEnd+1) = valueSave;
1472                                    } // if some value tokens
1473                        
1474                                    *colon = ':';
1475                                }
1476 kumpf            1.98  
1477                                *sep = save;
1478                                line = sep + ((save == '\r') ? 2 : 1);
1479                                lineNum++;
1480 mike             1.2       }
1481                        
1482                            if (_contentOffset != -1 && bodylessMessage)
1483 kumpf            1.98          _contentLength = 0;
1484 mike             1.2   }
1485                        
1486                        void HTTPConnection::_clearIncoming()
1487                        {
1488                            _contentOffset = -1;
1489                            _contentLength = -1;
1490                            _incomingBuffer.clear();
1491 kumpf            1.98      _mpostPrefix.clear();
1492                            contentLanguages.clear();
1493 mike             1.2   }
1494                        
1495                        void HTTPConnection::_closeConnection()
1496                        {
1497 kumpf            1.98      // return - don't send the close connection message.
1498                            // let the monitor dispatch function do the cleanup.
1499                            PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::_closeConnection");
1500                            _connectionClosePending = true;
1501                        
1502                            // NOTE: if there is a response pending while a close connection request
1503                            // occurs, then this indicates potential activity on this connection apart
1504                            // from this thread of execution (although this code here is locked, other
1505                            // threads may be waiting on this one.)
1506                            // The caller MUST check this value before attempting a delete of this
1507                            // connnection, otherwise the delete may occur while other chunked responses
1508                            // are waiting to be delivered through this connection.
1509                            // This condition may happen on a client error/timeout/interrupt/disconnect
1510                        
1511                            if (_isClient() == false)
1512                            {
1513                                if (_responsePending == true)
1514                                {
1515 marek            1.131             PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL2,
1516 kumpf            1.122                 "HTTPConnection::_closeConnection - Close connection "
1517                                            "requested while responses are still expected on this "
1518                                            "connection. connection=0x%p, socket=%d\n",
1519 marek            1.131                 (void*)this, getSocket()));
1520 kumpf            1.61  
1521 kumpf            1.98          }
1522                        
1523                                // still set to DYING
1524 marek            1.154         PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
1525 kumpf            1.143             "Now setting state to %d", MonitorEntry::STATUS_DYING));
1526                                _monitor->setState(_entry_index, MonitorEntry::STATUS_DYING);
1527 kumpf            1.98          _monitor->tickle();
1528                            }
1529                        
1530                            if (_connectionRequestCount == 0)
1531                            {
1532 marek            1.131         PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
1533 kumpf            1.122             "HTTPConnection::_closeConnection - Connection being closed "
1534                                        "without receiving any requests.");
1535 kumpf            1.98      }
1536                        
1537                            PEG_METHOD_EXIT();
1538 mike             1.2   }
1539                        
1540 brian.campbell   1.81  Boolean HTTPConnection::isChunkRequested()
1541                        {
1542 kumpf            1.98      Boolean answer = false;
1543                            if (_transferEncodingTEValues.size() > 0 &&
1544                                (Contains(_transferEncodingTEValues, String(headerValueTEchunked)) ||
1545                                 Contains(_transferEncodingTEValues, String(headerValueTEtrailers))))
1546                                answer = true;
1547 chuck            1.95  
1548                        #ifdef PEGASUS_KERBEROS_AUTHENTICATION
1549 kumpf            1.98      CIMKerberosSecurityAssociation *sa = _authInfo->getSecurityAssociation();
1550 chuck            1.95  
1551 kumpf            1.98      if (sa)
1552                            {
1553                                answer = false;
1554                            }
1555 chuck            1.95  #endif
1556                        
1557 kumpf            1.98      return answer;
1558 brian.campbell   1.81  }
1559                        
1560 marek            1.115 void HTTPConnection::setSocketWriteTimeout(Uint32 socketWriteTimeout)
1561                        {
1562                                _socket->setSocketWriteTimeout(socketWriteTimeout);
1563                        }
1564                        
1565                        
1566 brian.campbell   1.72  // determine if the current code being executed is on the client side
1567                        
1568                        Boolean HTTPConnection::_isClient()
1569                        {
1570 kumpf            1.152     return (_owningAcceptor == 0);
1571 brian.campbell   1.72  }
1572                        
1573                        /*
1574                         * determine if the data read in should be treated as transfer encoded data.
1575                         * If so, proceed to strip out the transfer encoded meta data within the body
1576 david.dillard    1.93   * but the headers relating to transfer encoding will remain unchanged.
1577                         * One should refer to the transfer encoding section of the HTTP protocol
1578 brian.campbell   1.72   * specification to understand the parsing semantics below.
1579                         * NOTE: this function is coded as a syncronous read! The entire message will
1580                         * be read in before the message leaves this class and is passed up to the
1581                         * client application.
1582                         */
1583                        
1584 david.dillard    1.94  void HTTPConnection::_handleReadEventTransferEncoding()
1585 brian.campbell   1.72  {
1586 kumpf            1.165     PEG_METHOD_ENTER(TRC_HTTP,
1587                                "HTTPConnection::_handleReadEventTransferEncoding");
1588 kumpf            1.122 
1589 kumpf            1.128     Uint32 messageLength = _incomingBuffer.size();
1590 kumpf            1.98      Uint32 headerLength = (Uint32) _contentOffset;
1591                        
1592                            // return immediately under these conditions:
1593                        
1594                            // - Header terminator has not been reached yet (_contentOffset < 0)
1595                            // - This is a non-transfer encoded message because the content length
1596                            //   has been set from the given header value (_contentLength > 0)
1597                            //   (_contentLength == 0 means bodyless, so return too - section 4.3)
1598                            // - The message read in so far is <= to the header length
1599                            // - No transfer encoding has been declared in the header.
1600                        
1601                            if (_contentOffset < 0 || _contentLength >= 0 ||
1602                                messageLength <= headerLength || _transferEncodingValues.size() == 0)
1603                            {
1604                                PEG_METHOD_EXIT();
1605                                return;
1606                            }
1607                        
1608                            // on the first chunk in the message, set the encoding offset to the content
1609                            // offset
1610                        
1611 kumpf            1.98      if (_transferEncodingChunkOffset == 0)
1612                                _transferEncodingChunkOffset = (Uint32) _contentOffset;
1613 brian.campbell   1.72  
1614 kumpf            1.98      char *headerStart = (char *) _incomingBuffer.getData();
1615                            char *messageStart = headerStart;
1616 brian.campbell   1.72  
1617 kumpf            1.98      // loop thru the received data (so far) and strip out all chunked meta data.
1618                            // this logic assumes that the data read in may be only partial at any point
1619                            // during the parsing. the variable _transferEncodingChunkOffset represents
1620                            // the byte offset (from the start of the message) of the last NON completed
1621                            // chunk parsed within the message. Remember that the tcp reader has padded
1622                            // the buffer with a terminating null for easy string parsing.
1623 brian.campbell   1.72  
1624 kumpf            1.98      for (;;)
1625                            {
1626                                // we have parsed the length, but not all bytes of chunk have been read
1627                                // in yet
1628                                if (_transferEncodingChunkOffset >= messageLength)
1629                                    break;
1630                        
1631                                // this is the length from _transferEncodingChunkOffset to the end
1632                                // of the message (so far). It represents the bytes that have not been
1633                                // processed yet
1634                        
1635                                Uint32 remainderLength = messageLength - _transferEncodingChunkOffset;
1636                        
1637                                // the start of the first fully non-parsed chunk of this interation
1638                                char *chunkLineStart = messageStart + _transferEncodingChunkOffset;
1639                                char *chunkLineEnd = chunkLineStart;
1640                        
1641 kumpf            1.122         // Find the end of the hex string representing the data portion
1642                                // length of the current chunk. Note that we must hit at least one
1643                                // non-hexdigit (except null) to know we have read in the complete
1644                                // number
1645 kumpf            1.98  
1646                                while (isxdigit(*chunkLineEnd))
1647                                    chunkLineEnd++;
1648                        
1649                                if (! *chunkLineEnd)
1650                                    break;
1651                        
1652                                // This is the parsed chunk length in hex. From here on, this many bytes
1653                                // plus the chunk terminator (AFTER this chunk line is done) must be
1654                                // read in to constitute a complete chunk in which
1655                                // _transferEncodingChunkOffset can be incremented to the next chunk
1656                        
1657                                Uint32 chunkLengthParsed =
1658                                    (Uint32) strtoul((const char *)chunkLineStart, 0, 16);
1659                        
1660                                // this also covers strings stated even larger
1661                                if (chunkLengthParsed == PEG_NOT_FOUND)
1662                                    _throwEventFailure(HTTP_STATUS_REQUEST_TOO_LARGE,
1663                                        "stated chunk length too large");
1664                        
1665                                char *chunkExtensionStart = chunkLineEnd;
1666 kumpf            1.98          chunkLineEnd = strstr(chunkLineEnd, chunkLineTerminator);
1667                        
1668 kumpf            1.122         // If we have not received the chunk line terminator yet, then
1669                                // return and wait for the next iteration. This is done because the
1670                                // hex length given only represents the non-meta data, not the chunk
1671                                // line itself.
1672 kumpf            1.98  
1673                                if (!chunkLineEnd)
1674                                    break;
1675                        
1676 kumpf            1.122         // the token after the hex digit must be either the chunk line
1677                                // terminator or the chunk extension terminator. If not, the sender
1678                                // has sent an illegal chunked encoding syntax.
1679 kumpf            1.98  
1680                                if (strncmp(chunkExtensionStart, chunkExtensionTerminator,
1681                                        chunkExtensionTerminatorLength) != 0 &&
1682                                    strncmp(chunkExtensionStart, chunkLineTerminator,
1683                                        chunkLineTerminatorLength) != 0)
1684 kumpf            1.122         {
1685                                    _throwEventFailure(
1686                                        HTTP_STATUS_BADREQUEST, "missing chunk extension");
1687                                }
1688 kumpf            1.98  
1689                                chunkLineEnd += chunkLineTerminatorLength;
1690 a.dunfey         1.126         Uint32 chunkLineLength = (Uint32)(chunkLineEnd - chunkLineStart);
1691 kumpf            1.98          Uint32 chunkMetaLength = chunkLineLength;
1692                                if (chunkLengthParsed > 0)
1693                                    chunkMetaLength += chunkTerminatorLength;
1694                                Uint32 chunkTerminatorOffset = _transferEncodingChunkOffset +
1695                                    chunkLineLength + chunkLengthParsed;
1696                        
1697                                // The parsed length represents the non-meta data bytes which starts
1698                                // after the chunk line terminator has been received.
1699 kumpf            1.122         // If we dont have enough remainder bytes to process from the length
1700                                // parsed then return and wait for the next iteration.
1701 kumpf            1.98  
1702 jim.wunderlich   1.108         //
1703                                // Also, if this is the last chunk, then we have to know if there
1704 kumpf            1.122         // is enough data in here to be able to verify that meta crlf for
1705 jim.wunderlich   1.108         // the end of the whole chunked message is present.
1706 kumpf            1.122         // If chunkLengthParsed + chunkMetaLenght == reminderLength, it
1707 jim.wunderlich   1.108         // means that there is a space only for meta crlf of the last chunk.
1708 kumpf            1.122         // Therefore go back and re-read socket until you get enough data
1709                                // for at least 2 crlfs.  One for the end of the last chunk or
1710                                // the end of the optional trailer, and one for the end of whole
1711 jim.wunderlich   1.108         // message.
1712                        
1713                                if (chunkLengthParsed + chunkMetaLength >= remainderLength)
1714 kumpf            1.98              break;
1715                        
1716 kumpf            1.122         // at this point we have a complete chunk. proceed and strip out
1717                                // meta-data
1718                                // NOTE: any time "remove" is called on the buffer, many variables
1719                                // must be recomputed to reflect the data removed.
1720 brian.campbell   1.72  
1721 kumpf            1.98          // remove the chunk length line
1722                                _incomingBuffer.remove(_transferEncodingChunkOffset, chunkLineLength);
1723 kumpf            1.128         messageLength = _incomingBuffer.size();
1724 kumpf            1.98          // always keep the byte after the last data byte null for easy string
1725                                // processing.
1726 brian.campbell   1.72          messageStart[messageLength] = 0;
1727                        
1728 kumpf            1.98          // recalculate since we just removed the chunk length line
1729                                chunkTerminatorOffset -= chunkLineLength;
1730                        
1731                                // is this the last chunk ?
1732                                if (chunkLengthParsed == 0)
1733                                {
1734                                    // We are at the last chunk. The only remaining data should be:
1735                                    // 1. optional trailer first
1736 kumpf            1.122             // 2. message terminator (will remain on incoming buffer and
1737                                    //    passed up)
1738 kumpf            1.98  
1739                                    remainderLength -= chunkLineLength;
1740                        
1741                                    CIMStatusCode cimStatusCode = CIM_ERR_SUCCESS;
1742                                    Uint32 httpStatusCode = HTTP_STATUSCODE_OK;
1743                                    String httpStatus;
1744                                    String cimErrorValue;
1745                        
1746                                    // is there an optional trailer ?
1747                                    if (remainderLength > chunkBodyTerminatorLength)
1748                                    {
1749 kumpf            1.122                 Uint32 trailerLength =
1750                                            remainderLength - chunkBodyTerminatorLength;
1751 kumpf            1.98                  Uint32 trailerOffset = _transferEncodingChunkOffset;
1752                                        char *trailerStart = messageStart + trailerOffset;
1753                                        char *trailerTerminatorStart = trailerStart + trailerLength -
1754                                            trailerTerminatorLength;
1755                        
1756                                        // no trailer terminator before end of chunk body ?
1757                                        if (strncmp(trailerTerminatorStart, trailerTerminator,
1758                                                trailerTerminatorLength) != 0)
1759                                            _throwEventFailure(HTTP_STATUS_BADREQUEST,
1760                                                "No chunk trailer terminator received");
1761                        
1762 mike             1.104                 Buffer trailer;
1763 kumpf            1.98                  // add a dummy startLine so that the parser works
1764                                        trailer << " " << headerLineTerminator;
1765                        
1766                                        char save = trailerStart[trailerLength];
1767                                        trailerStart[trailerLength] = 0;
1768                                        trailer << trailerStart;
1769                                        trailerStart[trailerLength] = save;
1770                        
1771                                        _incomingBuffer.remove(trailerOffset, trailerLength);
1772 kumpf            1.128                 messageLength = _incomingBuffer.size();
1773 kumpf            1.98                  messageStart[messageLength] = 0;
1774                                        remainderLength -= trailerLength;
1775                        
1776                                        // parse the trailer looking for the code and description
1777 kumpf            1.156                 String trailerStartLine;
1778                                        Array<HTTPHeader> trailers;
1779                                        Uint32 trailerContentLength = 0;
1780 kumpf            1.98                  HTTPMessage httpTrailer(trailer);
1781 kumpf            1.156                 httpTrailer.parse(
1782                                            trailerStartLine, trailers, trailerContentLength);
1783 kumpf            1.98  
1784                                        // first look for cim error. this is an http level error
1785                                        Boolean found = false;
1786                        
1787 kumpf            1.122                 found = httpTrailer.lookupHeader(
1788 kumpf            1.164                     trailers, headerNameError, cimErrorValue, true);
1789 kumpf            1.98  
1790                                        if (found == true)
1791                                        {
1792 kumpf            1.122                     // we have a cim error. parse the header to get the
1793                                            // original http level error if any, otherwise, we have
1794                                            // to make one up.
1795 kumpf            1.98  
1796 mike             1.104                     Buffer header(messageStart, headerLength);
1797 kumpf            1.156                     String headerStartLine;
1798 kumpf            1.98                      Array<HTTPHeader> headers;
1799 kumpf            1.156                     Uint32 headerContentLength = 0;
1800 kumpf            1.98                      HTTPMessage httpHeader(header);
1801 kumpf            1.156                     httpHeader.parse(
1802                                                headerStartLine, headers, headerContentLength);
1803 kumpf            1.98                      String httpVersion;
1804 kumpf            1.122                     Boolean isValid = httpHeader.parseStatusLine(
1805 kumpf            1.156                         headerStartLine,
1806                                                httpVersion,
1807                                                httpStatusCode,
1808                                                httpStatus);
1809 kumpf            1.98                      if (isValid == false || httpStatusCode == 0 ||
1810                                                    httpStatusCode == HTTP_STATUSCODE_OK)
1811                                            {
1812                                                // ATTN: make up our own http code if not given ?
1813                                                httpStatusCode = (Uint32) HTTP_STATUSCODE_BADREQUEST;
1814                                                httpStatus = HTTP_STATUS_BADREQUEST;
1815                                            }
1816                                        }
1817                                        else
1818                                        {
1819 kumpf            1.164                     const char* codeValue;
1820 kumpf            1.122                     found = httpTrailer.lookupHeader(
1821 kumpf            1.164                         trailers, headerNameCode, codeValue, true);
1822                                            if (found && *codeValue &&
1823                                                (cimStatusCode = (CIMStatusCode)atoi(codeValue)) > 0)
1824 kumpf            1.98                      {
1825 kumpf            1.122                         HTTPMessage::lookupHeaderPrefix(
1826 kumpf            1.164                             trailers, headerNameCode, _mpostPrefix);
1827                                                httpStatus = _mpostPrefix + headerNameCode +
1828 kumpf            1.122                             headerNameTerminator + codeValue +
1829                                                    headerLineTerminator;
1830 kumpf            1.98  
1831                                                // look for cim status description
1832                                                String descriptionValue;
1833 kumpf            1.122                         found = httpTrailer.lookupHeader(
1834 kumpf            1.164                             trailers,
1835                                                    headerNameDescription,
1836                                                    descriptionValue,
1837                                                    true);
1838 kumpf            1.98                          if (descriptionValue.size() == 0)
1839 kumpf            1.122                         {
1840                                                    descriptionValue =
1841                                                        cimStatusCodeToString(cimStatusCode);
1842                                                }
1843 kumpf            1.98  
1844 kumpf            1.122                         httpStatus = httpStatus + _mpostPrefix +
1845 kumpf            1.164                             headerNameDescription + headerNameTerminator +
1846 kumpf            1.122                             descriptionValue + headerLineTerminator;
1847 kumpf            1.98  
1848                                            } // if found a cim status code
1849                        
1850                                            // Get Content-Language out of the trailer, if it is there
1851                                            String contentLanguagesString;
1852 kumpf            1.156                     found = httpTrailer.lookupHeader(trailers,
1853 kumpf            1.98                          headerNameContentLanguage,
1854                                                contentLanguagesString,
1855                                                true);
1856                        
1857 kumpf            1.105                     contentLanguages.clear();
1858 kumpf            1.98                      if (found == true && contentLanguagesString.size() > 0)
1859                                            {
1860                                                try
1861                                                {
1862 kumpf            1.105                             contentLanguages =
1863                                                        LanguageParser::parseContentLanguageHeader(
1864                                                            contentLanguagesString);
1865 kumpf            1.98                          }
1866 kumpf            1.123                         catch (...)
1867 kumpf            1.98                          {
1868 marek            1.131                             PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
1869 kumpf            1.122                                 "HTTPConnection: ERROR: contentLanguages had "
1870                                                            "parsing failure. clearing languages. "
1871                                                            "error data=%s",
1872                                                        (const char *)contentLanguagesString.
1873 marek            1.131                                     getCString()));
1874 kumpf            1.105                             contentLanguages.clear();
1875 kumpf            1.98                          }
1876                                            }
1877                        
1878                                        } // else not a cim error
1879                                    } // if optional trailer present
1880                        
1881                                    char *chunkBodyTerminatorStart =
1882                                        messageStart + _transferEncodingChunkOffset;
1883                        
1884                                    // look for chunk body terminator
1885                                    if (remainderLength != chunkBodyTerminatorLength ||
1886                                        strncmp(chunkBodyTerminatorStart, chunkBodyTerminator,
1887                                            chunkBodyTerminatorLength) != 0)
1888                                        _throwEventFailure(HTTP_STATUS_BADREQUEST,
1889                                            "No chunk body terminator received");
1890                        
1891                                    // else the remainder is just the terminator, which we will leave
1892                                    // on the incoming buffer and pass up
1893                                    // (as if a non-transfer message arrived)
1894 brian.campbell   1.72  
1895 kumpf            1.98              _transferEncodingChunkOffset = 0;
1896                                    _contentLength = messageLength - headerLength;
1897 brian.campbell   1.72  
1898 kumpf            1.98              if (httpStatusCode != HTTP_STATUSCODE_OK)
1899                                    {
1900                                        _handleReadEventFailure(httpStatus, cimErrorValue);
1901                                    }
1902                                    else if (cimStatusCode != CIM_ERR_SUCCESS)
1903                                    {
1904 kumpf            1.122                 // discard the XML payload data (body) according to cim
1905                                        // operations spec and add code and description to the
1906                                        // header so the next layer can interpret the error correctly
1907 kumpf            1.98  
1908                                        _incomingBuffer.remove(headerLength, _contentLength);
1909                                        // remove the header line terminator
1910 kumpf            1.122                 _incomingBuffer.remove(
1911                                            headerLength - headerLineTerminatorLength,
1912 kumpf            1.98                      headerLineTerminatorLength);
1913                                        // append new status
1914 kumpf            1.122                 _incomingBuffer.append(
1915                                            httpStatus.getCString(), httpStatus.size());
1916 kumpf            1.98  
1917 kumpf            1.122                 _incomingBuffer.append(
1918                                            headerLineTerminator, headerLineTerminatorLength);
1919                                        // null terminate - the buffer is at least as long after
1920                                        // removing
1921 kumpf            1.98                  char *data = (char *)_incomingBuffer.getData();
1922                                        data[_incomingBuffer.size()] = 0;
1923                                        _contentLength = 0;
1924                                        _contentOffset = 0;
1925                                    }
1926 brian.campbell   1.72  
1927 kumpf            1.98              break;
1928                                } // if last chunk
1929                        
1930 kumpf            1.122         // we are NOT on the last chunk! validate that the offset where the
1931                                // chunk terminator was found matches what the parsed chunk length
1932                                // claimed.
1933 kumpf            1.98  
1934                                if (strncmp(messageStart + chunkTerminatorOffset, chunkTerminator,
1935 brian.campbell   1.72                  chunkTerminatorLength) != 0)
1936 kumpf            1.98              _throwEventFailure(HTTP_STATUS_BADREQUEST, "Bad chunk terminator");
1937 brian.campbell   1.72  
1938 kumpf            1.98          // now remove the chunk terminator
1939                                _incomingBuffer.remove(chunkTerminatorOffset, chunkTerminatorLength);
1940 kumpf            1.128         messageLength = _incomingBuffer.size();
1941 kumpf            1.98          messageStart[messageLength] = 0;
1942 brian.campbell   1.72  
1943 kumpf            1.122         // jump to the start of the next chunk (which may not have been
1944                                // read yet)
1945 kumpf            1.98          _transferEncodingChunkOffset = chunkTerminatorOffset;
1946                            } // for all remaining bytes containing chunks
1947                        
1948                            PEG_METHOD_EXIT();
1949 brian.campbell   1.72  }
1950                        
1951                        /*
1952 david.dillard    1.93   * Handle a failure on the read or an HTTP error. This is NOT meant for
1953 brian.campbell   1.72   * errors found in the cim response or the trailer.
1954                         * The http status MAY have the detailed message attached to it using the
1955                         * detail delimiter.
1956                         */
1957                        
1958 kumpf            1.109 void HTTPConnection::_handleReadEventFailure(
1959                            const String& httpStatusWithDetail,
1960                            const String& cimError)
1961 brian.campbell   1.72  {
1962 kumpf            1.98      Uint32 delimiterFound = httpStatusWithDetail.find(httpDetailDelimiter);
1963                            String httpDetail;
1964 kumpf            1.109     String httpStatus = httpStatusWithDetail.subString(0, delimiterFound);
1965 brian.campbell   1.72  
1966 kumpf            1.98      if (delimiterFound != PEG_NOT_FOUND)
1967                            {
1968 kumpf            1.109         httpDetail = httpStatusWithDetail.subString(
1969                                    delimiterFound + httpDetailDelimiter.size());
1970 kumpf            1.98      }
1971 brian.campbell   1.72  
1972 thilo.boehm      1.157     PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,"%s%s%s%s%s",
1973                                (const char*)httpStatus.getCString(),
1974                                (const char*)httpDetailDelimiter.getCString(),
1975                                (const char*)httpDetail.getCString(),
1976                                (const char*)httpDetailDelimiter.getCString(),
1977                                (const char*)cimError.getCString()));
1978 brian.campbell   1.72  
1979 mike             1.104     Buffer message;
1980 kumpf            1.98      message = XmlWriter::formatHttpErrorRspMessage(httpStatus, cimError,
1981                                httpDetail);
1982                            HTTPMessage* httpMessage = new HTTPMessage(message);
1983                        
1984                            // this is common error code. If we are the server side, we want to send
1985                            // back the error to the client, but if we are the client side, then we
1986                            // simply want to queue up this error locally so the client app can receive
1987                            // the error. The client side's own message queue name will be the same
1988                            // as the connector name (the server would be acceptor)
1989 brian.campbell   1.72  
1990 kumpf            1.98      if (_isClient() == true)
1991                            {
1992                                httpMessage->dest = _outputMessageQueue->getQueueId();
1993 brian.campbell   1.72  
1994 kumpf            1.98          _outputMessageQueue->enqueue(httpMessage);
1995 brian.campbell   1.72  
1996 kumpf            1.98          _clearIncoming();
1997                            }
1998                            else
1999                            {
2000                                // else server side processing error - send back to client
2001 marek            1.131         PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2,
2002 kumpf            1.130             "<!-- Error response: queue id: %u -->\n%s",
2003                                    getQueueId(),
2004 marek            1.131             httpMessage->message.getData()));
2005 kumpf            1.98          handleEnqueue(httpMessage);
2006                            }
2007                            _closeConnection();
2008 brian.campbell   1.72  }
2009                        
2010 mike             1.2   void HTTPConnection::_handleReadEvent()
2011                        {
2012 kumpf            1.165     PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::_handleReadEvent");
2013 kumpf            1.7   
2014 kumpf            1.102     if (_acceptPending)
2015                            {
2016                                PEGASUS_ASSERT(!_isClient());
2017                        
2018                                Sint32 socketAcceptStatus = _socket->accept();
2019                        
2020                                if (socketAcceptStatus < 0)
2021                                {
2022 marek            1.154             PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1,
2023 kumpf            1.102                 "HTTPConnection: SSL_accept() failed");
2024                                    _closeConnection();
2025                                    PEG_METHOD_EXIT();
2026                                    return;
2027                                }
2028                                else if (socketAcceptStatus == 0)
2029                                {
2030                                    // Not enough data yet to complete the SSL handshake
2031 marek            1.154             PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
2032 kumpf            1.102                 "HTTPConnection: SSL_accept() pending");
2033                                    PEG_METHOD_EXIT();
2034                                    return;
2035                                }
2036                                else
2037                                {
2038 r.kieninger      1.158 #ifndef PEGASUS_OS_ZOS
2039 thilo.boehm      1.141         // Add SSL verification information to the authentication information
2040 thilo.boehm      1.117             if (_socket->isSecure())
2041                                    {
2042 kumpf            1.122                 if (_socket->isPeerVerificationEnabled() &&
2043                                            _socket->isCertificateVerified())
2044                                        {
2045 sushma.fernandes 1.132                     _authInfo->setConnectionAuthenticated(true);
2046 kumpf            1.122                     _authInfo->setAuthType(
2047                                                AuthenticationInfoRep::AUTH_TYPE_SSL);
2048                                            _authInfo->setClientCertificateChain(
2049                                                _socket->getPeerCertificateChain());
2050                                        }
2051 thilo.boehm      1.141             }
2052 thilo.boehm      1.117 #else
2053 thilo.boehm      1.141             if (_socket->isClientAuthenticated())
2054                                    {
2055                                        _authInfo->setAuthType(_socket->getAuthType());
2056                                        _authInfo->setConnectionAuthenticated(true);
2057                                        _authInfo->setConnectionUser(_socket->getAuthenticatedUser());
2058                                    }
2059 thilo.boehm      1.117 #endif
2060 kumpf            1.102 
2061                                    // Go back to the select() and wait for data on the connection
2062                                    _acceptPending = false;
2063                                    PEG_METHOD_EXIT();
2064                                    return;
2065                                }
2066                            }
2067                        
2068 mike             1.2       // -- Append all data waiting on socket to incoming buffer:
2069                        
2070                            Sint32 bytesRead = 0;
2071 kumpf            1.38      Boolean incompleteSecureReadOccurred = false;
2072 brian.campbell   1.72  
2073 mike             1.2       for (;;)
2074                            {
2075 kumpf            1.133         char buffer[httpTcpBufferSize];
2076 kumpf            1.98  
2077                                Sint32 n = _socket->read(buffer, sizeof(buffer)-1);
2078                        
2079 thilo.boehm      1.150         // Check if this was the first read of a connection to the server.
2080                                // This has to happen inside the read loop, because there can be
2081                                // an incomplete SSL read.
2082                                if (_firstRead && n > 5 && !_isClient())
2083                                {
2084                                    // The first bytes of a connection to the server have to contain
2085                                    // a valid cim-over-http HTTP Method (M-POST or POST).
2086                                    if ((strncmp(buffer, "POST", 4) != 0) &&
2087                                        (strncmp(buffer, "M-POST", 6) != 0))
2088                                    {
2089                                        _clearIncoming();
2090                        
2091 marek            1.154                 PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
2092 thilo.boehm      1.150                       "This Request has non-valid CIM-HTTP Method: "
2093                                              "%02X %02X %02X %02X %02X %02X",
2094                                              buffer[0],buffer[1],buffer[2],
2095                                              buffer[3],buffer[4],buffer[5]));
2096                        
2097 kumpf            1.163                 // Try to send message to client.
2098 thilo.boehm      1.150                 // This function also closes the connection.
2099                                        _handleReadEventFailure(HTTP_STATUS_NOTIMPLEMENTED);
2100                        
2101                                        PEG_METHOD_EXIT();
2102                                        return;
2103                                    }
2104                                    _firstRead = false;
2105                                }
2106                        
2107 kumpf            1.98          if (n <= 0)
2108                                {
2109 kumpf            1.140             // It is possible that SSL_read was not able to
2110                                    // read the entire SSL record.  This could happen
2111                                    // if the record was send in multiple packets
2112                                    // over the network and only some of the packets
2113                                    // are available.  Since SSL requires the entire
2114                                    // record to successfully decrypt, the SSL_read
2115                                    // operation will return "0 bytes" read.
2116                                    // Once all the bytes of the SSL record have been read,
2117                                    // SSL_read will return the entire record.
2118                                    // The following test was added to allow
2119                                    // handleReadEvent to distinguish between a
2120                                    // disconnect and partial read of an SSL record.
2121                                    //
2122                                    incompleteSecureReadOccurred =
2123                                        _socket->incompleteSecureReadOccurred(n);
2124 kumpf            1.98              break;
2125 kumpf            1.63          }
2126 david.dillard    1.93  
2127 kumpf            1.98          try
2128                                {
2129 kumpf            1.133             _incomingBuffer.reserveCapacity(_incomingBuffer.size() + n);
2130 kumpf            1.98              _incomingBuffer.append(buffer, n);
2131                                }
2132 kumpf            1.123         catch (...)
2133 kumpf            1.98          {
2134                                    static const char detailP[] =
2135                                        "Unable to append the request to the input buffer";
2136 kumpf            1.133             String httpStatus =
2137 kumpf            1.98                  HTTP_STATUS_REQUEST_TOO_LARGE + httpDetailDelimiter + detailP;
2138                                    _handleReadEventFailure(httpStatus);
2139                                    PEG_METHOD_EXIT();
2140                                    return;
2141                                }
2142 david.dillard    1.93  
2143 kumpf            1.98          bytesRead += n;
2144 gs.keenan        1.92  #if defined (PEGASUS_OS_VMS)
2145                                if (n < sizeof(buffer))
2146                                {
2147 kumpf            1.98              //
2148                                    // Read is smaller than the buffer size.
2149                                    // No more to read, continue.
2150                                    //
2151                                    break;
2152 gs.keenan        1.92          }
2153                        #endif
2154 mike             1.2       }
2155 mday             1.49  
2156 marek            1.131     PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
2157 kumpf            1.98          "Total bytesRead = %d; Bytes read this iteration = %d",
2158 marek            1.131         _incomingBuffer.size(), bytesRead));
2159 mike             1.2   
2160 brian.campbell   1.72      try
2161                            {
2162 kumpf            1.98          if (_contentOffset == -1)
2163                                    _getContentLengthAndContentOffset();
2164                                _handleReadEventTransferEncoding();
2165 brian.campbell   1.72      }
2166 kumpf            1.123     catch (Exception& e)
2167 brian.campbell   1.72      {
2168 kumpf            1.133         _handleReadEventFailure(e.getMessage());
2169 kumpf            1.98          PEG_METHOD_EXIT();
2170                                return;
2171 brian.campbell   1.72      }
2172 mike             1.2   
2173 david.dillard    1.93      // -- See if the end of the message was reached (some peers signal end of
2174 mike             1.2       // -- the message by closing the connection; others use the content length
2175                            // -- HTTP header and then there are those messages which have no bodies
2176                            // -- at all).
2177                        
2178 david.dillard    1.93      if ((bytesRead == 0 && !incompleteSecureReadOccurred) ||
2179 h.sterling       1.101         (_contentLength != -1 && _contentOffset != -1 &&
2180                                (Sint32(_incomingBuffer.size()) >= _contentLength + _contentOffset)))
2181 mike             1.2       {
2182 kumpf            1.133         // If no message was received, just close the connection
2183                                if (_incomingBuffer.size() == 0)
2184                                {
2185                                    _clearIncoming();
2186                        
2187                                    PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2,
2188                                        "<!-- No request message received; connection closed: "
2189                                            "queue id: %u -->",
2190                                        getQueueId()));
2191                                    _closeConnection();
2192                        
2193 sushma.fernandes 1.142             //
2194 kumpf            1.163             // If we are executing on the server side, the connection
2195 sushma.fernandes 1.142             // is closed, return. Do not forward an empty HTTP message.
2196                                    //
2197                                    if (!_isClient())
2198                                    {
2199                                        PEG_METHOD_EXIT();
2200                                        return;
2201                                    }
2202 kumpf            1.133         }
2203                        
2204 kumpf            1.163         // If the connection was closed and we are executing on the client
2205                                // side send an empty HTTP message. Otherwise, a message was
2206 sushma.fernandes 1.142         // received, so process it.
2207 kumpf            1.133 
2208 kumpf            1.98          HTTPMessage* message = new HTTPMessage(_incomingBuffer, getQueueId());
2209 a.arora          1.77          message->authInfo = _authInfo.get();
2210 kumpf            1.120         message->ipAddress = _ipAddress;
2211 kumpf            1.98          message->contentLanguages = contentLanguages;
2212 kumpf            1.133         message->dest = _outputMessageQueue->getQueueId();
2213 brian.campbell   1.85  
2214 sushma.fernandes 1.142         //
2215 kumpf            1.163         // The _closeConnection method sets the _connectionClosePending flag.
2216                                // If we are executing on the client side and the
2217 sushma.fernandes 1.142         // _connectionClosePending flag is set, send an empty HTTP message.
2218                                //
2219                                if (_connectionClosePending)
2220                                {
2221                                    _outputMessageQueue->enqueue(message);
2222                                    PEG_METHOD_EXIT();
2223                                    return;
2224                                }
2225                        
2226 kumpf            1.130         if (_isClient() == false)
2227                                {
2228 sushma.fernandes 1.138             PEG_TRACE((
2229 kumpf            1.163                 TRC_XML_IO,
2230 marek            1.154                 Tracer::LEVEL4,
2231 kumpf            1.163                 "<!-- Request: queue id: %u -->\n%s",
2232 kumpf            1.130                 getQueueId(),
2233 sushma.fernandes 1.138                 Tracer::getHTTPRequestMessage(
2234                                            _incomingBuffer).get()));
2235 kumpf            1.130         }
2236                        
2237 mike             1.2           //
2238 david.dillard    1.93          // increment request count
2239 mike             1.2           //
2240 kumpf            1.133         _connectionRequestCount++;
2241                                _responsePending = true;
2242                        
2243 sushma.fernandes 1.78          //
2244                                // Set the entry status to BUSY.
2245                                //
2246 sushma.fernandes 1.142         if (_isClient() == false)
2247 sushma.fernandes 1.78          {
2248 marek            1.154             PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
2249 kumpf            1.143                 "Now setting state to %d", MonitorEntry::STATUS_BUSY));
2250                                    _monitor->setState(_entry_index, MonitorEntry::STATUS_BUSY);
2251 sushma.fernandes 1.78              _monitor->tickle();
2252                                }
2253 kumpf            1.133 
2254 mreddy           1.124         try
2255                                {
2256                                    _outputMessageQueue->enqueue(message);
2257                                }
2258                                catch (Exception& e)
2259                                {
2260                                    String httpStatus =
2261                                        HTTP_STATUS_BADREQUEST + httpDetailDelimiter + e.getMessage();
2262                                    _handleReadEventFailure(httpStatus);
2263 dmitry.mikulin   1.149         }
2264                                catch (const exception& e)
2265                                {
2266                                    String httpStatus =
2267                                        HTTP_STATUS_BADREQUEST + httpDetailDelimiter + e.what();
2268                                    _handleReadEventFailure(httpStatus);
2269                                }
2270                                catch (...)
2271                                {
2272 kumpf            1.163             MessageLoaderParms
2273                                        mlParms("Common.HTTPConnection.UNKNOWN_EXCEPTION",
2274 dmitry.mikulin   1.149                         "Unknown exception caught while parsing HTTP "
2275                                                "message.");
2276                                    String mlString(MessageLoader::getMessage(mlParms));
2277                                    String httpStatus =
2278                                        HTTP_STATUS_BADREQUEST + httpDetailDelimiter + mlString;
2279                                    _handleReadEventFailure(httpStatus);
2280                                }
2281 mreddy           1.124 
2282 kumpf            1.98          _clearIncoming();
2283 mike             1.2       }
2284 kumpf            1.7       PEG_METHOD_EXIT();
2285 mike             1.2   }
2286                        
2287 kumpf            1.147 Boolean HTTPConnection::isResponsePending()
2288 mike             1.2   {
2289 kumpf            1.147     return _responsePending;
2290 mike             1.2   }
2291 mday             1.18  
2292 mday             1.19  Boolean HTTPConnection::run(Uint32 milliseconds)
2293                        {
2294 kumpf            1.98      Boolean handled_events = false;
2295                            int events = 0;
2296 kumpf            1.133     fd_set fdread;
2297 kumpf            1.98      struct timeval tv = { 0, 1 };
2298                            FD_ZERO(&fdread);
2299                            FD_SET(getSocket(), &fdread);
2300                            events = select(FD_SETSIZE, &fdread, NULL, NULL, &tv);
2301 mike             1.111 
2302                            if (events == PEGASUS_SOCKET_ERROR)
2303 kumpf            1.98          return false;
2304                        
2305                            if (events)
2306                            {
2307                                events = 0;
2308 kumpf            1.123         if (FD_ISSET(getSocket(), &fdread))
2309 kumpf            1.98          {
2310                                    events |= SocketMessage::READ;
2311                                    Message *msg = new SocketMessage(getSocket(), events);
2312                                    try
2313                                    {
2314                                        handleEnqueue(msg);
2315                                    }
2316 kumpf            1.123             catch (...)
2317 kumpf            1.98              {
2318 marek            1.131                 PEG_TRACE_CSTRING(
2319 kumpf            1.163                     TRC_DISCARDED_DATA,
2320 marek            1.154                     Tracer::LEVEL1,
2321 kumpf            1.98                      "HTTPConnection::run handleEnqueue(msg) failure");
2322                                        return true;
2323                                    }
2324                                    handled_events = true;
2325                                }
2326                            }
2327 sushma.fernandes 1.78  
2328 kumpf            1.98      return handled_events;
2329 mday             1.19  }
2330 mday             1.54  
2331 mike             1.2   PEGASUS_NAMESPACE_END

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2