HTTPFileSource.cpp

Go to the documentation of this file.
00001 //===========================================================================
00002 //Copyright (C) 2003, 2004 Zentaro Kavanagh
00003 //
00004 //Redistribution and use in source and binary forms, with or without
00005 //modification, are permitted provided that the following conditions
00006 //are met:
00007 //
00008 //- Redistributions of source code must retain the above copyright
00009 //  notice, this list of conditions and the following disclaimer.
00010 //
00011 //- Redistributions in binary form must reproduce the above copyright
00012 //  notice, this list of conditions and the following disclaimer in the
00013 //  documentation and/or other materials provided with the distribution.
00014 //
00015 //- Neither the name of Zentaro Kavanagh nor the names of contributors 
00016 //  may be used to endorse or promote products derived from this software 
00017 //  without specific prior written permission.
00018 //
00019 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020 //``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021 //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
00022 //PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
00023 //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00024 //EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00025 //PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00026 //PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00027 //LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00028 //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029 //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 //===========================================================================
00031 #include "stdafx.h"
00032 #include "httpfilesource.h"
00033 
00034 //#define OGGCODECS_LOGGING
00035 HTTPFileSource::HTTPFileSource(void)
00036         :       mBufferLock(NULL)
00037         ,       mIsChunked(false)
00038         ,       mIsFirstChunk(true)
00039         ,       mChunkRemains(0)
00040         ,       mNumLeftovers(0)
00041 {
00042         mBufferLock = new CCritSec;
00043 #ifdef OGGCODECS_LOGGING
00044         debugLog.open("d:\\zen\\logs\\htttp.log", ios_base::out | ios_base::app);
00045         fileDump.open("d:\\zen\\logs\\filedump.ogg", ios_base::out|ios_base::binary);
00046         rawDump.open("D:\\zen\\logs\\rawdump.out", ios_base::out|ios_base::binary);
00047 #endif
00048         mInterBuff = new unsigned char[RECV_BUFF_SIZE* 2];
00049 
00050 }
00051 
00052 HTTPFileSource::~HTTPFileSource(void)
00053 {
00054         //debugLog<<"About to close socket"<<endl;
00055         close();
00056         //debugLog<<"Winsock ended"<<endl;
00057 #ifdef OGGCODECS_LOGGING
00058         debugLog.close();
00059         fileDump.close();
00060         rawDump.close();
00061 #endif
00062         delete mBufferLock;
00063         delete[] mInterBuff;
00064 }
00065 
00066 void HTTPFileSource::unChunk(unsigned char* inBuff, unsigned long inNumBytes) 
00067 {
00068 
00069         //This method is a bit rough and ready !!
00070         ASSERT(inNumBytes > 2);
00071         rawDump.write((char*)inBuff, inNumBytes);
00072         //debugLog<<"UnChunk"<<endl;
00073         unsigned long locNumBytesLeft = inNumBytes;
00074 
00075         memcpy((void*)(mInterBuff + mNumLeftovers), (const void*)inBuff, inNumBytes);
00076         locNumBytesLeft +=  mNumLeftovers;
00077         mNumLeftovers = 0;
00078         unsigned char* locWorkingBuffPtr = mInterBuff;
00079 
00080         //debugLog<<"inNumBytes = "<<inNumBytes<<endl;
00081 
00082         while (locNumBytesLeft > 8) {
00083                 //debugLog<<"---"<<endl;
00084                 //debugLog<<"Bytes left = "<<locNumBytesLeft<<endl;
00085                 //debugLog<<"ChunkRemaining = "<<mChunkRemains<<endl;
00086 
00087                 if (mChunkRemains == 0) {
00088                         //debugLog<<"Zero bytes of chunk remains"<<endl;
00089 
00090                         //Assign to a string for easy manipulation of the hex size
00091                         string locTemp;
00092                 
00093                         if (mIsFirstChunk) {
00094                                 //debugLog<<"It's the first chunk"<<endl;
00095                                 mIsFirstChunk = false;
00096                                 locTemp = (char*)locWorkingBuffPtr;
00097                         } else {
00098                                 //debugLog<<"Not the first chunk"<<endl;
00099                                 //debugLog<<"Skip bytes = "<<(int)locWorkingBuffPtr[0]<<(int)locWorkingBuffPtr[1]<<endl;
00100                                 locTemp = (char*)(locWorkingBuffPtr + 2);
00101                                 locWorkingBuffPtr+=2;
00102                                 locNumBytesLeft -= 2;
00103                         }
00104 
00105         /*              if (mLeftOver != "") {
00106                                 debugLog<<"Sticking the leftovers back together..."<<endl;
00107                                 locTemp = mLeftOver + locTemp;
00108                                 mLeftOver = "";
00109                         }*/
00110 
00111                         size_t locChunkSizePos = locTemp.find("\r\n");
00112                         
00113                         
00114                         if (locChunkSizePos != string::npos) {
00115                                 //debugLog<<"Found the size bytes "<<endl;
00116                                 //Get a string representation of the hex string that tells us the size of the chunk
00117                                 string locChunkSizeStr = locTemp.substr(0, locChunkSizePos);
00118                                 //debugLog<<"Sizingbuytes " << locChunkSizeStr<<endl;
00119                                 char* locDummyPtr = NULL;
00120 
00121                                 //Convert it to a number
00122                                 mChunkRemains = strtol(locChunkSizeStr.c_str(), &locDummyPtr, 16);
00123 
00124                                 //debugLog<<"Chunk reamining "<<mChunkRemains<<endl;
00125                                 //The size of the crlf 's and the chunk size value
00126                                 unsigned long locGuffSize = (unsigned long)(locChunkSizeStr.size() + 2);
00127                                 locWorkingBuffPtr +=  locGuffSize;
00128                                 locNumBytesLeft -= locGuffSize;
00129                         } else {
00130                                 //debugLog<<"************************************** "<<endl;
00131                         
00132 
00133                         }
00134                 }
00135 
00136                 //This is the end of file
00137                 if (mChunkRemains == 0) {
00138                         //debugLog<<"EOF"<<endl;
00139                         return;
00140                 }
00141 
00142                 //If theres less bytes than the remainder of the chunk
00143                 if (locNumBytesLeft < mChunkRemains) {
00144                         //debugLog<<"less bytes remain than the chunk needs"<<endl;
00145                         
00146                         mFileCache.write((const unsigned char*)locWorkingBuffPtr, locNumBytesLeft );
00147                         fileDump.write((char*)locWorkingBuffPtr, locNumBytesLeft);
00148                         locWorkingBuffPtr += locNumBytesLeft;
00149                         mChunkRemains -= locNumBytesLeft;
00150                         locNumBytesLeft = 0;
00151                 } else {
00152                         //debugLog<<"more bytes remain than the chunk needs"<<endl;
00153                         mFileCache.write((const unsigned char*)locWorkingBuffPtr, mChunkRemains );
00154                         fileDump.write((char*)locWorkingBuffPtr, mChunkRemains);
00155                         locWorkingBuffPtr += mChunkRemains;
00156                         locNumBytesLeft -= mChunkRemains;
00157                         mChunkRemains = 0;
00158                 }
00159 
00160         }
00161 
00162         if (locNumBytesLeft != 0) {
00163                 //debugLog<<"There is a non- zero amount of bytes leftover... buffer them up for next time..."<<endl;
00164                 memcpy((void*)mInterBuff, (const void*)locWorkingBuffPtr, locNumBytesLeft);
00165                 mNumLeftovers = locNumBytesLeft;
00166         }
00167 }
00168 void HTTPFileSource::DataProcessLoop() {
00169         //debugLog<<"DataProcessLoop: "<<endl;
00170         int locNumRead = 0;
00171         char* locBuff = NULL;
00172         DWORD locCommand = 0;
00173         bool locSeenAny = false;
00174         debugLog<<"Starting dataprocessloop"<<endl;
00175 
00176         locBuff = new char[RECV_BUFF_SIZE];
00177 
00178         while(true) {
00179                 if(CheckRequest(&locCommand) == TRUE) {
00180                         debugLog<<"Thread Data Process loop received breakout signal..."<<endl;
00181                         delete[] locBuff;
00182                         return;
00183                 }
00184                 //debugLog<<"About to call recv"<<endl;
00185                 locNumRead = recv(mSocket, locBuff, RECV_BUFF_SIZE, 0);
00186                 //debugLog<<"recv complete"<<endl;
00187                 if (locNumRead == SOCKET_ERROR) {
00188                         int locErr = WSAGetLastError();
00189                         debugLog<<"Socket error receiving - Err No = "<<locErr<<endl;
00190                         mWasError = true;
00191                         break;
00192                 }
00193 
00194                 if (locNumRead == 0) {
00195                         debugLog<<"Read last bytes..."<<endl;
00196                         mIsEOF = true;
00197                         delete[] locBuff;
00198                         return;
00199                 }
00200 
00201                 {//CRITICAL SECTION - PROTECTING BUFFER STATE
00202                         CAutoLock locLock(mBufferLock);
00203                         //debugLog <<"Num Read = "<<locNumRead<<endl;
00204                         if (mSeenResponse) {
00205                                 //Add to buffer
00206 
00207                                 if (mIsChunked) {
00208                                         unChunk((unsigned char*)locBuff, locNumRead);
00209                                 } else {
00210                                         mFileCache.write((const unsigned char*)locBuff, locNumRead);
00211                                 }
00212                         
00213                                 //Dump to file
00214                                 //fileDump.write(locBuff, locNumRead);
00215                         } else {
00216                                 //if (!locSeenAny) {
00217                                 //      locSeenAny = true;
00218                                 //      //Start of response
00219                                 //      if (locBuff[0] != '2') {
00220                                 //              mWasError = true;
00221                                 //              delete[] locBuff;
00222                                 //              return;
00223                                 //      }
00224                                 //}
00225                                 string locTemp = locBuff;
00226                                 //debugLog<<"Binary follows... "<<endl<<locTemp<<endl;
00227                                 size_t locPos = locTemp.find("\r\n\r\n");
00228                                 if (locPos != string::npos) {
00229                                         //Found the break
00230                                         //debugLog<<"locPos = "<<locPos<<endl;
00231                                         mSeenResponse = true;
00232                                         mLastResponse = locTemp.substr(0, locPos);
00233                                         debugLog<<"HTTP Response:"<<endl;
00234                                         debugLog<<mLastResponse<<endl;
00235 
00236                                         unsigned short locResponseCode = getHTTPResponseCode(mLastResponse);
00237 
00238                                         mRetryAt = "";
00239                                         if (locResponseCode == 301) {
00240                                                 size_t locLocPos = mLastResponse.find("Location: ");
00241                                                 if (locLocPos != string::npos) {
00242                                                         locLocPos += 10;
00243                                                         size_t locEndPos = mLastResponse.find("\r", locLocPos);
00244                                                         if (locEndPos != string::npos) {
00245                                                                 if (locEndPos > locLocPos) {
00246                                                                         mRetryAt = mLastResponse.substr(locLocPos, locEndPos - locLocPos);
00247                                                                         debugLog<<"Retry URL = "<<mRetryAt<<endl;
00248                                                                 }
00249                                                         }
00250                                                 }
00251 
00252                                                 debugLog<<"Setting error to true"<<endl;
00253                                                 mIsEOF = true;
00254                                                 mWasError = true;
00255                                                 //close();
00256                                         } else if (locResponseCode >= 300) {
00257                                                 debugLog<<"Setting error to true"<<endl;
00258                                                 mIsEOF = true;
00259                                                 mWasError = true;
00260                                                 //close();
00261                                         }
00262 
00263                                         if (locTemp.find("Transfer-Encoding: chunked") != string::npos) {
00264                                                 mIsChunked = true;
00265                                         }
00266 
00267                                         char* locBuff2 = locBuff + locPos + 4;  //View only - don't delete.
00268                                         locTemp = locBuff2;
00269 
00270                                         if (mIsChunked) {
00271                                                 if (locNumRead - locPos - 4 > 0) {
00272                                                         unChunk((unsigned char*)locBuff2, locNumRead - locPos - 4);
00273                                                 }
00274                                         } else {
00275                         //debugLog<<"Start of data follows"<<endl<<locTemp<<endl;
00276                                                 if (locNumRead - locPos - 4 > 0) {
00277                                                         mFileCache.write((const unsigned char*)locBuff2, (locNumRead - (locPos + 4)));
00278                                                 }
00279                                         }
00280                                 }
00281                         }
00282                 } //END CRITICAL SECTION
00283         }
00284 
00285         delete[] locBuff;
00286 }
00287 
00288 unsigned short HTTPFileSource::getHTTPResponseCode(string inHTTPResponse)
00289 {
00290         size_t locPos = inHTTPResponse.find(" ");
00291         if (locPos != string::npos) {
00292                 string locCodeString = inHTTPResponse.substr(locPos + 1, 3);
00293                 try {
00294                         unsigned short locCode = (unsigned short)StringHelper::stringToNum(locCodeString);
00295                         return locCode;
00296                 } catch(...) {
00297                         return 0;
00298                 }
00299         } else {
00300                 return 0;
00301         }
00302 }
00303 string HTTPFileSource::shouldRetryAt()
00304 {
00305         return mRetryAt;
00306 }
00307 
00308 DWORD HTTPFileSource::ThreadProc(void) {
00309         //debugLog<<"ThreadProc:"<<endl;
00310         while(true) {
00311                 DWORD locThreadCommand = GetRequest();
00312                 
00313                 switch(locThreadCommand) {
00314                         case THREAD_EXIT:
00315                                 
00316                                 Reply(S_OK);
00317                                 return S_OK;
00318 
00319                         case THREAD_RUN:
00320                                 
00321                                 Reply(S_OK);
00322                                 DataProcessLoop();
00323                                 break;
00324                                 //return S_OK;
00325                 }
00326 
00327 
00328         }
00329         return S_OK;
00330 }
00331 unsigned long HTTPFileSource::seek(unsigned long inPos) {
00332         //Close the socket down
00333         //Open up a new one to the same place.
00334         //Make the partial content request.
00335         //debugLog<<"Seeking to "<<inPos<<endl;
00336         if (mFileCache.readSeek(inPos)) {
00337                 return inPos;
00338         } else {
00339                 return (unsigned long) -1;
00340         }
00341         
00342 }
00343 
00344 
00345 void HTTPFileSource::close() {
00346         //Killing thread
00347         //debugLog<<"HTTPFileSource::close()"<<endl;
00348         if (ThreadExists() == TRUE) {
00349                 //debugLog<<"Calling Thread to EXIT"<<endl;
00350                 CallWorker(THREAD_EXIT);
00351                 //debugLog<<"Killing thread..."<<endl;
00352                 Close();
00353                 //debugLog<<"After Close called on CAMThread"<<endl;
00354         }
00355 
00356 
00357         
00358         //debugLog<<"Closing socket..."<<endl;
00359         //Close the socket down.
00360         closeSocket();
00361 }
00362 
00363 bool HTTPFileSource::startThread() {
00364         if (ThreadExists() == FALSE) {
00365                 Create();
00366         }
00367         CallWorker(THREAD_RUN);
00368         return true;
00369 }
00370 bool HTTPFileSource::open(string inSourceLocation, unsigned long inStartByte) {
00371         //Open network connection and start feeding data into a buffer
00372         //
00373         mSeenResponse = false;
00374         mLastResponse = "";
00375         //debugLog<<"Open: "<<inSourceLocation<<endl;
00376 
00377         { //CRITICAL SECTION - PROTECTING STREAM BUFFER
00378                 CAutoLock locLock(mBufferLock);
00379                 
00380                 //Init rand number generator
00381                 LARGE_INTEGER locTicks;
00382                 QueryPerformanceCounter(&locTicks);
00383                 srand((unsigned int)locTicks.LowPart);
00384 
00385                 int locRand = rand();
00386 
00387                 string locCacheFileName = getenv("TEMP");
00388                 //debugLog<<"Temp = "<<locCacheFileName<<endl;
00389                 locCacheFileName += "\\filecache";
00390                 
00391                 locCacheFileName += StringHelper::numToString(locRand);
00392                 locCacheFileName += ".ogg";
00393                 //debugLog<<"Cache file  = "<<locCacheFileName<<endl;
00394                 if(mFileCache.open(locCacheFileName)) {
00395                         //debugLog<<"OPEN : Cach file opened"<<endl;
00396                 }
00397         } //END CRITICAL SECTION
00398 
00399         bool locIsOK = setupSocket(inSourceLocation);
00400 
00401         if (!locIsOK) {
00402                 //debugLog<<"Setup socket FAILED"<<endl;
00403                 closeSocket();
00404                 return false;
00405         }
00406 
00407         //debugLog<<"Sending request..."<<endl;
00408 
00409         //How is filename already set ??
00410         httpRequest(assembleRequest(mFileName));
00411         //debugLog<<"Socket ok... starting thread"<<endl;
00412         locIsOK = startThread();
00413 
00414 
00415         return locIsOK;
00416 }
00417 void HTTPFileSource::clear() {
00418         //Reset flags.
00419         debugLog<<"Setting error to false";
00420         mIsEOF = false;
00421         mWasError = false;
00422         mRetryAt = "";
00423 }
00424 bool HTTPFileSource::isError()
00425 {
00426         return mWasError;
00427 }
00428 bool HTTPFileSource::isEOF() {
00429         { //CRITICAL SECTION - PROTECTING STREAM BUFFER
00430                 CAutoLock locLock(mBufferLock);
00431                 unsigned long locSizeBuffed = mFileCache.bytesAvail();;
00432         
00433                 //debugLog<<"isEOF : Amount Buffered avail = "<<locSizeBuffed<<endl;
00434                 if ((locSizeBuffed == 0) && mIsEOF) {
00435                         //debugLog<<"isEOF : It is EOF"<<endl;
00436                         return true;
00437                 } else {
00438                         //debugLog<<"isEOF : It's not EOF"<<endl;
00439                         return false;
00440                 }
00441         } //END CRITICAL SECTION
00442 
00443 }
00444 unsigned long HTTPFileSource::read(char* outBuffer, unsigned long inNumBytes) {
00445         //Reads from the buffer, will return 0 if nothing in buffer.
00446         // If it returns 0 check the isEOF flag to see if it was the end of file or the network is just slow.
00447 
00448         { //CRITICAL SECTION - PROTECTING STREAM BUFFER
00449                 CAutoLock locLock(mBufferLock);
00450                 
00451                 //debugLog<<"Read:"<<endl;
00452                 if((mFileCache.bytesAvail() == 0) || mWasError) {
00453                         //debugLog<<"read : Can't read is error or eof"<<endl;
00454                         return 0;
00455                 } else {
00456                         //debugLog<<"Reading from buffer"<<endl;
00457                         
00458                         unsigned long locNumRead = mFileCache.read((unsigned char*)outBuffer, inNumBytes);
00459 
00460                         if (locNumRead > 0) {
00461                                 debugLog<<locNumRead<<" bytes read from buffer"<<endl;
00462                         }
00463                         return locNumRead;
00464                 }
00465         } //END CRITICAL SECTION
00466 }

Generated on Thu Feb 16 23:48:12 2006 for oggdsf by  doxygen 1.3.9