diff options
Diffstat (limited to 'base/tps/src/httpClient/response.cpp')
-rw-r--r-- | base/tps/src/httpClient/response.cpp | 1115 |
1 files changed, 1115 insertions, 0 deletions
diff --git a/base/tps/src/httpClient/response.cpp b/base/tps/src/httpClient/response.cpp new file mode 100644 index 000000000..89b900492 --- /dev/null +++ b/base/tps/src/httpClient/response.cpp @@ -0,0 +1,1115 @@ +/* -*- 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 <ctype.h> +#include <string.h> +#include <math.h> + +#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<k; l++) + number = (int)(number + result[l]*(float)pow((float)10, (float)k-l-1)); +*/ + RA::Debug( LL_PER_PDU, + "RecvBuf::getAllContent: ", + "content length number=%d", + number ); + } + int remainingBytes = _curSize - (i+4); + RA::Debug( LL_PER_PDU, + "RecvBuf::getAllContent: ", + "remainingbytes=%d", + remainingBytes ); + if (remainingBytes == number) + return 1; + } + } + } + } + + return 0; +} + +void printBuf(int len, char* buf) { + RA::Debug(LL_PER_PDU, "response:printBuf", + "Buffer print begins"); + RA::Debug(LL_PER_PDU, "response::printBuf", + "%s", buf); + RA::Debug(LL_PER_PDU, "response:printBuf", + "Buffer print end"); + /* + int times = len/256; + if (len%256) + times++; + RA::Debug("response:printBuf", + "%d times", times); + RA::Debug("response:printBuf", + "attempting to print the whole buffer:"); + + int i; + + for (i = 0; i< times; i++) { + char *temp; + temp = PL_strdup((char *)buf+i*256); + RA::Debug("response:printBuf", + "%s", temp); + } + */ +} + +/** + * 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 (_curPos >= _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" ); + } + } +} |