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

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

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2