gBHttp.cpp

Go to the documentation of this file.
00001 // gBHttp.cpp -- Version 0.8
00002 
00003 #include <string.h>  //memcpy, ...
00004 #include "gBHttp.h"
00005 #include "gstringext.h"
00006 #include "garg.h"
00007 ////////////////////////////////////////////////////////////
00008 gXHttpReplyHeader::gXHttpReplyHeader ()
00009     : htmlCode( -1 ),
00010       isTransferEncodingChunked( false )
00011 {
00012 }
00013 
00014 bool gXHttpReplyHeader::IsText ()
00015 {
00016  gString sKeyText;
00017  // If no key is found, we assume binary
00018  if ( GetLine( "Content-Type", sKeyText )==false ) return false;
00019  gParam paramText( sKeyText, "/" );
00020  paramText.SetCaseSense( false );
00021  return paramText.Match( "text" );
00022 }
00023 
00024 bool gXHttpReplyHeader::GetLine (char* sKind, gString& sResult)
00025 {
00026  unsigned idx;
00027  idx = kindsL.Match( sKind );
00028  if ( idx==0 ) return false;
00029  sResult.Set( linesL.Str(idx) );
00030  return true;
00031 }
00032 
00033 bool gXHttpReplyHeader::AddLine (gString& s)
00034 {
00035  char* str;
00036  unsigned nParam, n=N();
00037  gParam aParam( s, ": ", gParam::e_StopSplitOnFirst );
00038  nParam = aParam.N();
00039  if ( nParam<2 ) {
00040      if ( n==0 ) {
00041          gParam firstLine( s, " " );
00042          nParam = firstLine.N();
00043          if ( nParam>=2 ) {
00044              t_uint32 vRes;
00045              str = firstLine.Str(2);
00046              if ( gStrControl::Self().ConvertToUInt32( str, vRes )==0 ) {
00047                  htmlCode = (int)vRes;
00048                  sHtmlCode.SetEmpty();
00049                  for (unsigned iterStr=3; iterStr<=nParam; iterStr++) {
00050                      str = firstLine.Str( iterStr );
00051                      if ( str!=nil && str[0]!=0 ) {
00052                          if ( iterStr>3 ) sHtmlCode.Add( " " );
00053                          sHtmlCode.Add( str );
00054                      }
00055                  }
00056              }
00057              else {
00058                  htmlCode = -1;
00059              }
00060              return thisAddLine( firstLine.Str(1), str );
00061          }
00062      }
00063      kindsL.Add( "\0" );
00064      linesL.Add( "\0" );
00065      Add( s );
00066      return false;
00067  }
00068  return thisAddLine( aParam.Str(1), aParam.Str(2) );
00069 }
00070 
00071 t_uint32 gXHttpReplyHeader::GetLength (bool& hasFound, bool& isLengthOk)
00072 {
00073  int error;
00074  t_uint32 vRes=0;
00075  gString sValue;
00076 
00077  hasFound = false;
00078  isLengthOk = false;
00079 
00080  if ( GetLine( "Transfer-Encoding", sValue ) ) {
00081      isTransferEncodingChunked = sValue.Find( "chunked" );
00082  }
00083 
00084  if ( GetLine( "Content-Length", sValue )==false ) return 0;
00085 
00086  hasFound = true;
00087  error = gStrControl::Self().ConvertToUInt32( sValue.Str(), vRes );
00088  isLengthOk = error==0 && vRes>0 && vRes<MAX_HTTP_CHUNK_SIZE;
00089  if ( isLengthOk ) return vRes;
00090  // RFC2616 refers:
00091  // {If a message is received with both a
00092  //  Transfer-Encoding header field and a Content-Length header field,
00093  //  the latter MUST be ignored.}
00094  // Therefore when 'isTransferEncodingChunked'=True, we must ignore the length.
00095  return 0;
00096 }
00097 
00098 int gXHttpReplyHeader::thisAddLine (char* sKind, char* sContent)
00099 {
00100  gString s1( sKind );
00101  gString s2( sContent );
00102  return thisAddLine( s1, s2 );
00103 }
00104 
00105 int gXHttpReplyHeader::thisAddLine (gString& sKind, gString& sContent)
00106 {
00107  gString aLine;
00108  kindsL.Add( sKind );
00109  linesL.Add( sContent );
00110  aLine = sKind;
00111  aLine.Add( ": " );
00112  aLine.AddString( sContent );
00113  Add( aLine );
00114  return 0;
00115 }
00116 ////////////////////////////////////////////////////////////
00117 gXHttpGeneric::gXHttpGeneric (gTcpConnect& connection)
00118     : gHttpGeneric( connection ),
00119       ioOpErrCode( 0 ),
00120       contentBytesRead( 0 ),
00121       totalBytesRead( 0 ),
00122       keepAliveTimeout( 0 ),  // for chunks, Apache usually sets 15 secs
00123       keepAliveMax( 0 ),  // for chunks, typically 100 secs
00124       fRepErr( nil ),
00125       pBuf( nil ),
00126       sProtocol( 40, '\0' )
00127 {
00128  sProtoVersion.Set( "1.1" );
00129  // Default HTTP version shoud be set to 1.1! See RFC2145 (...HTTP Version Numbers), ch.2.3
00130 }
00131 
00132 gXHttpGeneric::~gXHttpGeneric ()
00133 {
00134  delete pBuf;
00135 }
00136 
00137 gBigBuffer& gXHttpGeneric::GetBuffer ()
00138 {
00139  ASSERTION(pBuf!=nil,"pBuf!=nil");
00140  return *pBuf;
00141 }
00142 
00143 gUCharBuffer& gXHttpGeneric::GetBaseBuffer ()
00144 {
00145  gUCharBuffer* sBuf;
00146  sBuf = GetBuffer().baseBuf;
00147  ASSERTION(sBuf!=nil,"sBuf!=nil");
00148  return *sBuf;
00149 }
00150 
00151 gString& gXHttpGeneric::GetProtocolString ()
00152 {
00153  snprintf( sProtocol.Str(), 38, "HTTP/%s", sProtoVersion.Str() );
00154  return sProtocol;
00155 }
00156 
00157 gVersion& gXHttpGeneric::GetVersion ()
00158 {
00159  httpVersion.SetVersion( sProtoVersion );
00160  return httpVersion;
00161 }
00162 
00163 gTcpConnect& gXHttpGeneric::Connection ()
00164 {
00165  ASSERTION(pConnection!=nil,"Connection()");
00166  return *pConnection;
00167 }
00168 
00169 gTcpConnect* gXHttpGeneric::ConnectionPtr ()
00170 {
00171  ASSERTION(pConnection!=nil,"ConnectionPtr()");  // optional assert
00172  return pConnection;
00173 }
00174 
00175 bool gXHttpGeneric::GetContent (eContentMethod method,
00176                                 gString& sHost,
00177                                 gString& sPath,
00178                                 gString& sReply)
00179 {
00180  getHeaderL.Delete();
00181  return true;
00182 }
00183 
00184 bool gXHttpGeneric::SetProtocolVersion (char* str)
00185 {
00186  if ( str==nil ) return SetProtocolVersion( "1.1" );
00187  sProtoVersion.Set( str );
00188  return sProtoVersion.Length()<40;
00189 }
00190 
00191 int gXHttpGeneric::AddHead (gString& s, bool doCrNl)
00192 {
00193  // By default doCrNl=true, meaning a Carriage-Return and New-Line (13d,10d) will be appended to 's'
00194  gString sSend( s );
00195  if ( doCrNl ) sSend.Add( "\r\n" );
00196  DBGPRINT_MIN("DBG_WEB: AddHead: %s! (%s)\n",s.Str(),doCrNl?"CR&LF":"\0");
00197  if ( sSend.IsEmpty() ) return 0;  // Nothing to write
00198  getHeaderL.Add( sSend );
00199  return 0;
00200 }
00201 
00202 int gXHttpGeneric::AddHeadStr (char* str)
00203 {
00204  ASSERTION(str!=nil,"str!=nil");
00205  gString s( str );
00206  return AddHead( s );
00207 }
00208 
00209 int gXHttpGeneric::WriteHead (bool doFlush)
00210 {
00211  // Write HTTP header request only once: more efficient
00212  gString s;
00213  char* str;
00214  unsigned i, n;
00215  unsigned pos;
00216 
00217  for (i=1, n=getHeaderL.N(); i<=n; i++) {
00218      str = getHeaderL.Str( i );
00219      pos = lReq.FindHeadFullFirst( str );
00220      gString sNewHead( str );
00221      if ( pos>0 ) {
00222          DBGPRINT("DBG: list-pos=%u, of '%s', using: '%s'\n\n",
00223                   pos,
00224                   str,
00225                   lReq.Str(pos));
00226          sNewHead = lReq.Str( pos );
00227          lReq.Delete( pos, pos );
00228      }
00229      if ( sNewHead[ sNewHead.Length() ]!='\n' )
00230          sNewHead.Add( "\r\n" );
00231      s.AddString( sNewHead );
00232  }
00233 
00234  for (i=1, n=lReq.N(); i<=n; i++) {
00235      str = lReq.Str( i );
00236      gString sNewHead( str );
00237      if ( sNewHead[ sNewHead.Length() ]!='\n' )
00238          sNewHead.Add( "\r\n" );
00239      s.AddString( sNewHead );
00240  }
00241 
00242  if ( doFlush ) {
00243      s.Add( "\r\n" );
00244 
00245      for (i=1, n=lReq.NLines(); i<=n; i++) {
00246          str = lReq.lText.Str( i );
00247          ASSERTION(str,"str");
00248          gString sNewHead( str );
00249          if ( sNewHead[ sNewHead.Length() ]!='\n' )
00250              sNewHead.Add( "\r\n" );
00251          s.AddString( sNewHead );
00252      }
00253  }
00254 
00255  DBGPRINT_HIO("\nWRITE >>>>\n%s<<<< EOF\n\n",s.Str());
00256 
00257  return Connection().Write( s );
00258 }
00259 
00260 int gXHttpGeneric::SecSleep (t_uint32 aSec)
00261 {
00262  DBGPRINT("DBG: SecSleep(%u)\n",(unsigned)aSec);
00263  return gFileControl::Self().SecSleep( aSec );
00264 }
00265 
00266 int gXHttpGeneric::MiliSecSleep (t_uint32 aMiliSec)
00267 {
00268  DBGPRINT("DBG: MiliSecSleep(%u)/%d\n",(unsigned)aMiliSec,keepAliveTimeout);
00269  gFileControl::Self().MiliSecSleep( aMiliSec );
00270  return (int)aMiliSec;
00271 }
00272 
00273 int gXHttpGeneric::thisReadHeadReply (gTcpConnect& connection, unsigned& count, gString& sResult)
00274 {
00275  int error;
00276  t_uchar c, lastChr;
00277  gString sLine;
00278 
00279  for (count=0, lastChr=0; ; ) {
00280      error = connection.Read( c )==false;
00281      if ( error!=0 ) return 1; //Broken connection
00282      count++;
00283      if ( c=='\r' ) continue;
00284      if ( c=='\n' ) {
00285          //printf("DBG: REPLY '%s'\n",sLine.Str());
00286          if ( c==lastChr ) return 0;
00287          replyHeader.AddLine( sLine );
00288          sLine.SetEmpty();
00289      }
00290      else {
00291          sLine.Add( c );
00292      }
00293      lastChr = c;
00294      sResult.Add( c );
00295  }
00296  return 0;
00297 }
00298 
00299 int gXHttpGeneric::thisParseServerReply (gString& sReply, gXHttpReplyHeader& serverReply)
00300 {
00301  int count=0;
00302  unsigned pos;
00303  char* str;
00304  t_int32 value;
00305 
00306  // printf("kindsL: "); serverReply.kindsL.Show();
00307  // printf("linesL: "); serverReply.linesL.Show();
00308  // E.g. of a chunked response
00309  //     kindsL: ("HTTP/1.1" "Date" "Server" "X-Powered-By" "Keep-Alive" "Connection" "Transfer-Encoding" "Content-Type")
00310  //     linesL: ("OK" "Sun, 24 Sep 2006 13:45:02 GMT" "Apache/2.0.52 (Fedora)" "PHP/4.3.10" "timeout=15, max=100" "Keep-Alive" "chunked" "text/html; charset=iso-8859-1")
00311 
00312  pos = serverReply.kindsL.Match( "Keep-Alive" );
00313  if ( pos==0 ) return 0;
00314  gParam timers( serverReply.linesL.Str( pos ), "," );
00315  // E.g.: {"timeout=15", "max=100"}
00316  str = timers.Str( 1 );
00317  pos = gStrControl::Self().Find( str, '=' );
00318  if ( pos>0 && gStrControl::Self().ConvertToInt32( str+pos, value )==0 ) {
00319      keepAliveTimeout = value<32000 ? (int)value : 0;
00320      count++;
00321  }
00322  str = timers.Str( 2 );
00323  pos = gStrControl::Self().Find( str, '=' );
00324  if ( pos>0 && gStrControl::Self().ConvertToInt32( str+pos, value )==0 ) {
00325      keepAliveMax = value<32000 ? (int)value : 0;
00326      count++;
00327  }
00328 
00329  DBGPRINT("DBG: keepAlive={%d,%d}\n",keepAliveTimeout,keepAliveMax);
00330  return count;
00331 }
00332 ////////////////////////////////////////////////////////////
00333 gXHttpCont::gXHttpCont (gTcpConnect& connection)
00334     : gXHttpGeneric( connection ),
00335       isContText( false )
00336 {
00337  // I chose this agent: please set 'strUserAgent' empty for no "User-Agent" head writing
00338  strUserAgent.Set( "Lynx/2.8.4rel.1 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.6b" );
00339 }
00340 
00341 gXHttpCont::~gXHttpCont ()
00342 {
00343 }
00344 
00345 bool gXHttpCont::GetContent (eContentMethod method,
00346                              gString& sHost,
00347                              gString& sPath,
00348                              gString& sReply)
00349 {
00350  sHostname = sHost;
00351  if ( sHostname.Find('/')>0 ) {
00352      ioOpErrCode = 5;
00353      return false;
00354  }
00355  gXHttpGeneric::GetContent( method, sHost, sPath, sReply );
00356  SetError( thisGetHtmlContrl( method, sPath, sReply ) );
00357  return lastOpError==0;
00358 }
00359 
00360 char* gXHttpCont::GetHeadUserAgentStr ()
00361 {
00362  if ( strUserAgent.IsEmpty() ) return nil;
00363  sHeadTempStr.Set( "User-Agent: " );
00364  sHeadTempStr.AddString( strUserAgent );
00365  return sHeadTempStr.Str();
00366 }
00367 
00368 char* gXHttpCont::GetHeadRefererStr ()
00369 {
00370  if ( sHostname.IsEmpty() ) return nil;
00371  sHeadTempStr.Set( "Referer: http://" );
00372  sHeadTempStr.AddString( sHostname );
00373  sHeadTempStr.Add( "/" );
00374  return sHeadTempStr.Str();
00375 }
00376 
00377 int gXHttpCont::thisGetHtmlContrl (eContentMethod method,
00378                                    gString& sPath,
00379                                    gString& sReply)
00380 {
00381  int error;
00382  int parsedArgs;
00383  unsigned count;
00384  unsigned iL, nL;
00385  gString sendStr;
00386  eContentImport contImport;
00387  bool isGreaterThan1Dot0;
00388  bool doFlush = true;
00389  char* str;
00390  short vIdx;  // Stat index
00391 
00392  ioOpErrCode = 0;
00393  ASSERTION(pConnection!=nil,"pConnection!=nil");
00394  if ( sPath.IsEmpty() ) return -1;
00395 
00396  gVersion baseVersion( 1, 0 );
00397  isGreaterThan1Dot0 = GetVersion() > baseVersion;
00398 
00399  if ( method==e_Get )
00400      sendStr.Add( "GET " );
00401  else
00402      sendStr.Add( "POST " );
00403  sendStr.AddString( sPath );
00404  sendStr.Add( ' ' );
00405  //sendStr.Add( "HTTP/1.0" )  ==> May not send e.g. 'Host: yahoo.com'
00406  sendStr.AddString( GetProtocolString() );
00407  error = AddHead( sendStr );
00408  if ( error!=0 ) return 1;
00409  ioOpErrCode = 2;
00410  if ( isGreaterThan1Dot0 ) {
00411      // The client sends "Proxy-Connection: Keep-Alive" and "Pragma: No-Cache" headers,
00412      // which are HTTP/1.0 headers that have been replaced by:
00413      // "Connection: Keep-Alive" in HTTP/1.1.
00414      AddHeadStr( "Connection: Keep-Alive" );
00415  }
00416 
00417  gString sHostLine( "Host: " );
00418  sHostLine.AddString( sHostname );
00419  AddHead( sHostLine );
00420  for (iL=1, nL=strHeaderL.N(); iL<=nL; iL++) {
00421      str = strHeaderL.Str( iL );
00422      if ( str==nil || str[0]==0 ) continue;
00423      AddHeadStr( str );
00424  }
00425  str = GetHeadUserAgentStr();
00426  if ( str!=nil ) AddHeadStr( str );
00427  str = GetHeadRefererStr();
00428  if ( str!=nil ) AddHeadStr( str );
00429  if ( WriteHead( doFlush )!=0 ) return 1;
00430 
00431  ioOpErrCode = 3;
00432  if ( doFlush==false ) error = Connection().Write( "\r\n" );
00433  if ( error!=0 ) return 1;
00434  ioOpErrCode = 4;
00435 
00436  for (vIdx=0; vIdx<nStat; vIdx++) mStat[vIdx].Start();
00437 
00438  error = thisReadHeadReply( *pConnection, count, sReply );
00439  parsedArgs = thisParseServerReply( sReply, replyHeader );
00440  ioOpErrCode = 8;
00441  totalBytesRead += (t_uint64)count;
00442 
00443  DBGPRINT_MIN("DBG: first reply: error=%d, bytes-read=%u, count=%u, parsed-args=%d\n",
00444               error,
00445               (unsigned)totalBytesRead,
00446               (unsigned)count,
00447               parsedArgs);
00448 
00449  vIdx = 1;
00450  mStat[vIdx].CpuTics(); vStat[vIdx].SetUInt( mStat[vIdx].GetMilisec() ); vIdx++;
00451  if ( error!=0 ) return 1;
00452 
00453  bool hasFound, isLengthOk;
00454  t_uint16 uSize = replyHeader.GetLength( hasFound, isLengthOk );
00455  isLengthOk = isLengthOk && hasFound;
00456  bool isChunked = replyHeader.IsChunked();
00457 
00458  delete pBuf;
00459  pBuf = nil;
00460 
00461  // Allocate buffer
00462  if ( isLengthOk )
00463      pBuf = new gBigBuffer( (t_uint32)uSize );
00464  else
00465      pBuf = new gBigBuffer;
00466  ASSERTION(pBuf!=nil,"pBuf!=nil");
00467 
00468  isContText = replyHeader.IsText();
00469 
00470  DBGPRINT_MIN("TODO: first reply: isChunked:%c, isLengthOk:%c, isContText:%c\n",
00471               ISyORn(isChunked),
00472               ISyORn(isLengthOk),
00473               ISyORn(isContText));
00474 
00475  if ( isChunked ) {
00476      // Set correct 'isContText'
00477      error = ReadReplyChunked( *pConnection, *pBuf, count );
00478      DBGPRINT_MIN("TODO: ReadReplyChunked(%u), error=%d\n",count,error);
00479  }
00480  else {
00481      // If there is a key 'Content-Type: text/plain', this method returns true
00482      contImport = isContText ? gXHttpGeneric::e_TextPlain : gXHttpGeneric::e_Binary;
00483 
00484      if ( isLengthOk ) {
00485          DBGPRINT_HIO("DBG: uSize=%u, bBuf.size=%u\n",
00486                       uSize,
00487                       pBuf->baseBuf->size);
00488          if ( isContText )
00489              error = ReadReplyText( *pConnection, contImport, *pBuf, count );
00490          else
00491              error = ReadReplyBin( *pConnection, *pBuf, count );
00492      }
00493      else {
00494          error = ReadReplyUnsized( *pConnection, contImport, *pBuf, count );
00495          DBGPRINT_HIO("DBG: ReadReplyUnsized, error=%d\n",error);
00496          if ( error!=0 ) return ioOpErrCode = 32; //Unable to save buffer (for 'Unsized')
00497      }
00498 
00499      ioOpErrCode = 0;
00500      DBGPRINT_HIO("DBG: uSize=%u, count=%u\n",uSize,count);
00501      if ( uSize>0 && (unsigned)uSize!=count+1 ) {
00502          ioOpErrCode = 16;
00503          // Note: no error because this is a non-blocking error
00504      }
00505  }//end IF (not chunked)
00506 
00507  // vIdx is 2
00508  mStat[vIdx].CpuTics(); vStat[vIdx].SetUInt( mStat[vIdx].GetMilisec() ); vIdx++;
00509  // vIdx 3 is used for our own client pace
00510 
00511  contentBytesRead += (t_uint64)count;
00512  totalBytesRead += (t_uint64)count;
00513 
00514  DBGPRINT_MIN("DBG: gXHttpCont::thisGetHtmlContrl returning %d (lastOpError=%d, ioOpErrCode=%d)\n",
00515               error,
00516               lastOpError,
00517               ioOpErrCode);
00518  return error;
00519 }
00520 
00521 int gXHttpCont::ReadReplyText (gTcpConnect& connection,
00522                               eContentImport contImport,
00523                               gBigBuffer& bBuf,
00524                               unsigned& count)
00525 {
00526  static char chBuf[8], ch2Buf[2];
00527  t_uchar c;
00528  t_uchar* uBuf;
00529  bool isTextData;
00530  unsigned usedCount, size;
00531 
00532  uBuf = bBuf.baseBuf->uBuf;
00533  size = bBuf.baseBuf->size;
00534 
00535  if ( connection.IsOk()==false ) return 2;
00536  switch ( contImport ) {
00537  case e_TextPlain:
00538      isTextData = true;
00539      break;
00540  case e_Binary:  //Not for this method!
00541  default:
00542      ASSERTION_FALSE("ReadReply(1)");
00543  }
00544 
00545  for (count=1, usedCount=0; count<size; ) {
00546      DBGPRINT_HIO("DBG: reading-txt count=%u/%u BEF\n",count,size);
00547      if ( connection.Read( c )==false ) break;
00548      count++;
00549      if ( fRepErr!=nil && count%64==0 ) {
00550          sprintf(chBuf,"0x%02X",c);
00551          ch2Buf[0] = c;
00552          fprintf(fRepErr,":%4s %u/%u\n",c<' '||c>126?chBuf:ch2Buf,count,size);
00553      }
00554      if ( c=='\r' ) continue;
00555      uBuf[usedCount++] = c;
00556  }
00557 
00558  bBuf.usedSize = (t_uint64)usedCount;
00559  DBGPRINT_HIO("DBG: reading-txt count=%u/%u, usedCount=%u FINAL\n",count,size,usedCount);
00560  // Pad end with chr 0x0
00561  uBuf[ usedCount ] = 0x0;
00562 
00563  if ( fRepErr!=nil ) fprintf(fRepErr,":Done %u/%u\n",count,size);
00564 
00565  return count>=size ? 0 : -1;
00566 }
00567 
00568 int gXHttpCont::ReadReplyBin (gTcpConnect& connection,
00569                              gBigBuffer& bBuf,
00570                              unsigned& count)
00571 {
00572  t_uchar c;
00573  t_uchar* uBuf;
00574  unsigned size;
00575 
00576  uBuf = bBuf.baseBuf->uBuf;
00577  size = bBuf.baseBuf->size;
00578  ASSERTION(size>0,"size>0");
00579  size--;  // Buffer is one byte wider than needed.
00580 
00581  for (count=0; count<size; ) {
00582      DBGPRINT_HIO("DBG: reading count=%u/%u BEF\n",count,size);
00583      bBuf.usedSize = (t_uint64)count;  // Must be here!
00584      if ( connection.Read( c )==false ) {
00585          DBGPRINT_HIO("DBG: reading count=%u/%u AFT(ERROR)\n",count,size);
00586          return -1;
00587      }
00588      uBuf[ count++ ] = c;
00589      uBuf[ count ] = 0;
00590      DBGPRINT_HIO("DBG: reading count=%u/%u AFT(OK) [%02X]\t%c\n",
00591                   count, size,
00592                   c,
00593                   c<' ' || c>=127 ? '.' : c);
00594  }
00595  DBGPRINT_HIO("DBG: reading count=%u/%u FINAL\n",count,size);
00596  // Pad end with chr 0x0
00597  return 0;
00598 }
00599 
00600 int gXHttpCont::ReadReplyUnsized (gTcpConnect& connection,
00601                                  eContentImport contImport,
00602                                  gBigBuffer& bBuf,
00603                                  unsigned& count)
00604 {
00605  // Returns 0 on success
00606  t_uchar c;
00607  t_uchar* uBuf;
00608  unsigned idxCount=0;
00609  t_uint64 bCount=0;
00610  bool isReadOk;
00611  bool isFlushOk=true;
00612 
00613  bBuf.usedSize = 0;
00614  uBuf = bBuf.baseBuf->uBuf;
00615 
00616  for (count=0; isFlushOk==true; ) {
00617      c = 0; //TODO:cut
00618      isReadOk = connection.Read( c );
00619      //dbg...: if ( c<' ' || c=='[' || c==']' ) printf("[%02X]\t",c); else printf("%c\t",c);
00620      DBGPRINT_HIO("DBG: read-unsized ok?%c, count=%u, usedSize=%lu\n",
00621                   ISyORn( isReadOk ),
00622                   count,
00623                   (long unsigned)bBuf.usedSize);
00624      if ( isReadOk==false ) {
00625          bBuf.usedSize = bCount;
00626          isFlushOk = bBuf.Flush( idxCount );
00627          return isFlushOk ? 0 : -1;
00628      }
00629      count++;
00630      bCount++;
00631      uBuf[idxCount] = c;
00632      idxCount++;
00633 
00634      // Check buffer overflow
00635      if ( idxCount>=bBuf.baseBuf->size ) {
00636          bBuf.usedSize++;
00637          isFlushOk = bBuf.Flush( idxCount );
00638          idxCount = 0;
00639          // Important: buffer re-created, thus uBuf re-assigned
00640          // (and uBuf is for performance reasons)
00641          uBuf = bBuf.baseBuf->uBuf;
00642      }
00643  }
00644  // Only reaches here if buffering failed
00645  return -1;
00646 }
00647 
00648 int gXHttpCont::ReadReplyChunked (gTcpConnect& connection,
00649                                  gBigBuffer& bBuf,
00650                                  unsigned& count)
00651 {
00652  // Returns 0 on success, 1 if chunk-size is invalid, 2 if read failed, -1 on writting error.
00653  // 4 indicates no CR-LF sequence before chunk
00654  unsigned iter=0;
00655  int error;
00656  t_uint32 chunkSize=0;
00657  t_uint32 timeBefore=vStat[1].GetUInt();
00658  t_uint32 timeNew, timeChunk, waited=0;
00659  bool isFlushOk;
00660  bool isHugeBuffer;
00661  gBigBuffer* ptrBuf;
00662 
00663  bBuf.NewFile();
00664 
00665  for (count=0, error=0, isFlushOk=true;
00666       error==0 && isFlushOk==true && (chunkSize = thisReadChunkSize( connection, error ))>0;
00667       count+=chunkSize, iter++) {
00668      ASSERTION(chunkSize<MAX_HTTP_HUGE_CHUNK_SIZE,"ReadReplyChunked(0)");
00669      isHugeBuffer = chunkSize>=MAX_HTTP_CHUNK_SIZE;
00670      DBGPRINT_MIN("DBG: ReadReplyChunked()[%u]=%d chunkSize=%ld%s\n",iter,error,(long)chunkSize,isHugeBuffer?" (HUGE-CHUNK)":"\0");
00671      if ( error!=0 ) break;
00672      ptrBuf = new gBigBuffer( isHugeBuffer ? 10 : chunkSize );
00673      ASSERTION(ptrBuf!=nil,"ReadReplyChunked(1)");
00674      if ( isHugeBuffer ) {
00675          delete[] ptrBuf->baseBuf->uBuf;
00676          ptrBuf->baseBuf->uBuf = new t_uchar[ chunkSize+1 ];
00677          ASSERTION(ptrBuf->baseBuf->uBuf!=nil,"Not enough memory to read big chunks");
00678      }
00679 
00680      // Pace of server respected (acc.RFC2616, ch.8.2.4)
00681 #ifdef gDOS_SPEC
00682      timeBefore /= 1000;  // (CHECK THIS)
00683 #else
00684      timeBefore /= 100;  // 1/10th seconds before...
00685 #endif //gDOS_SPEC
00686 
00687      timeChunk = chunkSize>8192;
00688      timeChunk += 10*(chunkSize>MAX_HTTP_CHUNK_SIZE);
00689 
00690 #ifdef gDOS_SPEC
00691      timeNew = timeBefore + 2*(chunkSize>3000) + (chunkSize>1024)/*+1 would be an ugly try*/;
00692      waited = timeNew * 1000;
00693      SecSleep( timeNew );
00694 #else
00695      timeNew = timeBefore + timeChunk;
00696      waited = timeNew*10+chunkSize/8;
00697      MiliSecSleep( waited );
00698 #endif
00699 
00700      if ( fRepErr )
00701          fprintf(fRepErr,"Pace (ms): %u = (timeBefore=%u [chunk=%ld, timeChunk=%u])\n",
00702                  (unsigned)timeNew,
00703                  (unsigned)timeBefore,
00704                  (long)chunkSize,
00705                  (unsigned)timeChunk);
00706 
00707      // Just increment the vStat[3] (pace)
00708      vStat[3].SetUInt( vStat[3].GetUInt() + waited );  // Miliseconds made (pace) on our own HTTP client
00709      DBGPRINT("DBG: (iter=%u) stat=%u, waited=%lu (total_wait:%u)\n",
00710               iter,
00711               vStat[1].GetUInt(),
00712               (unsigned long)waited,
00713               vStat[3].GetUInt());
00714      // ==>>old?        mStat[3/*Hard-coded:pace*/].SetUInt( timeNew * 1000 );
00715      // Read from socket...
00716      error = connection.Read( ptrBuf->baseBuf->uBuf, chunkSize ) ? 0 : 2;
00717 
00718      DBGPRINT_MIN("DBG: timeNew=%u = (%u + %u [chunk=%ld]),error=%d\n",
00719             (unsigned)timeNew,
00720             (unsigned)timeBefore,
00721             (unsigned)timeChunk,
00722             (long)chunkSize,
00723             error);
00724 
00725      {
00726         int dosCount=0;
00727         for ( ; error!=0 && dosCount<3/* 200 ??*/; dosCount++) {
00728             DBGPRINT("DBG: READ(iter=%u,dosCount=%d):err=%d, size=%u\n",iter,dosCount,error,(unsigned)chunkSize);
00729             error = connection.Read( ptrBuf->baseBuf->uBuf, chunkSize ) ? 0 : 2;
00730             vStat[3].SetUInt( vStat[3].GetUInt() + 1 );  // Just loose some time, and increment statistical pace
00731         }
00732 #ifdef DEBUG
00733         if ( dosCount>0 ) printf("DBG: READ(iter=%u,dosCount=%d):err=%d (FINAL)\n",iter,dosCount,error);
00734 #endif //DEBUG
00735      }
00736 
00737      if ( error==0 ) {
00738          // ...and write to file
00739          isFlushOk = bBuf.WriteBuf( bBuf.GetFile().fHandle, *(ptrBuf->baseBuf), chunkSize );
00740          // Read CR-LF
00741          memset( ptrBuf->baseBuf->uBuf, 0x0, 10 );
00742          error = connection.Read( ptrBuf->baseBuf->uBuf, 2 )==false;
00743          if ( error==0 ) error = ptrBuf->baseBuf->uBuf[1]=='\n' ? 0 : 4;
00744      }
00745      delete ptrBuf;
00746      ptrBuf = nil;
00747  }//end FOR
00748 
00749  if ( isFlushOk==false ) return -1;
00750  if ( error==-1 ) return 1;
00751  if ( error>=2 ) return error;
00752  return count==0;
00753 }
00754 
00755 t_uint32 gXHttpCont::thisReadChunkSize (gTcpConnect& connection, int& error)
00756 {
00757  gString sChunk;
00758  t_uint32 numHex;
00759 
00760  error = 1;
00761  if ( connection.ReadLine( sChunk )==false ) return 0;
00762  error = gStrControl::Self().ConvertHexToUInt32( sChunk.Str(), e_DigConvAny, numHex );
00763  // error is -1 when the conversion fails
00764  if ( error!=0 ) return 0;
00765  return numHex;
00766 }
00767 ////////////////////////////////////////////////////////////
00768 

Generated on Sat Aug 18 02:40:51 2007 for xpfweb_v2x lib by  doxygen 1.4.2