/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /** BEGIN COPYRIGHT BLOCK * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Copyright (C) 2007 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ /** * HTTP response handler */ #include #include #include #include "nspr.h" #include "engine/RA.h" #include "main/Util.h" #include "httpClient/httpc/response.h" #include "httpClient/httpc/engine.h" //-- #include "httpClient/httpc/DebugLogger.h" #include "httpClient/httpc/PSPRUtil.h" #include "main/Memory.h" //-- static const char *DEBUG_MODULE = "httpclient"; //-- static const char *DEBUG_CLASS_NAME = "PSHttpResponse"; void printBuf(int , char* ); /** * Constructor. This class is used by the HttpResponse class for reading and * processing data from the socket * @param socket The NSPR socket from which the response is expected * @param size The size of the internal buffer to hold data * @param timeout Timeout in seconds on receiving a response */ RecvBuf::RecvBuf( const PRFileDesc *socket, int size, int timeout ) { _socket = socket; _allocSize = size; _buf = (char *)PR_Malloc(size); _curPos = 0; _curSize = 0; _chunkedMode = PR_FALSE; _currentChunkSize = _currentChunkBytesRead = 0; _timeout = PR_TicksPerSecond() * timeout; _content = NULL; } /** * Destructor */ RecvBuf::~RecvBuf() { if( _buf != NULL ) { PR_Free( _buf ); _buf = NULL; } } /** * Reads the specified number of bytes from the socket and place it into the buffer * * @param socket The NSPR socket from which the response is expected * @param size The size of the buffer * @return PR_TRUE on success, otherwise PR_FALSE */ PRBool RecvBuf::_getBytes(int size) { //-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); PRErrorCode pec; _curPos = 0; int num =1; int i =0; PRBool endChunk= PR_FALSE; RA::Debug( LL_PER_PDU, "RecvBuf::_getBytes: ", "Start RecvBuf::_getBytes" ); // actual reading from the socket happens here do { num = PR_Recv( (PRFileDesc*)_socket, &_buf[_curSize], _allocSize-_curSize, 0, _timeout ); RA::Debug( LL_PER_PDU, "RecvBuf::_getBytes: ", "num of bytes read from the socket=%d", num ); /* * in chunked mode, ending chunk contains a 0 to begin * loop through to see if it contains just 0 (skip carriage returns * endChunk indicates possible end chunk. */ if ((_chunkedMode == PR_TRUE) && (num < 10)) { endChunk = PR_FALSE; for (i=0; i< num; i++) { if (endChunk == PR_TRUE) { if ((_buf[_curSize+i] == 13) || (_buf[_curSize+i] == 10)) continue; else { endChunk = PR_FALSE; break; // not an endChunk } } else { // endChunk==PR_FALSE if (_buf[_curSize+i] == '0') { RA::Debug( LL_PER_PDU, "RecvBuf::_getBytes: ", "may be chunked mode end chunk" ); endChunk = PR_TRUE; } else if ((_buf[_curSize+i] == 13) || (_buf[_curSize+i] == 10)) continue; else { endChunk = PR_FALSE; break; // not an endChunk } } } // for } if (num >0) _curSize = _curSize+num; if (_chunkedMode == PR_FALSE) { if (getAllContent()) { RA::Debug( LL_PER_PDU, "RecvBuf::_getBytes: ", "Already got all the content, no need to call PR_Recv again." ); break; } } if (endChunk == PR_TRUE) break; } while (num > 0); if (num <0) { pec = PR_GetError(); RA::Debug( LL_PER_PDU, "RecvBuf::_getBytes: ", "error in pr_recv, err=%d", pec ); } if ( _curSize <= 0 ) { return PR_FALSE; } _buf[_curSize] = '\0'; //-- logger->Log( LOGLEVEL_FINEST, DEBUG_CLASS_NAME, //-- "getBytes", _content = (char *) PR_Malloc(_curSize+1); if (_content == NULL) { return PR_FALSE; } memcpy((char*) _content, (const char *)_buf, _curSize+1); _contentSize = _curSize +1; RA::Debug(LL_PER_PDU, "RecvBuf::_getBytes", "buffer received with size %d follows:", _contentSize); printBuf(_contentSize, _content); return PR_TRUE; } int RecvBuf::getAllContent() { //int result[10]; //int j=0; //int k=0; int number = 0; for (int i=0; i<_curSize; i++) { if (_buf[i] == '\r') { if (i < (_curSize-3)) { if (_buf[i+1] == '\n' && _buf[i+2] == '\r' && _buf[i+3] == '\n') { // find content length // strcasestr may not be supported by Solaris // char *clen = strcasestr(_buf, "Content-length:"); char *clen = strstr(_buf, "Content-Length:"); if (clen != NULL) { clen = &clen[16]; number = atoi(clen); /* while (1) { if ((number=Util::ascii2numeric(clen[j++])) >= 0) { result[k++] = number; } else { break; } } number = 0; for (int l=0; l= _curSize) { if (!_getBytes(_allocSize)) { /* bugscape #55624: Solaris RA exited with a signal ABRT if we raised exception without handling it */ return -1; /* throw RecvBuf::EndOfFile(); */ } } return _buf[_curPos++]; } /** * gets the next char from the buffer. If all the data in the buffer is read , * read a chunk to the buffer * @returns - the next char from the data */ char RecvBuf::getChar() { if (!_chunkedMode) return _getChar(); else { if (_currentChunkSize == 0) { // read the chunk header char ch, chunkStr[20]; int index = 0; while (!isspace(ch = _getChar()) ) chunkStr[index++] = ch; chunkStr[index] = '\0'; sscanf((char *)chunkStr, "%x", (unsigned int *)(&_currentChunkSize)); if (ch != '\n') { char ch2 = _getChar(); if (ch != '\r' || ch2 != '\n') { printf( "did not find CRLF after chunk"); } } if (_currentChunkSize == 0) return -1; _currentChunkBytesRead = 1; return _buf[_curPos++]; } else if (_currentChunkBytesRead < _currentChunkSize) { // read a byte from the chunk _currentChunkBytesRead++; return _getChar(); } else { // read the chunk trailer char ch1 = _getChar(); char ch2 = _getChar(); if (ch1 != '\r' || ch2 != '\n') { printf( "did not find CRLF after chunk"); }; _currentChunkSize = _currentChunkBytesRead = 0; return getChar(); }; }; } char *RecvBuf::get_content() { return _content; } int RecvBuf::get_contentSize() { return _contentSize; } /** * Decrements the pointer to the internal buffer so that the next read would * retrieve the last data again */ void RecvBuf::putBack() { if (_curPos > 0) { _curPos--; if (_chunkedMode) { _currentChunkBytesRead--; } } } /** * Sets the chunked mode for reading data * Not used now.. */ void RecvBuf::setChunkedMode() { _chunkedMode = PR_TRUE; _currentChunkSize = _currentChunkBytesRead = 0; } /** * Gets the timeout in seconds for reading * * @return The timeout in seconds for reading */ int RecvBuf::getTimeout() { return _timeout / PR_TicksPerSecond(); } Response::Response(const PRFileDesc *sock, NetRequest *request) { _socket = sock; _request = request; } /** * Constructor */ PSHttpResponse::PSHttpResponse( const PRFileDesc *sock, PSHttpRequest *request, int timeout , PRBool expectChunked): Response(sock, request) { _request = request; _proto = HTTPNA; _protocol = NULL; retcode =0 ; _statusNum = NULL; _statusString = NULL; _keepAlive = -1; _connectionClosed = 0; _bodyLength = -1; _content = NULL; _headers = new StringKeyCache("response",10*60); _expectChunked = expectChunked; _chunkedResponse = PR_FALSE; _timeout = timeout; } PSHttpResponse::~PSHttpResponse() { if( _protocol != NULL ) { PL_strfree( _protocol ); _protocol = NULL; } if( _statusString != NULL ) { PL_strfree( _statusString ); _statusString = NULL; } if( _statusNum != NULL ) { PL_strfree( _statusNum ); _statusNum = NULL; } if (_headers) { Iterator* iterator = _headers->GetKeyIterator(); while ( iterator->HasMore() ) { const char* name = (const char*)iterator->Next(); CacheEntry* entry = _headers->Remove( name ); if ( entry ) { char* value = (char*)entry->GetData(); if( value != NULL ) { PL_strfree( value ); value = NULL; } if( entry != NULL ) { delete entry; entry = NULL; } } } if( iterator != NULL ) { delete iterator; iterator = NULL; } if( _headers != NULL ) { delete _headers; _headers = NULL; } } _socket = 0; } long PSHttpResponse::getStatus() { return _statusNum ? atoi(_statusNum) : 0; } int PSHttpResponse::getReturnCode() { return retcode; } char * PSHttpResponse::getStatusString() { return _statusString?_statusString:(char*)""; } HttpProtocol PSHttpResponse::getProtocol() { // first check the response protocol if (_proto == HTTPNA) { if (_protocol) { int major, minor; sscanf(_protocol, "HTTP/%d.%d", &major, &minor); switch(major) { case 1: switch(minor) { case 0: _proto = HTTP10; break; case 1: _proto = HTTP11; break; } break; } } else { _proto = HTTP09; } } if (_proto == HTTP11) { // A 1.1 compliant server response shows the protocol as HTTP/1.1 even // for a HTTP/1.0 request, but it promises to only use HTTP/1.0 syntax. if (_request->getProtocol() == HTTP10) { _proto = HTTP10; } } return _proto; }; char * PSHttpResponse::getHeader(const char *name) { CacheEntry *entry = _headers->Get(name); return entry ? (char *)entry->GetData() : NULL; } int PSHttpResponse::getHeaders(char ***keys) { return _headers->GetKeys( keys ); } long PSHttpResponse::getBodyLength() { return _bodyLength; } char * PSHttpResponse::getContent() { return _content; } void PSHttpResponse::freeContent() { if( _content != NULL ) { PR_Free( _content ); _content = NULL; } } int PSHttpResponse::getContentSize() { return _contentSize; } char *PSHttpResponse::toString() { char *resp = (char *)""; char **keys; char *headerBuf = NULL; int nHeaders = getHeaders( &keys ); if ( nHeaders > 0 ) { char **values = new char*[nHeaders]; int len = 0; int *keyLengths = new int[nHeaders]; int *valueLengths = new int[nHeaders]; int i; for( i = 0; i < nHeaders; i++ ) { keyLengths[i] = strlen( keys[i] ); len += keyLengths[i] + 1; values[i] = getHeader(keys[i]); valueLengths[i] = strlen( values[i] ); len += valueLengths[i] + 1; } headerBuf = new char[len + nHeaders * 2]; char *p = headerBuf; for( i = 0; i < nHeaders; i++ ) { strcpy( p, keys[i] ); p += keyLengths[i]; *p++ = ':'; strcpy( p, values[i] ); p += valueLengths[i]; *p++ = ','; } *p = 0; for( i = 0; i < nHeaders; i++ ) { if( keys[i] != NULL ) { delete [] keys[i]; keys[i] = NULL; } } if( keys != NULL ) { delete [] keys; keys = NULL; } if( values != NULL ) { delete [] values; values = NULL; } if( keyLengths != NULL ) { delete [] keyLengths; keyLengths = NULL; } if( valueLengths != NULL ) { delete [] valueLengths; valueLengths = NULL; } } char *s = NULL; if ( headerBuf ) { s = PR_smprintf( "PSHttpResponse [%s\nbody bytes:%d]", headerBuf, _bodyLength ); } else { s = PR_smprintf( "PSHttpResponse [body bytes:%d]", _bodyLength ); } resp = new char[strlen(s) + 1]; strcpy( resp, s ); if( s != NULL ) { PR_smprintf_free( s ); s = NULL; } return resp; } PRBool PSHttpResponse::checkKeepAlive() { HttpProtocol proto; const char *connectionHeader; //-- static const char *DEBUG_METHOD_NAME = "checkKeepAlive"; //-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); if (_keepAlive < 0) { proto = getProtocol(); if (proto == HTTP11) { // default is connection: keep-alive _keepAlive = 1; } else { // default is connection: close // _keepAlive = 0; //CMS needs keepalive with HTTP10 (so no chunked encoding) _keepAlive=1; } connectionHeader = _request->getHeader("connection"); if (connectionHeader) { if (!PL_strcasecmp(connectionHeader, "keep-alive")) { _keepAlive = 1; } else if (!PL_strcasecmp(connectionHeader, "close")) { _keepAlive = 0; } else { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::checkKeepAlive: ", "Unknown connection header" ); } } } return (_keepAlive == 0?PR_FALSE:PR_TRUE); } PRBool PSHttpResponse::checkConnection() { // return true if the connection is OPEN return (_connectionClosed == 0?PR_TRUE:PR_FALSE); } int PSHttpResponse::_verifyStandardBody(RecvBuf &buf, int expectedBytes, PRBool check) { int bytesRead = 0; int curPos = 0; char ch; //-- static const char *DEBUG_METHOD_NAME = "_verifyStandardBody"; //-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); while(expectedBytes > 0 ) { ch = buf.getChar(); if (ch < 0 ) { break; } // if check is true, we think we know what the content looks like if ( check ) { if (ch != (char) curPos%256) { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_verifyStandardBody: ", "Response data corrupt at byte %d (%d, %d)", curPos, ch, ( curPos % 256 ) ); check = PR_FALSE; break; } curPos++; } bytesRead++; if (expectedBytes > 0) { expectedBytes--; } } return bytesRead; } PRBool PSHttpResponse::_handleBody( RecvBuf &buf ) { char *clHeader; // content length header char *teHeader; // transfer-encoding header int expected_cl=-1; // expected content length //-- static const char *DEBUG_METHOD_NAME = "_handleBody"; //-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); teHeader = getHeader("transfer-encoding"); if (teHeader && !PL_strcasecmp(teHeader, "chunked")) { _chunkedResponse = PR_TRUE; buf.setChunkedMode(); } else { _chunkedResponse = PR_FALSE; clHeader = getHeader("Content-length"); if (clHeader) { expected_cl = atoi(clHeader); } } if (_request->getExpectStandardBody()) { _bodyLength = _verifyStandardBody(buf, expected_cl, PR_TRUE); } else { _bodyLength = _verifyStandardBody(buf, expected_cl, PR_FALSE); } if (expected_cl >= 0) { if (_bodyLength != expected_cl) { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_handleBody: ", "Content length was incorrect (%d/%d bytes)", _bodyLength, expected_cl ); } } return PR_TRUE; } /** * Reads until the first space character * * @param buf Receive buffer to read from * @param headerBuf Array to read header into * @param len Size of headerBuf * @return Number of characters read, or -1 if too many */ static int readHeader( RecvBuf& buf, char* headerBuf, int len ) { int index = 0; do { char ch = buf.getChar(); if ( ch != -1 && !isspace(ch) ) { headerBuf[index++] = ch; if ( index >= (len-1) ) { return -1; } } else { headerBuf[index] = '\0'; break; } } while( true ); // RA::Debug( LL_PER_PDU, // "readHeader: ", // "headerBuf = %s", // headerBuf ); return index; } PRBool PSHttpResponse::processResponse() { RecvBuf buf( _socket, 8192, _timeout ); if (_expectChunked) { buf.setChunkedMode(); } //-- static const char *DEBUG_METHOD_NAME = "processResponse"; //-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Entered processResponse()" ); try { char tmp[2048]; int tmpLen = sizeof(tmp); // Get protocol string int nRead = readHeader( buf, tmp, tmpLen ); if ( nRead < 0 ) { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Returned more than expected bytes %d " "in protocol header", sizeof( tmp ) ); return PR_FALSE; } _protocol = PL_strdup(tmp); //-- logger->Log( LOGLEVEL_FINER, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Protocol header: %s", _protocol ); // Get status num nRead = readHeader( buf, tmp, tmpLen ); if ( nRead < 0 ) { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Returned more than expected bytes %d " "in status header", tmpLen ); return PR_FALSE; } _statusNum = PL_strdup( tmp ); //-- logger->Log( LOGLEVEL_FINER, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Status header: %s", _statusNum ); retcode = atoi( tmp ); // Get status string int index = 0; do { char ch = buf.getChar(); if ( ch != -1 && ch != '\r' ) { tmp[index++] = ch; if ( index >= (tmpLen-2) ) { tmp[index] = 0; //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Returned more than expected bytes %d " "in protocol header:\n%s", tmpLen, tmp ); return PR_FALSE; } } else { break; } } while (true); tmp[index] = '\0'; _statusString = PL_strdup( tmp ); // Skip CRLF (void)buf.getChar(); // loop over response headers index = 0; #ifdef CHECK PRBool doneParsing = PR_FALSE; PRBool atEOL = PR_FALSE; PRBool inName = PR_TRUE; char name[2048]; int nameLen = sizeof(name); while ( !doneParsing ) { char value[2048]; int valueLen = sizeof(value); char ch = buf.getChar(); switch( ch ) { case ':': if ( inName ) { name[index] = '\0'; index = 0; inName = PR_FALSE; nRead = readHeader( buf, value, valueLen ); if ( nRead < 0 ) { //-- logger->Log( LOGLEVEL_SEVERE, //-- DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Name %s in header does not " "have a value", name ); // return PR_FALSE; } else { value[index++] = ch; if ( index >= (int)(sizeof(value) - 1 ) ) { //-- logger->Log( LOGLEVEL_SEVERE, //-- DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Name %s in header does not " "have a value", name ); // return PR_FALSE; } } break; case '\r': if ( inName && !atEOL ) { name[index] = '\0'; //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Name %s in header does not " "have a value", name ); // return PR_FALSE; } break; case '\n': if ( atEOL ) { doneParsing = PR_TRUE; break; } if ( inName ) { name[index] = '\0'; //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Name %s in header does not " "have a value", name ); // return PR_FALSE; } value[index] = '\0'; index = 0; inName = PR_TRUE; _headers->Put(name, PL_strdup(value)); atEOL = PR_TRUE; break; default: atEOL = PR_FALSE; if (inName) { name[index++] = ch; } else { value[index++] = ch; } if ( inName && (index >= (nameLen-2)) ) { name[index] = '\0'; //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Name %s in header exceeds the expected " "%d max characters", name, nameLen ); // return PR_FALSE; } else if ( !inName && (index >= (valueLen-1)) ) { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Name %s in header does not " "have a value", name ); // return PR_FALSE; } break; } } } //while #endif //CHECK } catch ( RecvBuf::EndOfFile & ) { if ( !_request->isHangupOk() ) { int errCode = PR_GetError(); if ( PR_IO_TIMEOUT_ERROR == errCode ) { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Timed out reading response (%d seconds)", buf.getTimeout() ); } else { //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Received unexpected end of file from server\n%s", "XXX" ); } } return PR_FALSE; } // Read the body (HEAD requests don't have bodies) // jpierre 1xx, 204 and 304 don't have bodies either if ( PL_strcmp(_request->getMethod(), "HEAD") && (!((retcode>=100) && (retcode<200))) && (retcode!=204) && (retcode!=304) ) { if ( _handleBody(buf) == PR_FALSE ) { return PR_FALSE; } } if ( checkConnection() && !checkKeepAlive() ) { // if connection is still open, and we didn't expect a keepalive, // read another byte to see if the connection has closed. try { char ch; ch = buf.getChar(); buf.putBack(); // conflict! //-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "Connection kept alive when it shouldn't" ); } catch (RecvBuf::EndOfFile &) { _connectionClosed = 1; } } _checkResponseSanity(); _content = (char *)buf.get_content(); _contentSize = buf.get_contentSize(); RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "processed Buffer contentSize=%d", getContentSize() ); if (_content != NULL) { RA::Debug( LL_PER_PDU, "PSHttpResponse::processResponse: ", "processed Buffer content=%s", _content ); } // char * yo = getContent(); return PR_TRUE; } void PSHttpResponse::_checkResponseSanity() { char *clHeader = getHeader("Content-length"); char *teHeader = getHeader("Transfer-encoding"); //-- static const char *DEBUG_METHOD_NAME = "checkResponseSanity"; //-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); RA::Debug( LL_PER_PDU, "PSHttpResponse::_checkResponseSanity: ", "in _checkResponseSanity" ); /////////////////////////////////////////////////// // Check items relevant to HTTP/1.0 and HTTP/1.1 // /////////////////////////////////////////////////// // check for both content-length and chunked if ( clHeader && teHeader ) { //-- logger->Log( LOGLEVEL_FINER, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_checkResponseSanity: ", "Response contains both content-length and " "transfer-encoding" ); } // check for basic headers if ( !getHeader("Date") ) { //-- logger->Log( LOGLEVEL_WARNING, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_checkResponseSanity: ", "Response does not contain a date header" ); } if ( !getHeader("Server") ) { //-- logger->Log( LOGLEVEL_WARNING, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_checkResponseSanity: ", "Response does not contain a server header" ); } int expectedLength; if ((expectedLength = _request->getExpectedResponseLength()) > 0) { if (expectedLength != _bodyLength) { //-- logger->Log( LOGLEVEL_INFO, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_checkResponseSanity: ", "Response body length does not match expected " "response length (%d/%d)", _bodyLength, expectedLength ); } } /////////////////////////////////////// // Check items relevant to HTTP/1.0 // /////////////////////////////////////// if ( getProtocol() == HTTP10 ) { if ( _chunkedResponse ) { //-- logger->Log( LOGLEVEL_INFO, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_checkResponseSanity: ", "Server sent a chunked HTTP/1.0 response" ); } } /////////////////////////////////////// // Check items relevant to HTTP/1.1 // /////////////////////////////////////// if ( getProtocol() == HTTP11 ) { if ( (!clHeader && !_chunkedResponse) && (!((retcode>=100) && (retcode<200))) && (retcode!=204) && (retcode!=304) ) { //-- logger->Log( LOGLEVEL_INFO, DEBUG_CLASS_NAME, //-- DEBUG_METHOD_NAME, RA::Debug( LL_PER_PDU, "PSHttpResponse::_checkResponseSanity: ", "Server responded with a HTTP/1.1 response without " "content-length or chunked encoding" ); } } }