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

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2