summaryrefslogtreecommitdiffstats
path: root/base/tps/src/httpClient/response.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'base/tps/src/httpClient/response.cpp')
-rw-r--r--base/tps/src/httpClient/response.cpp1115
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" );
+ }
+ }
+}