Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/downloader/chunkedStreamBuf.cxx

Go to the documentation of this file.
00001 // Filename: chunkedStreamBuf.cxx
00002 // Created by:  drose (25Sep02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "chunkedStreamBuf.h"
00020 #include <ctype.h>
00021 
00022 // This module is not compiled if OpenSSL is not available.
00023 #ifdef HAVE_SSL
00024 
00025 #ifndef HAVE_STREAMSIZE
00026 // Some compilers (notably SGI) don't define this for us
00027 typedef int streamsize;
00028 #endif /* HAVE_STREAMSIZE */
00029 
00030 ////////////////////////////////////////////////////////////////////
00031 //     Function: ChunkedStreamBuf::Constructor
00032 //       Access: Public
00033 //  Description:
00034 ////////////////////////////////////////////////////////////////////
00035 ChunkedStreamBuf::
00036 ChunkedStreamBuf() {
00037   _chunk_remaining = 0;
00038   _done = true;
00039 
00040 #ifdef HAVE_IOSTREAM
00041   char *buf = new char[4096];
00042   char *ebuf = buf + 4096;
00043   setg(buf, ebuf, ebuf);
00044   setp(buf, ebuf);
00045 
00046 #else
00047   allocate();
00048   setg(base(), ebuf(), ebuf());
00049   setp(base(), ebuf());
00050 #endif
00051 }
00052 
00053 ////////////////////////////////////////////////////////////////////
00054 //     Function: ChunkedStreamBuf::Destructor
00055 //       Access: Public, Virtual
00056 //  Description:
00057 ////////////////////////////////////////////////////////////////////
00058 ChunkedStreamBuf::
00059 ~ChunkedStreamBuf() {
00060   close_read();
00061 }
00062 
00063 ////////////////////////////////////////////////////////////////////
00064 //     Function: ChunkedStreamBuf::open_read
00065 //       Access: Public
00066 //  Description: If the document pointer is non-NULL, it will be
00067 //               updated with the length of the file as it is derived
00068 //               from the chunked encoding.
00069 ////////////////////////////////////////////////////////////////////
00070 void ChunkedStreamBuf::
00071 open_read(BioStreamPtr *source, HTTPChannel *doc) {
00072   _source = source;
00073   nassertv(!_source.is_null());
00074   _chunk_remaining = 0;
00075   _done = false;
00076   _doc = doc;
00077 
00078   if (_doc != (HTTPChannel *)NULL) {
00079     _read_index = doc->_read_index;
00080     _doc->_file_size = 0;
00081 
00082     // Read a little bit from the file to get the first chunk (and
00083     // therefore the file size, or at least the size of the first
00084     // chunk).
00085     underflow();
00086   }
00087 }
00088 
00089 ////////////////////////////////////////////////////////////////////
00090 //     Function: ChunkedStreamBuf::close_read
00091 //       Access: Public
00092 //  Description:
00093 ////////////////////////////////////////////////////////////////////
00094 void ChunkedStreamBuf::
00095 close_read() {
00096   _source.clear();
00097 }
00098 
00099 ////////////////////////////////////////////////////////////////////
00100 //     Function: ChunkedStreamBuf::underflow
00101 //       Access: Protected, Virtual
00102 //  Description: Called by the system istream implementation when its
00103 //               internal buffer needs more characters.
00104 ////////////////////////////////////////////////////////////////////
00105 int ChunkedStreamBuf::
00106 underflow() {
00107   // Sometimes underflow() is called even if the buffer is not empty.
00108   if (gptr() >= egptr()) {
00109     size_t buffer_size = egptr() - eback();
00110     gbump(-(int)buffer_size);
00111 
00112     size_t num_bytes = buffer_size;
00113     size_t read_count = read_chars(gptr(), buffer_size);
00114 
00115     if (read_count != num_bytes) {
00116       // Oops, we didn't read what we thought we would.
00117       if (read_count == 0) {
00118         gbump(num_bytes);
00119         return EOF;
00120       }
00121 
00122       // Slide what we did read to the top of the buffer.
00123       nassertr(read_count < num_bytes, EOF);
00124       size_t delta = num_bytes - read_count;
00125       memmove(gptr() + delta, gptr(), read_count);
00126       gbump(delta);
00127     }
00128   }
00129 
00130   return (unsigned char)*gptr();
00131 }
00132 
00133 
00134 ////////////////////////////////////////////////////////////////////
00135 //     Function: ChunkedStreamBuf::read_chars
00136 //       Access: Private
00137 //  Description: Gets some characters from the source stream.
00138 ////////////////////////////////////////////////////////////////////
00139 size_t ChunkedStreamBuf::
00140 read_chars(char *start, size_t length) {
00141   nassertr(!_source.is_null(), 0);
00142   if (_done) {
00143     return 0;
00144   }
00145 
00146   if (_chunk_remaining != 0) {
00147     // Extract some of the bytes remaining in the chunk.
00148     length = min(length, _chunk_remaining);
00149     (*_source)->read(start, length);
00150     size_t read_count = (*_source)->gcount();
00151     _chunk_remaining -= read_count;
00152 
00153     if (read_count == 0 && (*_source)->is_closed()) {
00154       // Whoops, the socket closed while we were downloading.
00155       if (_doc != (HTTPChannel *)NULL && _read_index == _doc->_read_index) {
00156         _doc->_state = HTTPChannel::S_failure;
00157       }
00158     }
00159 
00160     return read_count;
00161   }
00162 
00163   // Read the next chunk.
00164   string line;
00165   bool got_line = http_getline(line);
00166   while (got_line && line.empty()) {
00167     // Skip blank lines.  There really should be exactly one blank
00168     // line, but who's counting?  It's tricky to count and maintain
00169     // reentry for nonblocking I/O.
00170     got_line = http_getline(line);
00171   }
00172   if (!got_line) {
00173     // EOF (or data unavailable) while trying to read the chunk size.
00174     if ((*_source)->is_closed()) {
00175       // Whoops, the socket closed while we were downloading.
00176       if (_doc != (HTTPChannel *)NULL && _read_index == _doc->_read_index) {
00177         _doc->_state = HTTPChannel::S_failure;
00178       }
00179     }
00180     return 0;
00181   }
00182   size_t chunk_size = (size_t)strtol(line.c_str(), NULL, 16);
00183   if (chunk_size == 0) {
00184     // Last chunk; we're done.
00185     _done = true;
00186     if (_doc != (HTTPChannel *)NULL && _read_index == _doc->_read_index) {
00187       _doc->finished_body(true);
00188     }
00189     return 0;
00190   }
00191 
00192   if (_doc != (HTTPChannel *)NULL && _read_index == _doc->_read_index) {
00193     _doc->_file_size += chunk_size;
00194   }
00195 
00196   _chunk_remaining = chunk_size;
00197   return read_chars(start, length);
00198 }
00199 
00200 ////////////////////////////////////////////////////////////////////
00201 //     Function: ChunkedStreamBuf::http_getline
00202 //       Access: Private
00203 //  Description: Reads a single line from the stream.  Returns
00204 //               true if the line is successfully retrieved, or false
00205 //               if a complete line has not yet been received or if
00206 //               the connection has been closed.
00207 ////////////////////////////////////////////////////////////////////
00208 bool ChunkedStreamBuf::
00209 http_getline(string &str) {
00210   nassertr(!_source.is_null(), false);
00211   int ch = (*_source)->get();
00212   while (!(*_source)->eof() && !(*_source)->fail()) {
00213     switch (ch) {
00214     case '\n':
00215       // end-of-line character, we're done.
00216       str = _working_getline;
00217       _working_getline = string();
00218       {
00219         // Trim trailing whitespace.  We're not required to do this per the
00220         // HTTP spec, but let's be generous.
00221         size_t p = str.length();
00222         while (p > 0 && isspace(str[p - 1])) {
00223           --p;
00224         }
00225         str = str.substr(0, p);
00226       }
00227 
00228       return true;
00229 
00230     case '\r':
00231       // Ignore CR characters.
00232       break;
00233 
00234     default:
00235       _working_getline += (char)ch;
00236     }
00237     ch = (*_source)->get();
00238   }
00239 
00240   return false;
00241 }
00242 
00243 #endif  // HAVE_SSL

Generated on Fri May 2 00:36:43 2003 for Panda by doxygen1.3