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

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2